tahoma2d/toonz/sources/tnztools/perspectivetool.cpp

2229 lines
75 KiB
C++
Raw Normal View History

2021-11-04 03:05:17 +13:00
#include "perspectivetool.h"
#include "tgl.h"
#include "tenv.h"
#include "tsystem.h"
#include "tools/toolhandle.h"
#include "tools/toolutils.h"
#include "toonz/stage.h"
#include "toonz/toonzfolders.h"
#include "toonz/tproject.h"
2021-11-04 03:05:17 +13:00
#include "toonzqt/selectioncommandids.h"
#include "../toonz/sceneviewer.h"
#include "../toonz/tscenehandle.h"
#include "../toonz/toonzscene.h"
#include "../toonz/tcamera.h"
#include <QApplication>
2021-11-10 01:03:51 +13:00
#include <QDir>
#include <QKeyEvent>
2021-11-04 03:05:17 +13:00
#include <QDebug>
TEnv::IntVar PerspectiveToolAdvancedControls("PerspectiveToolAdvancedControls",
0);
2021-11-04 03:05:17 +13:00
//----------------------------------------------------------------------------------------------------------
void PerspectivePreset::saveData(TOStream &os) {
2021-11-10 01:03:51 +13:00
os.openChild("version");
os << 1 << 0;
os.closeChild();
2021-11-04 03:05:17 +13:00
os.openChild("Name");
os << m_presetName;
os.closeChild();
std::vector<PerspectiveObject *>::iterator it;
for (it = m_perspectiveSet.begin(); it != m_perspectiveSet.end(); it++) {
PerspectiveObject *perspective = *it;
os.openChild("PerspectiveObject");
os.openChild("Type");
os << perspective->getType();
os.closeChild();
os.openChild("CenterPoint");
os << perspective->getCenterPoint().x << perspective->getCenterPoint().y;
os.closeChild();
os.openChild("RotationPos");
os << perspective->getRotationPos().x << perspective->getRotationPos().y;
os.closeChild();
os.openChild("SpacingPos");
os << perspective->getSpacingPos().x << perspective->getSpacingPos().y;
os.closeChild();
os.openChild("LeftPivotPos");
os << perspective->getLeftPivotPos().x << perspective->getLeftPivotPos().y;
os.closeChild();
os.openChild("LeftHandlePos");
os << perspective->getLeftHandlePos().x
<< perspective->getLeftHandlePos().y;
os.closeChild();
os.openChild("RightPivotPos");
os << perspective->getRightPivotPos().x
<< perspective->getRightPivotPos().y;
os.closeChild();
os.openChild("RightHandlePos");
os << perspective->getRightHandlePos().x
<< perspective->getRightHandlePos().y;
os.closeChild();
os.openChild("Rotation");
os << perspective->getRotation();
os.closeChild();
os.openChild("Spacing");
os << perspective->getSpacing();
os.closeChild();
os.openChild("Horizon");
os << (int)perspective->isHorizon();
os.closeChild();
os.openChild("Opacity");
os << perspective->getOpacity();
os.closeChild();
os.openChild("Color");
os << perspective->getColor();
os.closeChild();
os.openChild("Parallel");
os << (int)perspective->isParallel();
os.closeChild();
os.closeChild();
}
}
//----------------------------------------------------------------------------------------------------------
void PerspectivePreset::loadData(TIStream &is) {
std::string tagName;
2021-11-10 01:03:51 +13:00
VersionNumber version;
2021-11-04 03:05:17 +13:00
while (is.matchTag(tagName)) {
2021-11-10 01:03:51 +13:00
if (tagName == "version") {
is >> version.first >> version.second;
is.setVersion(version);
is.matchEndTag();
} else if (tagName == "Name")
2021-11-04 03:05:17 +13:00
is >> m_presetName, is.matchEndTag();
else if (tagName == "PerspectiveObject") {
PerspectiveObject *newObject;
while (is.matchTag(tagName)) {
if (tagName == "Type") {
int objType;
is >> objType, is.matchEndTag();
switch (objType) {
case 1:
newObject = new VanishingPointPerspective();
break;
case 2:
newObject = new LinePerspective();
break;
default:
break;
}
} else if (tagName == "CenterPoint") {
double x, y;
is >> x >> y, is.matchEndTag();
;
newObject->setCenterPoint(TPointD(x, y));
} else if (tagName == "RotationPos") {
double x, y;
is >> x >> y, is.matchEndTag();
;
newObject->setRotationPos(TPointD(x, y));
} else if (tagName == "SpacingPos") {
double x, y;
is >> x >> y, is.matchEndTag();
;
newObject->setSpacingPos(TPointD(x, y));
} else if (tagName == "LeftPivotPos") {
double x, y;
is >> x >> y, is.matchEndTag();
;
newObject->setLeftPivotPos(TPointD(x, y));
} else if (tagName == "LeftHandlePos") {
double x, y;
is >> x >> y, is.matchEndTag();
;
newObject->setLeftHandlePos(TPointD(x, y));
} else if (tagName == "RightPivotPos") {
double x, y;
is >> x >> y, is.matchEndTag();
;
newObject->setRightPivotPos(TPointD(x, y));
} else if (tagName == "RightHandlePos") {
double x, y;
is >> x >> y, is.matchEndTag();
;
newObject->setRightHandlePos(TPointD(x, y));
} else if (tagName == "Rotation") {
double rotation;
is >> rotation, is.matchEndTag();
;
newObject->setRotation(rotation);
} else if (tagName == "Spacing") {
double spacing;
is >> spacing, is.matchEndTag();
;
newObject->setSpacing(spacing);
} else if (tagName == "Horizon") {
int isHorizon;
is >> isHorizon, is.matchEndTag();
;
newObject->setHorizon(isHorizon);
} else if (tagName == "Opacity") {
double opacity;
is >> opacity, is.matchEndTag();
;
newObject->setOpacity(opacity);
} else if (tagName == "Color") {
TPixel32 color;
is >> color, is.matchEndTag();
;
newObject->setColor(color);
} else if (tagName == "Parallel") {
int isParallel;
is >> isParallel, is.matchEndTag();
;
newObject->setParallel(isParallel);
} else
is.skipCurrentTag();
}
m_perspectiveSet.push_back(newObject);
is.matchEndTag();
} else
is.skipCurrentTag();
}
}
PERSIST_IDENTIFIER(PerspectivePreset, "PerspectivePreset");
//----------------------------------------------------------------------------------------------------------
PerspectiveObjectUndo::PerspectiveObjectUndo(
std::vector<PerspectiveObject *> objs, PerspectiveTool *tool)
: m_tool(tool) {
if (!m_tool) return;
m_undoData = m_tool->copyPerspectiveSet(objs, false);
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------------------
void PerspectiveObjectUndo::setRedoData(std::vector<PerspectiveObject *> objs) {
if (!m_tool) return;
m_redoData = m_tool->copyPerspectiveSet(objs, false);
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------------------
void PerspectiveObjectUndo::undo() const {
if (!m_tool) return;
m_tool->setPerspectiveObjects(m_undoData);
m_tool->invalidate();
}
//----------------------------------------------------------------------------------------------------------
void PerspectiveObjectUndo::redo() const {
if (!m_tool) return;
m_tool->setPerspectiveObjects(m_redoData);
m_tool->invalidate();
}
//----------------------------------------------------------------------------------------------------------
void PerspectivePresetManager::addPreset(PerspectivePreset perspectiveSet) {
removePreset(perspectiveSet.m_presetName);
m_presets.insert(perspectiveSet);
2021-11-10 01:03:51 +13:00
savePreset(perspectiveSet.m_presetName);
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------------------
void PerspectivePresetManager::removePreset(const std::wstring &name) {
std::set<PerspectivePreset>::iterator it;
for (it = m_presets.begin(); it != m_presets.end(); it++) {
PerspectivePreset preset = *it;
if (preset.m_presetName != name) continue;
deletePreset(preset.m_path);
m_presets.erase(preset);
break;
2021-11-04 03:05:17 +13:00
}
}
//----------------------------------------------------------------------------------------------------------
2021-11-10 01:03:51 +13:00
void PerspectivePresetManager::loadPresets(const TFilePath &presetFolder) {
TFileStatus fs(presetFolder);
2021-11-04 03:05:17 +13:00
2021-11-10 01:03:51 +13:00
if (!fs.doesExist() || !fs.isDirectory()) return;
2021-11-29 03:33:00 +13:00
bool loadCustom = (presetFolder == ToonzFolder::getMyModuleDir());
2021-11-10 01:03:51 +13:00
TFilePathSet fileSet;
fileSet.clear();
QDir presetfp(presetFolder.getQString());
2021-11-10 01:03:51 +13:00
presetfp.setNameFilters(QStringList("*.grid"));
TSystem::readDirectory(fileSet, presetfp, false);
if (fileSet.size() == 0) return;
TFilePathSet::iterator it;
for (it = fileSet.begin(); it != fileSet.end(); it++) {
std::string tagName;
PerspectivePreset data;
TIStream is(*it);
try {
is >> data;
2021-11-29 03:33:00 +13:00
if (loadCustom)
m_customPreset = data;
else {
data.m_path = TFilePath(it->getQString());
data.m_presetName = data.m_path.getWideName();
m_presets.insert(data);
}
2021-11-10 01:03:51 +13:00
} catch (...) {
2021-11-04 03:05:17 +13:00
}
}
}
//----------------------------------------------------------------------------------------------------------
2021-11-10 01:03:51 +13:00
void PerspectivePresetManager::savePreset(std::wstring presetName) {
2021-11-29 03:33:00 +13:00
if (presetName == CUSTOM_WSTR) {
TFilePath fp =
ToonzFolder::getMyModuleDir() + TFilePath("perspective.grid");
if (TFileStatus(fp).doesExist()) TSystem::deleteFile(fp);
TSystem::touchParentDir(fp);
TOStream os(fp);
if (!m_customPreset.m_perspectiveSet.size()) return;
os << m_customPreset;
return;
}
2021-11-04 03:05:17 +13:00
std::set<PerspectivePreset>::iterator it, end = m_presets.end();
for (it = m_presets.begin(); it != end; ++it) {
2021-11-10 01:03:51 +13:00
if (it->m_presetName != presetName) continue;
TFilePath fp = it->m_path;
if (fp == TFilePath()) return;
TSystem::touchParentDir(fp);
TOStream os(fp);
2021-11-04 03:05:17 +13:00
os << (TPersist &)*it;
break;
2021-11-04 03:05:17 +13:00
}
2021-11-10 01:03:51 +13:00
}
2021-11-04 03:05:17 +13:00
2021-11-10 01:03:51 +13:00
//----------------------------------------------------------------------------------------------------------
void PerspectivePresetManager::deletePreset(TFilePath presetPath) {
if (presetPath == TFilePath()) return;
2021-11-10 01:03:51 +13:00
TFileStatus fs(presetPath);
2021-11-10 01:03:51 +13:00
if (!fs.doesExist()) return;
TSystem::deleteFile(presetPath);
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------------------
void PerspectiveControls::drawControls() {
if (!m_perspective) return;
TPointD centerPoint = m_perspective->getCenterPoint();
2021-11-10 08:27:26 +13:00
m_unit = sqrt(tglGetPixelSize2()) * getDevPixRatio();
2021-11-10 08:27:26 +13:00
double circleRadius = m_handleRadius * m_unit;
double diskRadius = (m_handleRadius - 2) * m_unit;
glLineWidth(1.5);
glLineStipple(1, 0xFFFF);
glEnable(GL_LINE_STIPPLE);
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Draw Rotation control
glColor3d(0.70, 0.70, 0.70);
2021-11-04 03:05:17 +13:00
tglDrawSegment(centerPoint, m_rotationPos);
tglDrawCircle(m_rotationPos, circleRadius);
2021-11-04 03:05:17 +13:00
if (m_active) glColor3d(1.0, 1.0, 0.0);
tglDrawDisk(m_rotationPos, diskRadius);
glColor3d(0.50, 0.50, 0.50);
2021-11-10 08:27:26 +13:00
tglDrawCircle(m_rotationPos, (m_handleRadius - 6) * m_unit);
2021-11-04 03:05:17 +13:00
// Draw Spacing control
2021-11-04 03:05:17 +13:00
if (m_perspective->getType() != PerspectiveType::Line ||
m_perspective->isParallel()) {
glColor3d(0.70, 0.70, 0.70);
2021-11-04 03:05:17 +13:00
tglDrawSegment(centerPoint, m_spacingPos);
tglDrawCircle(m_spacingPos, circleRadius);
2021-11-04 03:05:17 +13:00
if (m_active) glColor3d(1.0, 1.0, 0.0);
tglDrawDisk(m_spacingPos, diskRadius);
glColor3d(0.50, 0.50, 0.50);
2021-11-10 08:27:26 +13:00
tglDrawSegment(m_spacingPos + m_unit * TPointD(-4, 4),
m_spacingPos + m_unit * TPointD(-4, -4));
tglDrawSegment(m_spacingPos + m_unit * TPointD(0, 4),
m_spacingPos + m_unit * TPointD(0, -4));
tglDrawSegment(m_spacingPos + m_unit * TPointD(4, 4),
m_spacingPos + m_unit * TPointD(4, -4));
2021-11-04 03:05:17 +13:00
}
if (m_perspective->getType() == PerspectiveType::VanishingPoint &&
m_showAdvanced) {
// Draw Left Handle
glColor3d(0.70, 0.70, 0.70);
2021-11-04 03:05:17 +13:00
tglDrawSegment(m_leftPivotPos, m_leftHandlePos);
tglDrawCircle(m_leftHandlePos, circleRadius);
2021-11-04 03:05:17 +13:00
if (m_active) glColor3d(1.0, 1.0, 0.0);
tglDrawDisk(m_leftHandlePos, diskRadius);
TPointD p1, p2, p3;
double dx = m_leftPivotPos.x - m_leftHandlePos.x;
double dy = m_leftPivotPos.y - m_leftHandlePos.y;
double slope = dy / dx;
double angle = std::isnan(slope) ? 0 : std::atan(slope);
if (m_leftHandlePos.x > centerPoint.x) angle += (180 * (3.14159 / 180));
p1.y = std::sin(angle) * 5;
p1.x = std::cos(angle) * 5;
angle += (120 * (3.14159 / 180));
p2.y = std::sin(angle) * 5;
p2.x = std::cos(angle) * 5;
angle += (120 * (3.14159 / 180));
p3.y = std::sin(angle) * 5;
p3.x = std::cos(angle) * 5;
glColor3d(0.50, 0.50, 0.50);
2021-11-10 08:27:26 +13:00
tglDrawSegment(m_leftHandlePos + p1 * m_unit,
m_leftHandlePos + p2 * m_unit);
tglDrawSegment(m_leftHandlePos + p2 * m_unit,
m_leftHandlePos + p3 * m_unit);
tglDrawSegment(m_leftHandlePos + p3 * m_unit,
m_leftHandlePos + p1 * m_unit);
// Draw Right Handle
glColor3d(0.70, 0.70, 0.70);
tglDrawSegment(m_rightPivotPos, m_rightHandlePos);
tglDrawCircle(m_rightHandlePos, circleRadius);
2021-11-04 03:05:17 +13:00
if (m_active) glColor3d(1.0, 1.0, 0.0);
tglDrawDisk(m_rightHandlePos, diskRadius);
dx = m_rightPivotPos.x - m_rightHandlePos.x;
dy = m_rightPivotPos.y - m_rightHandlePos.y;
slope = dy / dx;
angle = std::isnan(slope) ? 0 : std::atan(slope);
if (m_rightHandlePos.x > centerPoint.x) angle += (180 * (3.14159 / 180));
p1.y = std::sin(angle) * 5;
p1.x = std::cos(angle) * 5;
angle += (120 * (3.14159 / 180));
p2.y = std::sin(angle) * 5;
p2.x = std::cos(angle) * 5;
angle += (120 * (3.14159 / 180));
p3.y = std::sin(angle) * 5;
p3.x = std::cos(angle) * 5;
glColor3d(0.50, 0.50, 0.50);
2021-11-10 08:27:26 +13:00
tglDrawSegment(m_rightHandlePos + p1 * m_unit,
m_rightHandlePos + p2 * m_unit);
tglDrawSegment(m_rightHandlePos + p2 * m_unit,
m_rightHandlePos + p3 * m_unit);
tglDrawSegment(m_rightHandlePos + p3 * m_unit,
m_rightHandlePos + p1 * m_unit);
// Draw Left Pivot
glColor3d(0.70, 0.70, 0.70);
tglDrawSegment(centerPoint, m_leftPivotPos);
tglDrawCircle(m_leftPivotPos, circleRadius);
2021-11-04 03:05:17 +13:00
if (m_active) glColor3d(1.0, 1.0, 0.0);
tglDrawDisk(m_leftPivotPos, diskRadius);
glColor3d(0.50, 0.50, 0.50);
2021-11-10 08:27:26 +13:00
tglDrawDisk(m_leftPivotPos, 2.0 * m_unit);
2021-11-04 03:05:17 +13:00
// Draw Right Pivot
glColor3d(0.70, 0.70, 0.70);
2021-11-04 03:05:17 +13:00
tglDrawSegment(centerPoint, m_rightPivotPos);
tglDrawCircle(m_rightPivotPos, circleRadius);
2021-11-04 03:05:17 +13:00
if (m_active) glColor3d(1.0, 1.0, 0.0);
tglDrawDisk(m_rightPivotPos, diskRadius);
glColor3d(0.50, 0.50, 0.50);
2021-11-10 08:27:26 +13:00
tglDrawDisk(m_rightPivotPos, 2.0 * m_unit);
2021-11-04 03:05:17 +13:00
}
// Draw Center Point
glColor3d(0.70, 0.70, 0.70);
tglDrawCircle(centerPoint, circleRadius);
2021-11-04 03:05:17 +13:00
if (m_active) glColor3d(0.0, 1.0, 0.0);
tglDrawDisk(centerPoint, diskRadius);
glColor3d(0.50, 0.50, 0.50);
2021-11-10 08:27:26 +13:00
tglDrawSegment(centerPoint + m_unit * TPointD(-12, 0),
centerPoint + m_unit * TPointD(12, 0));
tglDrawSegment(centerPoint + m_unit * TPointD(0, 12),
centerPoint + m_unit * TPointD(0, -12));
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
glDisable(GL_LINE_STIPPLE);
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------------------
void PerspectiveControls::shiftPerspectiveObject(TPointD delta) {
TPointD centerPoint = m_perspective->getCenterPoint();
m_perspective->setCenterPoint(centerPoint + delta);
m_rotationPos += delta;
m_spacingPos += delta;
m_leftPivotPos += delta;
m_leftHandlePos += delta;
m_rightPivotPos += delta;
m_rightHandlePos += delta;
}
//----------------------------------------------------------------------------------------------
void PerspectiveSelection::enableCommands() {
enableCommand(m_tool, MI_Clear, &PerspectiveTool::deleteSelectedObjects);
2021-11-19 06:26:27 +13:00
enableCommand(m_tool, MI_Cut, &PerspectiveTool::deleteSelectedObjects);
enableCommand(m_tool, MI_SelectAll, &PerspectiveTool::selectAllObjects);
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------
PerspectiveTool::PerspectiveTool()
: TTool("T_PerspectiveGrid")
, m_type("Type:")
, m_opacity("Opacity:", 1.0, 100.0, 30.0)
, m_color("Color:")
, m_horizon("Horizon", false)
, m_parallel("Parallel", false)
, m_advancedControls("Advanced Controls", false)
2021-11-04 03:05:17 +13:00
, m_preset("Preset:")
, m_modified(false)
, m_isShifting(false)
, m_isCenterMoving(false)
, m_isRotating(false)
, m_isSpacing(false)
, m_isLeftPivoting(false)
, m_isLeftMoving(false)
, m_isRightPivoting(false)
, m_isRightMoving(false)
, m_selecting(false)
, m_selectingRect(TRectD())
2021-12-01 15:25:22 +13:00
, m_firstTime(false)
2021-11-04 03:05:17 +13:00
, m_undo(0) {
bind(TTool::AllTargets);
m_prop.bind(m_type);
m_prop.bind(m_opacity);
m_prop.bind(m_color);
m_prop.bind(m_horizon);
m_prop.bind(m_parallel);
m_prop.bind(m_advancedControls);
2021-11-04 03:05:17 +13:00
m_prop.bind(m_preset);
m_type.addValue(L"Vanishing Point");
m_type.addValue(L"Line");
m_type.setId("PerspectiveType");
m_color.addValue(L"Magenta", TPixel::Magenta);
m_color.addValue(L"Red", TPixel::Red);
m_color.addValue(L"Green", TPixel::Green);
m_color.addValue(L"Blue", TPixel::Blue);
m_color.addValue(L"Yellow", TPixel::Yellow);
m_color.addValue(L"Cyan", TPixel::Cyan);
m_color.addValue(L"Black", TPixel::Black);
m_color.setId("Color");
2021-12-01 15:25:22 +13:00
m_advancedControls.setValue(false);
2021-11-04 03:05:17 +13:00
m_preset.setId("PerspectivePreset");
m_preset.addValue(CUSTOM_WSTR);
m_selection.setTool(this);
m_selection.selectNone();
TProjectManager::instance()->addListener(this);
}
//----------------------------------------------------------------------------------------------
PerspectiveTool::~PerspectiveTool() {
TProjectManager::instance()->removeListener(this);
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::onProjectSwitched() {
m_presetsManager.clearPresets();
initPresets();
for (int i = 0; i < (int)m_toolOptionsBox.size(); i++)
m_toolOptionsBox[i]->reloadPresetCombo();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::onProjectChanged() {
m_presetsManager.clearPresets();
initPresets();
for (int i = 0; i < (int)m_toolOptionsBox.size(); i++)
m_toolOptionsBox[i]->reloadPresetCombo();
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::updateTranslation() {
m_type.setQStringName(tr("Type:"));
m_type.setItemUIName(L"Vanishing Point", tr("Vanishing Point"));
m_type.setItemUIName(L"Line", tr("Line"));
m_color.setQStringName(tr("Color:"));
m_color.setItemUIName(L"Magenta", tr("Magenta"));
m_color.setItemUIName(L"Red", tr("Red"));
m_color.setItemUIName(L"Green", tr("Green"));
m_color.setItemUIName(L"Blue", tr("Blue"));
m_color.setItemUIName(L"Yellow", tr("Yellow"));
m_color.setItemUIName(L"Cyan", tr("Cyan"));
m_color.setItemUIName(L"Black", tr("Black"));
m_opacity.setQStringName(tr("Opacity:"));
m_horizon.setQStringName(tr("Horizon"));
m_parallel.setQStringName(tr("Parallel"));
m_advancedControls.setQStringName(tr("Advanced Controls"));
2021-11-04 03:05:17 +13:00
m_preset.setQStringName(tr("Preset:"));
m_preset.setItemUIName(CUSTOM_WSTR, tr("<custom>"));
}
//----------------------------------------------------------------------------------------------
TPropertyGroup *PerspectiveTool::getProperties(int idx) {
initPresets();
2021-11-04 03:05:17 +13:00
return &m_prop;
}
void PerspectiveTool::setToolOptionsBox(
PerspectiveGridToolOptionBox *toolOptionsBox) {
m_toolOptionsBox.push_back(toolOptionsBox);
}
2021-12-01 15:25:22 +13:00
//-----------------------------------------------------------------------------
void PerspectiveTool::onActivate() {
if (!m_firstTime) {
m_firstTime = true;
m_advancedControls.setValue(PerspectiveToolAdvancedControls);
}
}
2021-11-04 03:05:17 +13:00
//----------------------------------------------------------------------------------------------
bool PerspectiveTool::onPropertyChanged(std::string propertyName) {
if (m_propertyUpdating) return true;
std::set<int> selectedObjects = m_selection.getSelectedObjects();
std::set<int>::iterator it;
2021-11-04 03:05:17 +13:00
if (propertyName == m_preset.getName()) {
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setActive(false);
m_selection.selectNone();
2021-11-04 03:05:17 +13:00
if (m_preset.getValue() != CUSTOM_WSTR)
loadPreset();
else // Chose <custom>, go back to last preset
loadLastPreset();
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
return true;
}
if (propertyName == m_advancedControls.getName()) {
PerspectiveToolAdvancedControls = m_advancedControls.getValue();
for (int i = 0; i < m_perspectiveObjs.size(); i++)
m_perspectiveObjs[i]->setShowAdvancedControls(
PerspectiveToolAdvancedControls);
return true;
}
2021-11-04 03:05:17 +13:00
if (propertyName != m_type.getName() && selectedObjects.size() &&
m_preset.getValue() != CUSTOM_WSTR) {
m_preset.setValue(CUSTOM_WSTR);
m_lastPreset = copyPerspectiveSet(m_perspectiveObjs);
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setActive(false);
2021-11-04 03:05:17 +13:00
loadLastPreset();
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
}
if (selectedObjects.size() && !m_undo)
m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this);
if (propertyName == m_opacity.getName()) {
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setOpacity(m_opacity.getValue());
invalidate();
} else if (propertyName == m_horizon.getName()) {
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setHorizon(m_horizon.getValue());
} else if (propertyName == m_color.getName()) {
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setColor(m_color.getColorValue());
} else if (propertyName == m_parallel.getName()) {
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setParallel(m_parallel.getValue());
}
if (m_undo) {
m_undo->setRedoData(m_perspectiveObjs);
TUndoManager::manager()->add(m_undo);
m_undo = 0;
}
return true;
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::draw(SceneViewer *viewer) {
if (!m_perspectiveObjs.size()) return;
TRectD cameraRect = getApplication()
->getCurrentScene()
->getScene()
->getCurrentCamera()
->getStageRect();
for (int i = 0; i < m_perspectiveObjs.size(); i++) {
if (!m_perspectiveObjs[i]) continue;
m_perspectiveObjs[i]->draw(viewer, cameraRect);
}
bool drawControls = getApplication()->getCurrentTool()->getTool() == this;
if (drawControls) {
for (int i = 0; i < m_perspectiveObjs.size(); i++)
m_perspectiveObjs[i]->drawControls();
}
if (m_selecting) {
TPixel color = TPixel32::Red;
ToolUtils::drawRect(m_selectingRect, color, 0xFFFF, true);
}
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
m_modified = false;
m_isShifting = false;
m_isCenterMoving = false;
m_isRotating = false;
m_isSpacing = false;
m_isLeftPivoting = false;
m_isLeftMoving = false;
m_isRightPivoting = false;
m_isRightMoving = false;
m_selecting = false;
2021-11-13 20:35:29 +13:00
m_totalChange = 0;
2021-11-04 03:05:17 +13:00
m_firstPos = pos;
int controlIdx = -1;
2021-11-10 08:27:26 +13:00
for (int i = 0; i < m_perspectiveObjs.size(); i++) {
m_perspectiveObjs[i]->setCursorPos(pos);
2021-11-04 03:05:17 +13:00
if (m_perspectiveObjs[i]->isOverControl()) {
controlIdx = i;
break;
}
2021-11-10 08:27:26 +13:00
}
2021-11-04 03:05:17 +13:00
if (controlIdx >= 0) {
m_isCenterMoving = m_perspectiveObjs[controlIdx]->isOverCenterPoint();
m_isRotating = m_perspectiveObjs[controlIdx]->isOverRotation();
m_isSpacing = m_perspectiveObjs[controlIdx]->isOverSpacing();
m_isLeftPivoting = m_perspectiveObjs[controlIdx]->isOverLeftPivot();
m_isLeftMoving = m_perspectiveObjs[controlIdx]->isOverLeftHandle();
m_isRightPivoting = m_perspectiveObjs[controlIdx]->isOverRightPivot();
m_isRightMoving = m_perspectiveObjs[controlIdx]->isOverRightHandle();
// Hit a control while pressing CTRL: add/remove from selection
if (e.isCtrlPressed()) {
if (!m_selection.isSelected(controlIdx)) {
m_selection.select(controlIdx);
m_perspectiveObjs[controlIdx]->setActive(true);
m_mainControlIndex = controlIdx;
} else {
m_selection.unselect(controlIdx);
m_perspectiveObjs[controlIdx]->setActive(false);
m_isCenterMoving = m_isRotating = m_isSpacing = m_isLeftPivoting =
m_isLeftMoving = m_isRightPivoting = m_isRightMoving = false;
m_mainControlIndex = -1;
}
m_selection.makeCurrent();
invalidateControl(controlIdx);
m_modified = true;
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
return;
}
m_mainControlIndex = controlIdx;
if (e.isShiftPressed()) m_isShifting = true;
// Hit a control while not pressing Ctrl and is already active: Do nothing
if (m_perspectiveObjs[controlIdx]->isActive()) {
updateToolOptionValues();
return;
}
2021-11-04 03:05:17 +13:00
// Hit a control while not pressing Ctrl: Clear selection and make current
// active
for (int i = 0; i < m_perspectiveObjs.size(); i++)
if (m_perspectiveObjs[i]->isActive()) {
m_perspectiveObjs[i]->setActive(false);
invalidateControl(i);
}
m_selection.selectNone();
m_selection.select(controlIdx);
m_perspectiveObjs[controlIdx]->setActive(true);
m_selection.makeCurrent();
invalidateControl(controlIdx);
// Update toolbar options
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
return;
} else {
// Hit empty space
if (e.isCtrlPressed()) {
m_selecting = true; // Assume we're area selecting
m_selectingRect.x0 = pos.x;
m_selectingRect.y0 = pos.y;
m_selectingRect.x1 = pos.x + 1;
m_selectingRect.y1 = pos.y + 1;
return;
} else if (e.isShiftPressed()) // Do nothing
return;
// Hit empty space while not pressing CTRL...
// ... If we have only 1 selected: Clear current selection, add new
// ... If more than 1: Clear selections and do nothing
int wasSelected = m_selection.count();
for (int i = 0; i < m_perspectiveObjs.size(); i++)
if (m_perspectiveObjs[i]->isActive()) {
m_perspectiveObjs[i]->setActive(false);
invalidateControl(i);
}
m_selection.selectNone();
if (wasSelected > 1) {
m_mainControlIndex = -1;
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
return;
}
}
if (m_preset.getValue() != CUSTOM_WSTR) {
m_preset.setValue(CUSTOM_WSTR);
m_lastPreset = copyPerspectiveSet(m_perspectiveObjs);
loadLastPreset();
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
}
m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this);
PerspectiveObject *newObject;
switch (m_type.getIndex()) {
case 0:
newObject = new VanishingPointPerspective();
break;
case 1:
newObject = new LinePerspective();
break;
default:
return;
}
newObject->setCenterPoint(pos);
newObject->setRotationPos(pos + newObject->getRotationPos());
newObject->setSpacingPos(pos + newObject->getSpacingPos());
newObject->setLeftPivotPos(pos + newObject->getLeftPivotPos());
newObject->setLeftHandlePos(pos + newObject->getLeftHandlePos());
newObject->setRightPivotPos(pos + newObject->getRightPivotPos());
newObject->setRightHandlePos(pos + newObject->getRightHandlePos());
newObject->setOpacity(m_opacity.getValue());
newObject->setColor(m_color.getColorValue());
newObject->setHorizon(m_horizon.getValue());
newObject->setParallel(m_parallel.getValue());
newObject->setShowAdvancedControls(PerspectiveToolAdvancedControls);
2021-11-04 03:05:17 +13:00
newObject->setActive(true);
m_perspectiveObjs.push_back(newObject);
m_lastPreset.push_back(newObject);
m_mainControlIndex = m_perspectiveObjs.size() - 1;
m_selection.select(m_mainControlIndex);
m_selection.makeCurrent();
m_isShifting = true; // Allow click and shift
m_modified = true;
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
invalidate();
}
//----------------------------------------------------------------------------------------------
TPointD calculateHandlePos(TPointD handlePos, TPointD pivotPos,
TPointD pivotMove, TPointD centerPoint) {
TPointD newHandlePos;
TPointD newPivotPos = pivotPos + pivotMove;
double dOrigPivotHandle = std::sqrt(std::pow((pivotPos.x - handlePos.x), 2) +
std::pow((pivotPos.y - handlePos.y), 2));
double dCenterNewPivot =
std::sqrt(std::pow((centerPoint.x - newPivotPos.x), 2) +
std::pow((centerPoint.y - newPivotPos.y), 2));
double dNewCenterHandle = dOrigPivotHandle + dCenterNewPivot;
double dratio = dCenterNewPivot / dNewCenterHandle;
newHandlePos.x = (newPivotPos.x - ((1 - dratio) * centerPoint.x)) / dratio;
newHandlePos.y = (newPivotPos.y - ((1 - dratio) * centerPoint.y)) / dratio;
return newHandlePos;
}
TPointD calculateCenterPoint(TPointD leftHandlePos, TPointD leftPivotPos,
TPointD rightHandlePos, TPointD rightPivotPos) {
TPointD newCenterPoint;
double xNum = (((leftHandlePos.x * leftPivotPos.y) -
(leftHandlePos.y * leftPivotPos.x)) *
(rightHandlePos.x - rightPivotPos.x)) -
((leftHandlePos.x - leftPivotPos.x) *
((rightHandlePos.x * rightPivotPos.y) -
(rightHandlePos.y * rightPivotPos.x)));
double yNum = (((leftHandlePos.x * leftPivotPos.y) -
(leftHandlePos.y * leftPivotPos.x)) *
(rightHandlePos.y - rightPivotPos.y)) -
((leftHandlePos.y - leftPivotPos.y) *
((rightHandlePos.x * rightPivotPos.y) -
(rightHandlePos.y * rightPivotPos.x)));
double denom = ((leftHandlePos.x - leftPivotPos.x) *
(rightHandlePos.y - rightPivotPos.y)) -
((leftHandlePos.y - leftPivotPos.y) *
(rightHandlePos.x - rightPivotPos.x));
2021-11-06 16:33:30 +13:00
newCenterPoint.x = xNum / denom;
newCenterPoint.y = yNum / denom;
2021-11-04 03:05:17 +13:00
return newCenterPoint;
}
2021-11-05 11:09:50 +13:00
2021-11-13 20:35:29 +13:00
TPointD calculatePointOnLine(TPointD firstPoint, double rotation,
double distance) {
TPointD refPoint;
double theta = rotation * (3.14159 / 180);
double yLength = std::sin(theta) * distance;
double xLength = std::cos(theta) * distance;
refPoint.x = firstPoint.x + xLength;
refPoint.y = firstPoint.y + yLength;
return refPoint;
}
2021-11-17 17:51:14 +13:00
double calculateSpacing(PerspectiveType perspectiveType, TPointD spacingPos,
TPointD rotationPos, double rotation,
TPointD centerPoint) {
double newSpace;
double dx = spacingPos.x - centerPoint.x;
double dy = spacingPos.y - centerPoint.y;
double distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
double angle = std::atan2(dy, dx) / (3.14159 / 180);
if (perspectiveType == PerspectiveType::VanishingPoint) {
if (angle < 0) angle += 360;
double drx = rotationPos.x - centerPoint.x;
double dry = rotationPos.y - centerPoint.y;
double rangle = std::atan2(dry, drx) / (3.14159 / 180);
if (rangle < 0) rangle += 360;
newSpace = std::abs(rangle - angle);
if (newSpace > 180) newSpace = 360 - newSpace;
} else { // Assumed Line
angle -= rotation;
TPointD normPos = calculatePointOnLine(centerPoint, angle, distance);
newSpace = std::abs(normPos.y - centerPoint.y);
}
return newSpace;
}
TPointD calculateSpacingPos(PerspectiveType perspectiveType, double newSpace,
double rotation, TPointD oldSpacingPos,
TPointD rotationPos, TPointD centerPoint) {
TPointD newSpacingPos;
double dx = oldSpacingPos.x - centerPoint.x;
double dy = oldSpacingPos.y - centerPoint.y;
double distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
double angle;
if (perspectiveType == PerspectiveType::VanishingPoint) {
double drx = rotationPos.x - centerPoint.x;
double dry = rotationPos.y - centerPoint.y;
double rangle = std::atan2(dry, drx) / (3.14159 / 180);
if (rangle < 0) rangle += 360;
angle = rangle + newSpace;
} else { // Assumed Line
angle = std::atan2(dy, dx) / (3.14159 / 180);
angle -= rotation;
TPointD normPos = calculatePointOnLine(centerPoint, angle, distance);
dy = newSpace;
if ((normPos.y - centerPoint.y) < 0) dy *= -1;
dx = normPos.x - centerPoint.x;
distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
angle = std::atan2(dy, dx) / (3.14159 / 180);
angle += rotation;
}
newSpacingPos = calculatePointOnLine(centerPoint, angle, distance);
return newSpacingPos;
}
2021-11-04 03:05:17 +13:00
void PerspectiveTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
if (!m_perspectiveObjs.size()) return;
2021-11-13 20:35:29 +13:00
TPointD usePos = pos;
2021-11-04 03:05:17 +13:00
if (m_selecting) {
2021-11-13 20:35:29 +13:00
m_selectingRect.x1 = usePos.x;
m_selectingRect.y1 = usePos.y;
2021-11-04 03:05:17 +13:00
TRectD rect(m_selectingRect);
if (rect.x0 > rect.x1) std::swap(rect.x0, rect.x1);
if (rect.y0 > rect.y1) std::swap(rect.y0, rect.y1);
std::vector<PerspectiveObject *>::iterator it;
for (int i = 0; i < m_perspectiveObjs.size(); i++) {
PerspectiveObject *obj = m_perspectiveObjs[i];
if (obj->isActive()) continue;
if (rect.contains(obj->getCenterPoint())) {
m_selection.select(i);
obj->setActive(true);
}
}
invalidate();
return;
}
if (m_selection.isEmpty() || m_mainControlIndex < 0) return;
std::set<int> selectedObjects = m_selection.getSelectedObjects();
std::set<int>::iterator it;
2021-11-04 03:05:17 +13:00
if (m_preset.getValue() != CUSTOM_WSTR) {
m_preset.setValue(CUSTOM_WSTR);
m_lastPreset = copyPerspectiveSet(m_perspectiveObjs);
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setActive(false);
2021-11-04 03:05:17 +13:00
loadLastPreset();
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
}
if (!m_undo) m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this);
bool applyToSelection = true;
PerspectiveObject *mainObj = m_perspectiveObjs[m_mainControlIndex];
TPointD centerPoint = mainObj->getCenterPoint();
2021-11-13 20:35:29 +13:00
TPointD rotationPos = mainObj->getRotationPos();
2021-11-17 17:51:14 +13:00
TPointD spacingPos = mainObj->getSpacingPos();
2021-11-13 20:35:29 +13:00
TPointD dPos = usePos - m_firstPos;
2021-11-04 03:05:17 +13:00
double dAngle = 0.0;
double dSpace = 0.0;
if (m_isRotating) {
2021-11-13 20:35:29 +13:00
TPointD newRotationPos = rotationPos + dPos;
2021-11-04 03:05:17 +13:00
2021-11-13 20:35:29 +13:00
double dox = rotationPos.x - centerPoint.x;
double doy = rotationPos.y - centerPoint.y;
double oangle = std::atan2(doy, dox) / (3.14159 / 180);
if (oangle < 0) oangle += 360;
2021-11-04 03:05:17 +13:00
2021-11-13 20:35:29 +13:00
double dnx = newRotationPos.x - centerPoint.x;
double dny = newRotationPos.y - centerPoint.y;
double nangle = std::atan2(dny, dnx) / (3.14159 / 180);
if (nangle < 0) nangle += 360;
if ((oangle >= 270 && oangle <= 360 && nangle >= 0 && nangle <= 90))
dAngle = (360 - oangle) + nangle;
else if ((oangle >= 0 && oangle <= 90 && nangle >= 270 && nangle <= 360))
dAngle = (oangle + (360 - nangle)) * -1;
else
dAngle = nangle - oangle;
2021-11-04 03:05:17 +13:00
} else if (m_isSpacing) {
2021-11-17 17:51:14 +13:00
TPointD newSpacingPos = spacingPos + dPos;
double newSpace;
if (mainObj->getType() == PerspectiveType::VanishingPoint) {
double dox = spacingPos.x - centerPoint.x;
double doy = spacingPos.y - centerPoint.y;
double oangle = std::atan2(doy, dox) / (3.14159 / 180);
double dnx = newSpacingPos.x - centerPoint.x;
double dny = newSpacingPos.y - centerPoint.y;
double nangle = std::atan2(dny, dnx) / (3.14159 / 180);
dAngle = nangle - oangle;
if (nangle < 0) nangle += 360;
double drx = rotationPos.x - centerPoint.x;
double dry = rotationPos.y - centerPoint.y;
double rangle = std::atan2(dry, drx) / (3.14159 / 180);
if (rangle < 0) rangle += 360;
newSpace = std::abs(rangle - nangle);
if (newSpace > 180) newSpace = 360 - newSpace;
dSpace = newSpace - mainObj->getSpacing();
} else { // Assumed Line
double dx = newSpacingPos.x - centerPoint.x;
double dy = newSpacingPos.y - centerPoint.y;
double distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
double angle = std::atan2(dy, dx) / (3.14159 / 180);
angle -= mainObj->getRotation();
TPointD normPos = calculatePointOnLine(centerPoint, angle, distance);
newSpace = std::abs(normPos.y - centerPoint.y);
dSpace = newSpace - mainObj->getSpacing();
}
2021-11-04 03:05:17 +13:00
} else if (!m_isShifting &&
mainObj->getType() == PerspectiveType::VanishingPoint) {
TPointD leftPivotPos = mainObj->getLeftPivotPos();
TPointD leftHandlePos = mainObj->getLeftHandlePos();
TPointD rightPivotPos = mainObj->getRightPivotPos();
TPointD rightHandlePos = mainObj->getRightHandlePos();
if (m_isLeftMoving) {
TPointD oldCenterPoint = centerPoint;
TPointD newPos = leftHandlePos + dPos;
2021-11-04 03:05:17 +13:00
mainObj->setLeftHandlePos(newPos);
// Also move Center Point
2021-11-06 16:33:30 +13:00
TPointD newCenterPoint;
2021-11-13 20:35:29 +13:00
TPointD dPosCP;
if (mainObj->isHorizon() && e.isAltPressed()) {
TPointD otherHorizonPoint =
2021-11-13 20:35:29 +13:00
calculatePointOnLine(centerPoint, mainObj->getRotation(), 100);
2021-11-06 16:33:30 +13:00
newCenterPoint = calculateCenterPoint(newPos, leftPivotPos, centerPoint,
otherHorizonPoint);
} else
2021-11-06 16:33:30 +13:00
newCenterPoint = calculateCenterPoint(newPos, leftPivotPos,
rightHandlePos, rightPivotPos);
2021-11-13 20:35:29 +13:00
if (!std::isnan(newCenterPoint.x) && !std::isnan(newCenterPoint.y)) {
dPosCP = newCenterPoint - centerPoint;
2021-11-06 16:33:30 +13:00
centerPoint = newCenterPoint;
2021-11-13 20:35:29 +13:00
}
2021-11-04 03:05:17 +13:00
mainObj->setCenterPoint(centerPoint);
2021-11-17 17:51:14 +13:00
if (mainObj->isHorizon() && e.isAltPressed()) {
// Move Rotation/Space controls to maintain angle/spacing
2021-11-13 20:35:29 +13:00
mainObj->setRotationPos(rotationPos + dPosCP);
2021-11-17 17:51:14 +13:00
mainObj->setSpacingPos(spacingPos + dPosCP);
updateMeasuredValueToolOptions();
2021-11-17 17:51:14 +13:00
} else {
// Recalculate Angle
double dox = rotationPos.x - oldCenterPoint.x;
double doy = rotationPos.y - oldCenterPoint.y;
double oangle = std::atan2(doy, dox) / (3.14159 / 180);
if (oangle < 0) oangle += 360;
double dnx = rotationPos.x - newCenterPoint.x;
double dny = rotationPos.y - newCenterPoint.y;
double nangle = std::atan2(dny, dnx) / (3.14159 / 180);
if (nangle < 0) nangle += 360;
double dAngle;
if ((oangle >= 270 && oangle <= 360 && nangle >= 0 && nangle <= 90))
dAngle = (360 - oangle) + nangle;
else if ((oangle >= 0 && oangle <= 90 && nangle >= 270 &&
nangle <= 360))
dAngle = (oangle + (360 - nangle)) * -1;
else
dAngle = nangle - oangle;
mainObj->setRotation(mainObj->getRotation() + dAngle);
2021-11-17 17:51:14 +13:00
// Recalculate Spacing
double newSpacing =
calculateSpacing(mainObj->getType(), spacingPos, rotationPos,
mainObj->getRotation(), centerPoint);
mainObj->setSpacing(newSpacing);
updateMeasuredValueToolOptions();
2021-11-13 20:35:29 +13:00
}
2021-11-04 03:05:17 +13:00
// Check Right Handle
rightHandlePos = calculateHandlePos(rightHandlePos, rightPivotPos,
TPointD(0.0, 0.0), centerPoint);
mainObj->setRightHandlePos(rightHandlePos);
applyToSelection = false;
} else if (m_isRightMoving) {
TPointD oldCenterPoint = centerPoint;
TPointD newPos = rightHandlePos + dPos;
2021-11-04 03:05:17 +13:00
mainObj->setRightHandlePos(newPos);
2021-11-13 20:35:29 +13:00
2021-11-04 03:05:17 +13:00
// Also move Center Point
2021-11-06 16:33:30 +13:00
TPointD newCenterPoint;
2021-11-13 20:35:29 +13:00
TPointD dPosCP;
if (mainObj->isHorizon() && e.isAltPressed()) {
TPointD otherHorizonPoint =
2021-11-13 20:35:29 +13:00
calculatePointOnLine(centerPoint, mainObj->getRotation(), 100);
2021-11-06 16:33:30 +13:00
newCenterPoint = calculateCenterPoint(centerPoint, otherHorizonPoint,
newPos, rightPivotPos);
} else
2021-11-06 16:33:30 +13:00
newCenterPoint = calculateCenterPoint(leftHandlePos, leftPivotPos,
newPos, rightPivotPos);
2021-11-13 20:35:29 +13:00
if (!std::isnan(newCenterPoint.x) && !std::isnan(newCenterPoint.y)) {
dPosCP = newCenterPoint - centerPoint;
2021-11-06 16:33:30 +13:00
centerPoint = newCenterPoint;
2021-11-13 20:35:29 +13:00
}
2021-11-04 03:05:17 +13:00
mainObj->setCenterPoint(centerPoint);
2021-11-17 17:51:14 +13:00
if (mainObj->isHorizon() && e.isAltPressed()) {
// Move Rotation/Space controls to maintain angle/spacing;
2021-11-13 20:35:29 +13:00
mainObj->setRotationPos(rotationPos + dPosCP);
2021-11-17 17:51:14 +13:00
mainObj->setSpacingPos(spacingPos + dPosCP);
updateMeasuredValueToolOptions();
2021-11-17 17:51:14 +13:00
} else {
// Recalculate Angle
double dox = rotationPos.x - oldCenterPoint.x;
double doy = rotationPos.y - oldCenterPoint.y;
double oangle = std::atan2(doy, dox) / (3.14159 / 180);
if (oangle < 0) oangle += 360;
double dnx = rotationPos.x - newCenterPoint.x;
double dny = rotationPos.y - newCenterPoint.y;
double nangle = std::atan2(dny, dnx) / (3.14159 / 180);
if (nangle < 0) nangle += 360;
double dAngle;
if ((oangle >= 270 && oangle <= 360 && nangle >= 0 && nangle <= 90))
dAngle = (360 - oangle) + nangle;
else if ((oangle >= 0 && oangle <= 90 && nangle >= 270 &&
nangle <= 360))
dAngle = (oangle + (360 - nangle)) * -1;
else
dAngle = nangle - oangle;
mainObj->setRotation(mainObj->getRotation() + dAngle);
2021-11-17 17:51:14 +13:00
// Recalculate Spacing
double newSpacing =
calculateSpacing(mainObj->getType(), spacingPos, rotationPos,
mainObj->getRotation(), centerPoint);
mainObj->setSpacing(newSpacing);
updateMeasuredValueToolOptions();
2021-11-13 20:35:29 +13:00
}
2021-11-04 03:05:17 +13:00
// Check Left Handle
leftHandlePos = calculateHandlePos(leftHandlePos, leftPivotPos,
TPointD(0.0, 0.0), centerPoint);
mainObj->setLeftHandlePos(leftHandlePos);
applyToSelection = false;
} else if (m_isLeftPivoting) {
TPointD newPos = leftPivotPos + dPos;
mainObj->setLeftPivotPos(newPos);
// Also move Left Handle
leftHandlePos =
calculateHandlePos(leftHandlePos, leftPivotPos, dPos, centerPoint);
mainObj->setLeftHandlePos(leftHandlePos);
applyToSelection = false;
} else if (m_isRightPivoting) {
TPointD newPos = rightPivotPos + dPos;
mainObj->setRightPivotPos(newPos);
// Also move Right Handle
rightHandlePos =
calculateHandlePos(rightHandlePos, rightPivotPos, dPos, centerPoint);
mainObj->setRightHandlePos(rightHandlePos);
2021-11-04 03:05:17 +13:00
applyToSelection = false;
}
}
if (applyToSelection) {
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++) {
PerspectiveObject *obj = m_perspectiveObjs[*it];
2021-11-13 20:35:29 +13:00
TPointD objCenterPoint = obj->getCenterPoint();
TPointD rotationPos = obj->getRotationPos();
2021-11-17 17:51:14 +13:00
TPointD spacingPos = obj->getSpacingPos();
2021-11-13 20:35:29 +13:00
if (m_isShifting) {
if (obj->isHorizon() && e.isAltPressed()) {
double distance =
std::sqrt(std::pow(dPos.x, 2) + std::pow(dPos.y, 2));
double rotation = obj->getRotation();
if (rotation != 90 && rotation != 270) {
if (dPos.x < 0) distance *= -1;
if (rotation > 90 && rotation < 270) distance *= -1;
} else if ((rotation == 90 && dPos.y < 0) ||
(rotation == 270 && dPos.y > 0))
distance *= -1;
TPointD newCenter =
2021-11-13 20:35:29 +13:00
calculatePointOnLine(objCenterPoint, rotation, distance);
dPos = newCenter - objCenterPoint;
}
obj->shiftPerspectiveObject(dPos);
} else if (m_isCenterMoving) { // Moving
2021-11-13 20:35:29 +13:00
TPointD newCenterPoint = objCenterPoint + dPos;
obj->setCenterPoint(newCenterPoint);
// Recalculate Angle
double dox = rotationPos.x - objCenterPoint.x;
double doy = rotationPos.y - objCenterPoint.y;
double oangle = std::atan2(doy, dox) / (3.14159 / 180);
if (oangle < 0) oangle += 360;
double dnx = rotationPos.x - newCenterPoint.x;
double dny = rotationPos.y - newCenterPoint.y;
double nangle = std::atan2(dny, dnx) / (3.14159 / 180);
if (nangle < 0) nangle += 360;
double dAngle;
if ((oangle >= 270 && oangle <= 360 && nangle >= 0 && nangle <= 90))
dAngle = (360 - oangle) + nangle;
else if ((oangle >= 0 && oangle <= 90 && nangle >= 270 &&
nangle <= 360))
dAngle = (oangle + (360 - nangle)) * -1;
else
dAngle = nangle - oangle;
obj->setRotation(obj->getRotation() + dAngle);
// Recalculate Spacing
double newSpacing =
calculateSpacing(obj->getType(), spacingPos, rotationPos,
obj->getRotation(), objCenterPoint);
obj->setSpacing(newSpacing);
if (obj == mainObj) updateMeasuredValueToolOptions();
2021-11-04 03:05:17 +13:00
// Also Move Left and Right Handles
if (obj->getType() == PerspectiveType::VanishingPoint) {
TPointD leftPivotPos = obj->getLeftPivotPos();
TPointD leftHandlePos = obj->getLeftHandlePos();
TPointD rightPivotPos = obj->getRightPivotPos();
TPointD rightHandlePos = obj->getRightHandlePos();
leftHandlePos = calculateHandlePos(leftHandlePos, leftPivotPos,
TPointD(0.0, 0.0), objCenterPoint);
obj->setLeftHandlePos(leftHandlePos);
rightHandlePos = calculateHandlePos(
rightHandlePos, rightPivotPos, TPointD(0.0, 0.0), objCenterPoint);
obj->setRightHandlePos(rightHandlePos);
}
} else if (m_isRotating) {
double rotation = obj->getRotation();
2021-11-13 20:35:29 +13:00
TPointD newRotationPos = rotationPos + dPos;
double rot =
rotation +
((obj->getType() == PerspectiveType::VanishingPoint) ? 270 : 0);
TPointD rotPos = obj == mainObj ? usePos : rotationPos;
double distance = std::sqrt(std::pow((objCenterPoint.x - rotPos.x), 2) +
std::pow((objCenterPoint.y - rotPos.y), 2));
if (e.isAltPressed()) {
2021-11-13 20:35:29 +13:00
double ang = tfloor((int)((rot + dAngle) + 7.5), 15);
dAngle = ang - rot;
newRotationPos =
calculatePointOnLine(objCenterPoint, (rot + dAngle), distance);
if (obj == mainObj)
usePos = dAngle == 0 ? m_firstPos : newRotationPos;
} else if (obj != mainObj) {
newRotationPos =
calculatePointOnLine(objCenterPoint, (rot + dAngle), distance);
}
2021-11-04 03:05:17 +13:00
obj->setRotation(rotation + dAngle);
2021-11-13 20:35:29 +13:00
obj->setRotationPos(newRotationPos);
2021-11-17 17:51:14 +13:00
// Move Spacing control to keep current spacing
double dx = spacingPos.x - objCenterPoint.x;
double dy = spacingPos.y - objCenterPoint.y;
distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
2021-11-17 17:51:14 +13:00
double angle = std::atan2(dy, dx) / (3.14159 / 180);
double nangle = angle + dAngle;
TPointD newSpacingPos =
calculatePointOnLine(objCenterPoint, nangle, distance);
obj->setSpacingPos(newSpacingPos);
if (obj == mainObj) updateMeasuredValueToolOptions();
2021-11-04 03:05:17 +13:00
} else if (m_isSpacing) {
2021-11-17 17:51:14 +13:00
double spacing = obj->getSpacing();
TPointD newSpacingPos = spacingPos + dPos;
double delta = dSpace;
2021-11-04 03:05:17 +13:00
2021-11-17 17:51:14 +13:00
TPointD pos = obj == mainObj ? usePos : spacingPos;
double dx = pos.x - objCenterPoint.x;
double dy = pos.y - objCenterPoint.y;
double distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
double angle = std::atan2(dy, dx) / (3.14159 / 180);
if (e.isAltPressed()) {
double space = tfloor((int)(spacing + delta + 5), 10);
delta = space - spacing;
if (obj->getType() == PerspectiveType::VanishingPoint) {
double dx2 = newSpacingPos.x - objCenterPoint.x;
double dy2 = newSpacingPos.y - objCenterPoint.y;
double angle2 = std::atan2(dy2, dx2) / (3.14159 / 180);
if (angle2 < 0) angle2 += 360;
double drx = rotationPos.x - objCenterPoint.x;
double dry = rotationPos.y - objCenterPoint.y;
double rangle = std::atan2(dry, drx) / (3.14159 / 180);
if (rangle < 0) rangle += 360;
double diffAngle = angle2 - rangle;
if (diffAngle < 0) diffAngle += 360;
double newAngle = spacing + delta + 270;
if (newAngle < 0) newAngle += 360;
if (diffAngle > 180) {
newAngle *= -1;
distance *= -1;
}
newAngle += obj->getRotation();
newSpacingPos =
calculatePointOnLine(objCenterPoint, newAngle, distance);
} else { // Assumed Line
angle -= obj->getRotation();
TPointD normPos =
calculatePointOnLine(objCenterPoint, angle, distance);
dy = spacing + delta;
if ((normPos.y - objCenterPoint.y) < 0) dy *= -1;
dx = normPos.x - objCenterPoint.x;
distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
angle = std::atan2(dy, dx) / (3.14159 / 180);
angle += obj->getRotation();
newSpacingPos =
calculatePointOnLine(objCenterPoint, angle, distance);
}
if (obj == mainObj) usePos = delta == 0 ? m_firstPos : newSpacingPos;
} else if (obj != mainObj) {
if (obj->getType() == PerspectiveType::VanishingPoint) {
double nangle = angle + dAngle;
newSpacingPos =
calculatePointOnLine(objCenterPoint, nangle, distance);
if (nangle < 0) nangle += 360;
double drx = rotationPos.x - objCenterPoint.x;
double dry = rotationPos.y - objCenterPoint.y;
double rangle = std::atan2(dry, drx) / (3.14159 / 180);
if (rangle < 0) rangle += 360;
double newSpace = std::abs(rangle - nangle);
if (newSpace > 180) newSpace = 360 - newSpace;
delta = newSpace - spacing;
} else { // Assumed line
angle -= obj->getRotation();
TPointD normPos =
calculatePointOnLine(objCenterPoint, angle, distance);
dy = spacing + delta;
if ((normPos.y - objCenterPoint.y) < 0) dy *= -1;
dx = normPos.x - objCenterPoint.x;
distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
angle = std::atan2(dy, dx) / (3.14159 / 180);
angle += obj->getRotation();
newSpacingPos =
calculatePointOnLine(objCenterPoint, angle, distance);
}
}
obj->setSpacing(spacing + delta);
obj->setSpacingPos(newSpacingPos);
if (obj == mainObj) updateMeasuredValueToolOptions();
2021-11-04 03:05:17 +13:00
}
}
}
m_modified = true;
2021-11-13 20:35:29 +13:00
m_firstPos = usePos;
2021-11-04 03:05:17 +13:00
invalidate();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) {
if (m_undo) {
if (!m_modified)
delete m_undo;
else {
m_undo->setRedoData(m_perspectiveObjs);
TUndoManager::manager()->add(m_undo);
}
m_undo = 0;
}
invalidate();
m_modified = false;
m_isShifting = false;
m_isCenterMoving = false;
m_isRotating = false;
m_isSpacing = false;
m_isLeftPivoting = false;
m_isLeftMoving = false;
m_isRightPivoting = false;
m_isRightMoving = false;
m_selecting = false;
2021-11-13 20:35:29 +13:00
m_totalChange = 0;
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------
bool PerspectiveTool::keyDown(QKeyEvent *event) {
TPointD delta;
switch (event->key()) {
case Qt::Key_Up:
delta.y = 1;
break;
case Qt::Key_Down:
delta.y = -1;
break;
case Qt::Key_Left:
delta.x = -1;
break;
case Qt::Key_Right:
delta.x = 1;
break;
default:
return false;
break;
}
std::set<int> selectedObjects = m_selection.getSelectedObjects();
std::set<int>::iterator it;
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->shiftPerspectiveObject(delta);
return true;
}
//----------------------------------------------------------------------------------------------
2021-11-04 03:05:17 +13:00
void PerspectiveTool::invalidateControl(int controlIdx) {
if (!m_perspectiveObjs.size() || controlIdx >= m_perspectiveObjs.size())
return;
invalidate(m_perspectiveObjs[controlIdx]->getCenterPointRect());
invalidate(m_perspectiveObjs[controlIdx]->getRotationRect());
invalidate(m_perspectiveObjs[controlIdx]->getSpacingRect());
invalidate(m_perspectiveObjs[controlIdx]->getLeftPivotRect());
invalidate(m_perspectiveObjs[controlIdx]->getLeftHandleRect());
invalidate(m_perspectiveObjs[controlIdx]->getRightPivotRect());
invalidate(m_perspectiveObjs[controlIdx]->getRightHandleRect());
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::onEnter() {
if (!m_selection.isEmpty()) return;
for (int i = 0; i < m_perspectiveObjs.size(); i++)
if (m_perspectiveObjs[i]->isActive()) m_selection.select(i);
if (!m_selection.isEmpty()) m_selection.makeCurrent();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::onDeactivate() {
if (m_selection.isEmpty()) return;
for (int i = 0; i < m_perspectiveObjs.size(); i++)
if (m_perspectiveObjs[i]->isActive()) {
m_perspectiveObjs[i]->setActive(false);
invalidateControl(i);
}
m_selection.selectNone();
m_mainControlIndex = -1;
}
//----------------------------------------------------------------------------------------------
2021-11-04 03:05:17 +13:00
void PerspectiveTool::deleteSelectedObjects() {
if (m_selection.isEmpty()) return;
std::set<int> selectedObjects = m_selection.getSelectedObjects();
std::set<int>::iterator it;
2021-11-04 03:05:17 +13:00
if (m_preset.getValue() != CUSTOM_WSTR) {
m_preset.setValue(CUSTOM_WSTR);
m_lastPreset = copyPerspectiveSet(m_perspectiveObjs);
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setActive(false);
2021-11-04 03:05:17 +13:00
loadLastPreset();
}
m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this);
std::set<int>::reverse_iterator rit = selectedObjects.rbegin();
for (; rit != selectedObjects.rend(); rit++) {
2021-11-05 01:05:41 +13:00
m_lastPreset.erase(m_lastPreset.begin() + *rit);
m_perspectiveObjs.erase(m_perspectiveObjs.begin() + *rit);
2021-11-04 03:05:17 +13:00
}
m_selection.selectNone();
m_mainControlIndex = -1;
2021-11-04 03:05:17 +13:00
m_undo->setRedoData(m_perspectiveObjs);
TUndoManager::manager()->add(m_undo);
m_undo = 0;
invalidate();
updateToolOptionValues();
2021-11-04 03:05:17 +13:00
}
//----------------------------------------------------------------------------------------------
2021-11-19 06:26:27 +13:00
void PerspectiveTool::selectAllObjects() {
if (!m_perspectiveObjs.size()) return;
for (int i = 0; i < m_perspectiveObjs.size(); i++) {
m_selection.select(i);
m_perspectiveObjs[i]->setActive(true);
}
m_selection.makeCurrent();
invalidate();
updateToolOptionValues();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::setPerspectiveObjects(
std::vector<PerspectiveObject *> objs) {
m_perspectiveObjs = objs;
m_selection.selectNone();
m_lastPreset = copyPerspectiveSet(m_perspectiveObjs);
m_mainControlIndex = -1;
if (m_preset.getValue() != CUSTOM_WSTR) m_preset.setValue(CUSTOM_WSTR);
updateToolOptionValues();
}
//----------------------------------------------------------------------------------------------
2021-11-04 03:05:17 +13:00
std::vector<PerspectiveObject *> PerspectiveTool::copyPerspectiveSet(
std::vector<PerspectiveObject *> perspectiveSet, bool keepStatus) {
2021-11-04 03:05:17 +13:00
std::vector<PerspectiveObject *> copy;
std::vector<PerspectiveObject *>::iterator it;
for (it = perspectiveSet.begin(); it != perspectiveSet.end(); it++) {
PerspectiveObject *currentObject = *it;
PerspectiveObject *newObject;
VanishingPointPerspective *v =
dynamic_cast<VanishingPointPerspective *>(*it);
LinePerspective *l = dynamic_cast<LinePerspective *>(*it);
if (v)
newObject = new VanishingPointPerspective();
else if (l)
newObject = new LinePerspective();
else
continue;
newObject->setCenterPoint(currentObject->getCenterPoint());
newObject->setRotationPos(currentObject->getRotationPos());
newObject->setSpacingPos(currentObject->getSpacingPos());
newObject->setLeftPivotPos(currentObject->getLeftPivotPos());
newObject->setLeftHandlePos(currentObject->getLeftHandlePos());
newObject->setRightPivotPos(currentObject->getRightPivotPos());
newObject->setRightHandlePos(currentObject->getRightHandlePos());
newObject->setRotation(currentObject->getRotation());
newObject->setSpacing(currentObject->getSpacing());
newObject->setOpacity(currentObject->getOpacity());
newObject->setColor(currentObject->getColor());
newObject->setHorizon(currentObject->isHorizon());
newObject->setParallel(currentObject->isParallel());
newObject->setShowAdvancedControls(PerspectiveToolAdvancedControls);
if (keepStatus) newObject->setActive(currentObject->isActive());
2021-11-04 03:05:17 +13:00
copy.push_back(newObject);
}
return copy;
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::initPresets() {
if (!m_presetsManager.isLoaded()) {
TFilePath projectFolder =
TProjectManager::instance()->getCurrentProject()->getProjectFolder();
m_presetsManager.loadPresets(projectFolder + "grids");
2021-11-10 01:03:51 +13:00
m_presetsManager.loadPresets(ToonzFolder::getLibraryFolder() +
"perspective grids");
m_presetsManager.setIsLoaded(true);
2021-11-04 03:05:17 +13:00
}
// Rebuild the presets property entries
const std::set<PerspectivePreset> &presets = m_presetsManager.presets();
m_preset.deleteAllValues();
m_preset.addValue(CUSTOM_WSTR);
m_preset.setItemUIName(CUSTOM_WSTR, tr("<custom>"));
std::set<PerspectivePreset>::const_iterator it, end = presets.end();
for (it = presets.begin(); it != end; ++it)
m_preset.addValue(it->m_presetName);
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::loadPreset() {
const std::set<PerspectivePreset> &presets = m_presetsManager.presets();
std::set<PerspectivePreset>::const_iterator it;
PerspectivePreset preset;
for (it = presets.begin(); it != presets.end(); it++) {
preset = *it;
if (preset.m_presetName == m_preset.getValue()) break;
}
if (it == presets.end()) return;
m_perspectiveObjs = preset.m_perspectiveSet;
onPropertyChanged(m_advancedControls.getName());
2021-11-04 03:05:17 +13:00
invalidate();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::addPreset(QString name, bool isLibrary) {
TFilePath projectFolder =
TProjectManager::instance()->getCurrentProject()->getProjectFolder();
TFilePath path =
(isLibrary ? (ToonzFolder::getLibraryFolder() + "perspective grids")
: (projectFolder + "grids")) +
TFilePath(name);
path = path.withType("grid");
PerspectivePreset preset(name.toStdWString(), path);
2021-11-04 03:05:17 +13:00
preset.m_perspectiveSet = m_perspectiveObjs;
// Pass the preset to the manager
m_presetsManager.addPreset(preset);
// Reinitialize the associated preset enum
initPresets();
// Set the value to the specified one
m_preset.setValue(preset.m_presetName);
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::removePreset() {
std::wstring name(m_preset.getValue());
if (name == CUSTOM_WSTR) return;
m_presetsManager.removePreset(name);
initPresets();
std::set<int> selectedObjects = m_selection.getSelectedObjects();
std::set<int>::iterator it;
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++)
m_perspectiveObjs[*it]->setActive(false);
m_selection.selectNone();
2021-11-04 03:05:17 +13:00
// No parameter change, and set the preset value to custom
m_preset.setValue(CUSTOM_WSTR);
loadLastPreset();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::loadLastPreset() {
m_perspectiveObjs = m_lastPreset;
onPropertyChanged(m_advancedControls.getName());
2021-11-04 03:05:17 +13:00
invalidate();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::updateSpacing(double space) {
if (m_selection.isEmpty()) return;
m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this);
std::set<int> selectedObjects = m_selection.getSelectedObjects();
std::set<int>::iterator it;
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++) {
PerspectiveObject *obj = m_perspectiveObjs[*it];
obj->setSpacing(space);
// Move Spacing Control
TPointD newSpacingPos = calculateSpacingPos(
obj->getType(), space, obj->getRotation(), obj->getSpacingPos(),
obj->getRotationPos(), obj->getCenterPoint());
obj->setSpacingPos(newSpacingPos);
}
m_undo->setRedoData(m_perspectiveObjs);
TUndoManager::manager()->add(m_undo);
m_undo = 0;
invalidate();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::updateRotation(double rotation) {
if (m_selection.isEmpty()) return;
m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this);
std::set<int> selectedObjects = m_selection.getSelectedObjects();
std::set<int>::iterator it;
for (it = selectedObjects.begin(); it != selectedObjects.end(); it++) {
PerspectiveObject *obj = m_perspectiveObjs[*it];
TPointD centerPoint = obj->getCenterPoint();
TPointD oldRotationPos = obj->getRotationPos();
double oldRotation = obj->getRotation();
obj->setRotation(rotation);
// Move Rotation Control
double dx = oldRotationPos.x - centerPoint.x;
double dy = oldRotationPos.y - centerPoint.y;
double distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
double controlAngle = rotation;
double oldAngle = oldRotation;
if (obj->getType() == PerspectiveType::VanishingPoint) {
controlAngle += 270;
oldAngle += 270;
}
double deltaAngle = controlAngle - oldAngle;
TPointD newRotationPos =
calculatePointOnLine(centerPoint, controlAngle, distance);
obj->setRotationPos(newRotationPos);
// Also move Spacing Control to maintain current spacing
TPointD oldSpacingPos = obj->getSpacingPos();
dx = oldSpacingPos.x - centerPoint.x;
dy = oldSpacingPos.y - centerPoint.y;
distance = std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
double space = obj->getSpacing();
controlAngle = std::atan2(dy, dx) / (3.14159 / 180);
controlAngle += deltaAngle;
TPointD newSpacingPos =
calculatePointOnLine(centerPoint, controlAngle, distance);
obj->setSpacingPos(newSpacingPos);
}
m_undo->setRedoData(m_perspectiveObjs);
TUndoManager::manager()->add(m_undo);
m_undo = 0;
invalidate();
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::updateMeasuredValueToolOptions() {
double spacing = 0, rotation = 0;
if (m_mainControlIndex != -1 &&
m_mainControlIndex < m_perspectiveObjs.size()) {
PerspectiveObject *obj = m_perspectiveObjs[m_mainControlIndex];
if (obj) {
spacing = obj->getSpacing();
rotation = obj->getRotation();
}
}
for (int i = 0; i < (int)m_toolOptionsBox.size(); i++)
m_toolOptionsBox[i]->updateMeasuredValues(spacing, rotation);
}
//----------------------------------------------------------------------------------------------
void PerspectiveTool::updateToolOptionValues() {
if (m_mainControlIndex != -1 &&
m_mainControlIndex < m_perspectiveObjs.size()) {
PerspectiveObject *obj = m_perspectiveObjs[m_mainControlIndex];
if (obj) {
m_opacity.setValue(obj->getOpacity());
m_color.setColor(obj->getColor());
m_horizon.setValue(obj->isHorizon());
m_parallel.setValue(obj->isParallel());
}
}
updateMeasuredValueToolOptions();
m_propertyUpdating = true;
getApplication()->getCurrentTool()->notifyToolChanged();
m_propertyUpdating = false;
}
//----------------------------------------------------------------------------------------------
2021-11-29 03:33:00 +13:00
void PerspectiveTool::saveTool() {
PerspectivePreset customPreset(CUSTOM_WSTR);
if (m_preset.getValue() != CUSTOM_WSTR)
customPreset.m_perspectiveSet = m_lastPreset;
else
customPreset.m_perspectiveSet = m_perspectiveObjs;
m_presetsManager.setCustomPreset(customPreset);
m_presetsManager.savePreset(CUSTOM_WSTR);
}
void PerspectiveTool::loadTool() {
2021-12-01 15:25:22 +13:00
onActivate();
2021-11-29 03:33:00 +13:00
m_presetsManager.loadPresets(
ToonzFolder::getMyModuleDir()); // Load custom grid from last session
PerspectivePreset preset = m_presetsManager.getCustomPreset();
m_lastPreset = preset.m_perspectiveSet;
loadLastPreset();
}
//----------------------------------------------------------------------------------------------
2021-11-04 03:05:17 +13:00
void VanishingPointPerspective::draw(SceneViewer *viewer, TRectD cameraRect) {
TRectD rect = cameraRect;
int x1, x2, y1, y2;
viewer->rect().getCoords(&x1, &y1, &x2, &y2);
TRect clipRect = TRect(x1, y1, x2 + 1, y2 + 1);
GLfloat modelView[16];
glGetFloatv(GL_MODELVIEW_MATRIX, modelView);
TAffine modelViewAff(modelView[0], modelView[4], modelView[12], modelView[1],
modelView[5], modelView[13]);
TPointD clipCorner[] = {
modelViewAff.inv() * TPointD(clipRect.x0, clipRect.y0),
modelViewAff.inv() * TPointD(clipRect.x1, clipRect.y0),
modelViewAff.inv() * TPointD(clipRect.x1, clipRect.y1),
modelViewAff.inv() * TPointD(clipRect.x0, clipRect.y1)};
TRectD bounds;
bounds.x0 = bounds.x1 = clipCorner[0].x;
bounds.y0 = bounds.y1 = clipCorner[0].y;
int i;
for (i = 1; i < 4; i++) {
const TPointD &p = clipCorner[i];
if (p.x < bounds.x0)
bounds.x0 = p.x;
else if (p.x > bounds.x1)
bounds.x1 = p.x;
if (p.y < bounds.y0)
bounds.y0 = p.y;
else if (p.y > bounds.y1)
bounds.y1 = p.y;
}
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2021-11-08 03:44:55 +13:00
glLineWidth(1.0f);
2021-11-04 03:05:17 +13:00
TPointD p = getCenterPoint();
TPixel32 color = getColor();
color.m = ((getOpacity() / 100.0) * 255);
tglColor(color);
TPointD end;
double distanceToLeft = std::abs(p.x - bounds.x0);
double distanceToRight = std::abs(p.x - bounds.x1);
double distanceToTop = std::abs(p.y - bounds.y1);
double distanceToBottom = std::abs(p.y - bounds.y0);
double xDistance = std::max(distanceToLeft, distanceToRight);
double yDistance = std::max(distanceToTop, distanceToBottom);
2021-11-08 03:44:55 +13:00
double distance = std::max(xDistance, yDistance);
2021-11-04 03:05:17 +13:00
double totalDistance =
2021-11-08 03:44:55 +13:00
std::sqrt(std::pow(distance, 2) + std::pow(distance, 2));
2021-11-04 03:05:17 +13:00
double step = getSpacing();
2021-11-17 17:51:14 +13:00
step = std::min(step, getMaxSpacing());
step = std::max(step, getMinSpacing());
2021-11-08 03:44:55 +13:00
int rays = isHorizon() ? totalDistance : (360 / step);
2021-11-05 11:09:50 +13:00
if (rays == 0) rays = 1;
2021-11-04 03:05:17 +13:00
if (!isHorizon()) step = 360.0 / (double)rays;
double startAngle = 270.0 + getRotation();
double endAngle = (isHorizon() ? 180.0 : 90.0) + getRotation();
2021-11-08 03:44:55 +13:00
double yLength, xLength;
2021-11-04 03:05:17 +13:00
int raySpacing = 0;
if (isHorizon()) {
2021-11-08 03:44:55 +13:00
// Draw horizon line
yLength = std::sin(getRotation() * (3.14159 / 180)) * totalDistance;
xLength = std::cos(getRotation() * (3.14159 / 180)) * totalDistance;
end.x = p.x + xLength;
end.y = p.y + yLength;
tglDrawSegment(p, end);
yLength =
std::sin((180.0 + getRotation()) * (3.14159 / 180)) * totalDistance;
xLength =
std::cos((180.0 + getRotation()) * (3.14159 / 180)) * totalDistance;
end.x = p.x + xLength;
end.y = p.y + yLength;
tglDrawSegment(p, end);
2021-11-05 11:09:50 +13:00
double sinAngle = std::sin((270 + step) * (3.14159 / 180));
double cosAngle = std::cos((270 + step) * (3.14159 / 180));
if (cosAngle == 0) cosAngle = 0.001;
double slope = sinAngle / cosAngle;
if (slope == 0) slope = 0.001;
2021-11-04 03:05:17 +13:00
raySpacing = std::abs((int)((totalDistance - p.y) / slope));
if (!raySpacing) raySpacing = 1;
}
double lAngle = startAngle;
double rAngle = startAngle;
2021-11-08 03:44:55 +13:00
for (i = 1; i <= rays; i++) {
2021-11-04 03:05:17 +13:00
bool drawRight = true;
2021-11-08 03:44:55 +13:00
TPointD lStartPoint = p, rStartPoint = p, lowerCenterPoint = p,
lowerRefPoint = p;
2021-11-04 03:05:17 +13:00
if (isHorizon()) {
double angle =
std::atan(-totalDistance / (double)(raySpacing * (i - 1))) /
(3.14159 / 180);
rAngle = getRotation() + 360 + angle;
lAngle = getRotation() + 180 - angle;
2021-11-08 03:44:55 +13:00
if (step < 60) {
double dlower = (60 - step);
lowerCenterPoint.y = p.y + ::sin(startAngle * (3.14159 / 180)) * dlower;
lowerCenterPoint.x = p.x + ::cos(startAngle * (3.14159 / 180)) * dlower;
yLength = std::sin(getRotation() * (3.14159 / 180)) * dlower;
xLength = std::cos(getRotation() * (3.14159 / 180)) * dlower;
lowerRefPoint.x = lowerCenterPoint.x + xLength;
lowerRefPoint.y = lowerCenterPoint.y + yLength;
}
} else {
2021-11-04 03:05:17 +13:00
// No need to draw right side if left side already drawn
int e = endAngle * 1000;
int l = lAngle * 1000.0;
int r = (rAngle - 360.0) * 1000.0;
2021-11-08 03:44:55 +13:00
if (i != 1) {
if (l == e || r == e || l == r)
drawRight = false;
else if (l < e)
break;
}
2021-11-04 03:05:17 +13:00
}
// Draw left of startAngle
yLength = std::sin(lAngle * (3.14159 / 180)) * totalDistance;
xLength = std::cos(lAngle * (3.14159 / 180)) * totalDistance;
end.x = p.x + xLength;
end.y = p.y + yLength;
2021-11-08 03:44:55 +13:00
if (lowerCenterPoint != p)
lStartPoint =
calculateCenterPoint(end, p, lowerCenterPoint, lowerRefPoint);
tglDrawSegment(lStartPoint, end);
if (i > 1) {
// When overlaps, no need to draw right side if left side already drawn
if (!drawRight) break;
// Draw right of startAngle
yLength = std::sin(rAngle * (3.14159 / 180)) * totalDistance;
xLength = std::cos(rAngle * (3.14159 / 180)) * totalDistance;
end.x = rStartPoint.x + xLength;
end.y = rStartPoint.y + yLength;
if (lowerCenterPoint != p)
rStartPoint =
calculateCenterPoint(end, p, lowerCenterPoint, lowerRefPoint);
tglDrawSegment(rStartPoint, end);
}
2021-11-04 03:05:17 +13:00
2021-11-08 03:44:55 +13:00
rAngle += step;
lAngle -= step;
2021-11-04 03:05:17 +13:00
}
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
}
//----------------------------------------------------------------------------------------------
void LinePerspective::draw(SceneViewer *viewer, TRectD cameraRect) {
TRectD rect = cameraRect;
int x1, x2, y1, y2;
viewer->rect().getCoords(&x1, &y1, &x2, &y2);
TRect clipRect = TRect(x1, y1, x2 + 1, y2 + 1);
GLfloat modelView[16];
glGetFloatv(GL_MODELVIEW_MATRIX, modelView);
TAffine modelViewAff(modelView[0], modelView[4], modelView[12], modelView[1],
modelView[5], modelView[13]);
TPointD clipCorner[] = {
modelViewAff.inv() * TPointD(clipRect.x0, clipRect.y0),
modelViewAff.inv() * TPointD(clipRect.x1, clipRect.y0),
modelViewAff.inv() * TPointD(clipRect.x1, clipRect.y1),
modelViewAff.inv() * TPointD(clipRect.x0, clipRect.y1)};
TRectD bounds;
bounds.x0 = bounds.x1 = clipCorner[0].x;
bounds.y0 = bounds.y1 = clipCorner[0].y;
int i;
for (i = 1; i < 4; i++) {
const TPointD &p = clipCorner[i];
if (p.x < bounds.x0)
bounds.x0 = p.x;
else if (p.x > bounds.x1)
bounds.x1 = p.x;
if (p.y < bounds.y0)
bounds.y0 = p.y;
else if (p.y > bounds.y1)
bounds.y1 = p.y;
}
glEnable(GL_BLEND); // Enable blending.
glEnable(GL_LINE_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth(1.0f);
TPointD p = getCenterPoint();
TPixel32 color = getColor();
color.m = ((getOpacity() / 100.0) * 255);
tglColor(color);
TPointD start, mid, end;
double distanceToLeft = std::abs(p.x - bounds.x0);
double distanceToRight = std::abs(p.x - bounds.x1);
double distanceToTop = std::abs(p.y - bounds.y1);
double distanceToBottom = std::abs(p.y - bounds.y0);
double xDistance = std::max(distanceToLeft, distanceToRight);
double yDistance = std::max(distanceToTop, distanceToBottom);
2021-11-08 03:44:55 +13:00
double distance = std::max(xDistance, yDistance);
2021-11-04 03:05:17 +13:00
double totalDistance =
2021-11-08 03:44:55 +13:00
std::sqrt(std::pow(distance, 2) + std::pow(distance, 2));
2021-11-04 03:05:17 +13:00
double theta = getRotation() * (3.14159 / 180);
double yLength = std::sin(theta) * totalDistance;
double xLength = std::cos(theta) * totalDistance;
// Draw line at control Point
mid = p;
start.x = mid.x - xLength;
start.y = mid.y - yLength;
end.x = mid.x + xLength;
end.y = mid.y + yLength;
tglDrawSegment(start, end);
// Draw lines above/below control point
if (isParallel()) {
2021-11-17 17:51:14 +13:00
TPointD above = p;
TPointD below = p;
double step = getSpacing();
step = std::min(step, getMaxSpacing());
step = std::max(step, getMinSpacing());
double distanceFromCP = step;
2021-11-04 03:05:17 +13:00
2021-11-17 17:51:14 +13:00
double stepspeed = isHorizon() ? 1.5 : 1.0;
2021-11-04 03:05:17 +13:00
2021-11-17 17:51:14 +13:00
while (distanceFromCP <= totalDistance) {
2021-11-04 03:05:17 +13:00
if (!isHorizon()) {
above.y = p.y + (std::cos(-theta) * distanceFromCP);
above.x = p.x + (std::sin(-theta) * distanceFromCP);
start.x = above.x - xLength;
start.y = above.y - yLength;
end.x = above.x + xLength;
end.y = above.y + yLength;
tglDrawSegment(start, end);
}
below.y = p.y - (std::cos(-theta) * distanceFromCP);
below.x = p.x - (std::sin(-theta) * distanceFromCP);
start.x = below.x - xLength;
start.y = below.y - yLength;
end.x = below.x + xLength;
end.y = below.y + yLength;
tglDrawSegment(start, end);
2021-11-17 17:51:14 +13:00
distanceFromCP += (step * stepspeed);
step = step * stepspeed;
2021-11-04 03:05:17 +13:00
}
}
glLineWidth(1.0f);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
}
TPointD LinePerspective::getReferencePoint(TPointD firstPoint) {
TPointD refPoint;
double theta = getRotation() * (3.14159 / 180);
double yLength = std::sin(theta) * 100;
double xLength = std::cos(theta) * 100;
refPoint.x = firstPoint.x + xLength;
refPoint.y = firstPoint.y + yLength;
return refPoint;
}
2021-12-01 15:25:22 +13:00
PerspectiveTool perspectiveTool;