#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 "toonzqt/selectioncommandids.h" #include "../toonz/sceneviewer.h" #include "../toonz/tscenehandle.h" #include "../toonz/toonzscene.h" #include "../toonz/tcamera.h" #include #include PerspectiveTool perspectiveTool; //---------------------------------------------------------------------------------------------------------- #define CUSTOM_WSTR L"" //---------------------------------------------------------------------------------------------------------- void PerspectivePreset::saveData(TOStream &os) { os.openChild("Name"); os << m_presetName; os.closeChild(); std::vector::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; while (is.matchTag(tagName)) { if (tagName == "Name") 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 objs, PerspectiveTool *tool) : m_tool(tool) { if (!m_tool) return; m_undoData = m_tool->copyPerspectiveSet(objs); } //---------------------------------------------------------------------------------------------------------- void PerspectiveObjectUndo::setRedoData(std::vector objs) { if (!m_tool) return; m_redoData = m_tool->copyPerspectiveSet(objs); } //---------------------------------------------------------------------------------------------------------- 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); save(); } //---------------------------------------------------------------------------------------------------------- void PerspectivePresetManager::removePreset(const std::wstring &name) { std::set::iterator it; for (it = m_presets.begin(); it != m_presets.end(); it++) { PerspectivePreset preset = *it; if (preset.m_presetName == name) m_presets.erase(preset); } save(); } //---------------------------------------------------------------------------------------------------------- void PerspectivePresetManager::load(const TFilePath &fp) { m_fp = fp; std::string tagName; TIStream is(m_fp); try { while (is.matchTag(tagName)) { if (tagName == "version") { VersionNumber version; is >> version.first >> version.second; is.setVersion(version); is.matchEndTag(); } else if (tagName == "presets") { while (is.matchTag(tagName)) { if (tagName == "preset") { PerspectivePreset data; is >> data, m_presets.insert(data); is.matchEndTag(); } else is.skipCurrentTag(); } is.matchEndTag(); } else is.skipCurrentTag(); } } catch (...) { } } //---------------------------------------------------------------------------------------------------------- void PerspectivePresetManager::save() { TSystem::touchParentDir(m_fp); TOStream os(m_fp); os.openChild("version"); os << 1 << 0; os.closeChild(); os.openChild("presets"); std::set::iterator it, end = m_presets.end(); for (it = m_presets.begin(); it != end; ++it) { os.openChild("preset"); os << (TPersist &)*it; os.closeChild(); } os.closeChild(); } //---------------------------------------------------------------------------------------------------------- void PerspectiveControls::drawControls() { if (!m_perspective) return; TPointD centerPoint = m_perspective->getCenterPoint(); // Draw rotation control glColor3d(0.5, 0.5, 0.5); tglDrawSegment(centerPoint, m_rotationPos); tglDrawCircle(m_rotationPos, m_handleRadius); if (m_active) glColor3d(1.0, 1.0, 0.0); tglDrawDisk(m_rotationPos, m_handleRadius - 2); // Draw spacing control if (m_perspective->getType() != PerspectiveType::Line || m_perspective->isParallel()) { glColor3d(0.5, 0.5, 0.5); tglDrawSegment(centerPoint, m_spacingPos); tglDrawCircle(m_spacingPos, m_handleRadius); if (m_active) glColor3d(1.0, 1.0, 0.0); tglDrawDisk(m_spacingPos, m_handleRadius - 2); } if (m_perspective->getType() == PerspectiveType::VanishingPoint) { glColor3d(0.5, 0.5, 0.5); tglDrawSegment(m_leftPivotPos, m_leftHandlePos); tglDrawCircle(m_leftHandlePos, m_handleRadius); if (m_active) glColor3d(1.0, 1.0, 0.0); tglDrawDisk(m_leftHandlePos, m_handleRadius - 2); glColor3d(0.5, 0.5, 0.5); tglDrawSegment(centerPoint, m_leftPivotPos); tglDrawCircle(m_leftPivotPos, m_handleRadius); if (m_active) glColor3d(1.0, 1.0, 0.0); tglDrawDisk(m_leftPivotPos, m_handleRadius - 2); glColor3d(0.5, 0.5, 0.5); tglDrawSegment(m_rightPivotPos, m_rightHandlePos); tglDrawCircle(m_rightHandlePos, m_handleRadius); if (m_active) glColor3d(1.0, 1.0, 0.0); tglDrawDisk(m_rightHandlePos, m_handleRadius - 2); glColor3d(0.5, 0.5, 0.5); tglDrawSegment(centerPoint, m_rightPivotPos); tglDrawCircle(m_rightPivotPos, m_handleRadius); if (m_active) glColor3d(1.0, 1.0, 0.0); tglDrawDisk(m_rightPivotPos, m_handleRadius - 2); } // Draw center/move control glColor3d(0.5, 0.5, 0.5); tglDrawCircle(centerPoint, m_handleRadius); if (m_active) glColor3d(0.0, 1.0, 0.0); tglDrawDisk(centerPoint, m_handleRadius - 2); } //---------------------------------------------------------------------------------------------------------- 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); } //---------------------------------------------------------------------------------------------- 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_preset("Preset:") , m_presetsLoaded(false) , 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()) , 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_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"); m_preset.setId("PerspectivePreset"); m_preset.addValue(CUSTOM_WSTR); m_selection.setTool(this); m_selection.selectNone(); } //---------------------------------------------------------------------------------------------- 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_preset.setQStringName(tr("Preset:")); m_preset.setItemUIName(CUSTOM_WSTR, tr("")); } //---------------------------------------------------------------------------------------------- TPropertyGroup *PerspectiveTool::getProperties(int idx) { if (!m_presetsLoaded) initPresets(); return &m_prop; } //---------------------------------------------------------------------------------------------- bool PerspectiveTool::onPropertyChanged(std::string propertyName) { if (m_propertyUpdating) return true; if (propertyName == m_preset.getName()) { if (m_preset.getValue() != CUSTOM_WSTR) loadPreset(); else // Chose , go back to last preset loadLastPreset(); m_propertyUpdating = true; getApplication()->getCurrentTool()->notifyToolChanged(); m_propertyUpdating = false; return true; } std::set selectedObjects = m_selection.getSelectedObjects(); std::set::iterator it; if (propertyName != m_type.getName() && selectedObjects.size() && m_preset.getValue() != CUSTOM_WSTR) { m_preset.setValue(CUSTOM_WSTR); m_lastPreset = copyPerspectiveSet(m_perspectiveObjs); loadLastPreset(); m_propertyUpdating = true; getApplication()->getCurrentTool()->notifyToolChanged(); m_propertyUpdating = false; } 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; m_firstPos = pos; int controlIdx = -1; for (int i = 0; i < m_perspectiveObjs.size(); i++) if (m_perspectiveObjs[i]->isOverControl()) { controlIdx = i; break; } 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; 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()) return; // 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 m_opacity.setValue(m_perspectiveObjs[controlIdx]->getOpacity()); m_horizon.setValue(m_perspectiveObjs[controlIdx]->isHorizon()); m_color.setColor(m_perspectiveObjs[controlIdx]->getColor()); m_propertyUpdating = true; getApplication()->getCurrentTool()->notifyToolChanged(); m_propertyUpdating = false; 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; return; } } if (m_preset.getValue() != CUSTOM_WSTR) { m_preset.setValue(CUSTOM_WSTR); m_lastPreset = copyPerspectiveSet(m_perspectiveObjs); loadLastPreset(); m_propertyUpdating = true; getApplication()->getCurrentTool()->notifyToolChanged(); m_propertyUpdating = false; } 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->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; 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; if (dratio == 0) dratio = 0.001; 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)); if (denom == 0) denom = 0.001; newCenterPoint.x = xNum / denom; newCenterPoint.y = yNum / denom; return newCenterPoint; } void PerspectiveTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { if (!m_perspectiveObjs.size()) return; if (m_selecting) { m_selectingRect.x1 = pos.x; m_selectingRect.y1 = pos.y; 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::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; if (m_preset.getValue() != CUSTOM_WSTR) { m_preset.setValue(CUSTOM_WSTR); m_lastPreset = copyPerspectiveSet(m_perspectiveObjs); loadLastPreset(); m_propertyUpdating = true; getApplication()->getCurrentTool()->notifyToolChanged(); m_propertyUpdating = false; } if (!m_undo) m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this); bool applyToSelection = true; PerspectiveObject *mainObj = m_perspectiveObjs[m_mainControlIndex]; TPointD centerPoint = mainObj->getCenterPoint(); TPointD dPos = pos - m_firstPos; double dAngle = 0.0; double dSpace = 0.0; if (m_isRotating) { TPointD a = m_firstPos - centerPoint; TPointD b = pos - centerPoint; double a2 = norm2(a), b2 = norm2(b); const double eps = 1e-8; if (a2 < eps || b2 < eps) return; dAngle = asin(cross(a, b) / sqrt(a2 * b2)) * M_180_PI; } else if (m_isSpacing) { double da = std::sqrt(std::pow(centerPoint.x - m_firstPos.x, 2) + std::pow(centerPoint.y - m_firstPos.y, 2)); double db = std::sqrt(std::pow(centerPoint.x - pos.x, 2) + std::pow(centerPoint.y - pos.y, 2)); dSpace = da - db; } 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_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_isLeftMoving) { TPointD newPos = leftHandlePos + dPos; mainObj->setLeftHandlePos(newPos); // Also move Center Point centerPoint = calculateCenterPoint(newPos, leftPivotPos, rightHandlePos, rightPivotPos); mainObj->setCenterPoint(centerPoint); // Check Right Handle rightHandlePos = calculateHandlePos(rightHandlePos, rightPivotPos, TPointD(0.0, 0.0), centerPoint); mainObj->setRightHandlePos(rightHandlePos); 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); applyToSelection = false; } else if (m_isRightMoving) { TPointD newPos = rightHandlePos + dPos; mainObj->setRightHandlePos(newPos); // Also move Center Point centerPoint = calculateCenterPoint(leftHandlePos, leftPivotPos, newPos, rightPivotPos); mainObj->setCenterPoint(centerPoint); // Check Left Handle leftHandlePos = calculateHandlePos(leftHandlePos, leftPivotPos, TPointD(0.0, 0.0), centerPoint); mainObj->setLeftHandlePos(leftHandlePos); applyToSelection = false; } } if (applyToSelection) { std::set selectedObjects = m_selection.getSelectedObjects(); std::set::iterator it; for (it = selectedObjects.begin(); it != selectedObjects.end(); it++) { PerspectiveObject *obj = m_perspectiveObjs[*it]; if (m_isShifting) obj->shiftPerspectiveObject(dPos); else if (m_isCenterMoving) { // Moving TPointD objCenterPoint = obj->getCenterPoint(); obj->setCenterPoint(objCenterPoint + dPos); // 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(); obj->setRotation(rotation + dAngle); TPointD rotationPos = obj->getRotationPos(); obj->setRotationPos(rotationPos + dPos); } else if (m_isSpacing) { double spacing = obj->getSpacing(); obj->setSpacing(spacing + dSpace); TPointD spacingPos = obj->getSpacingPos(); obj->setSpacingPos(spacingPos + dPos); } } } m_modified = true; m_firstPos = pos; 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; } //---------------------------------------------------------------------------------------------- void PerspectiveTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { m_mousePos = pos; if (!m_perspectiveObjs.size()) return; for (int i = 0; i < m_perspectiveObjs.size(); i++) m_perspectiveObjs[i]->setCursorPos(pos); } //---------------------------------------------------------------------------------------------- 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::deleteSelectedObjects() { if (m_selection.isEmpty()) return; if (m_preset.getValue() != CUSTOM_WSTR) { m_preset.setValue(CUSTOM_WSTR); m_lastPreset = copyPerspectiveSet(m_perspectiveObjs); loadLastPreset(); m_propertyUpdating = true; getApplication()->getCurrentTool()->notifyToolChanged(); m_propertyUpdating = false; } m_undo = new PerspectiveObjectUndo(m_perspectiveObjs, this); std::set selectedObjects = m_selection.getSelectedObjects(); std::set::reverse_iterator rit = selectedObjects.rbegin(); for (; rit != selectedObjects.rend(); rit++) { m_lastPreset.erase(m_lastPreset.begin() + *rit); m_perspectiveObjs.erase(m_perspectiveObjs.begin() + *rit); } m_selection.selectNone(); m_undo->setRedoData(m_perspectiveObjs); TUndoManager::manager()->add(m_undo); m_undo = 0; invalidate(); } //---------------------------------------------------------------------------------------------- std::vector PerspectiveTool::copyPerspectiveSet( std::vector perspectiveSet) { std::vector copy; std::vector::iterator it; for (it = perspectiveSet.begin(); it != perspectiveSet.end(); it++) { PerspectiveObject *currentObject = *it; PerspectiveObject *newObject; VanishingPointPerspective *v = dynamic_cast(*it); LinePerspective *l = dynamic_cast(*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()); copy.push_back(newObject); } return copy; } //---------------------------------------------------------------------------------------------- void PerspectiveTool::initPresets() { if (!m_presetsLoaded) { // If necessary, load the presets from file m_presetsLoaded = true; m_presetsManager.load(ToonzFolder::getMyModuleDir() + "perspective_grids.txt"); } // Rebuild the presets property entries const std::set &presets = m_presetsManager.presets(); m_preset.deleteAllValues(); m_preset.addValue(CUSTOM_WSTR); m_preset.setItemUIName(CUSTOM_WSTR, tr("")); std::set::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 &presets = m_presetsManager.presets(); std::set::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; invalidate(); } //---------------------------------------------------------------------------------------------- void PerspectiveTool::addPreset(QString name) { PerspectivePreset preset(name.toStdWString()); // , m_perspectiveObjs); 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(); // No parameter change, and set the preset value to custom m_preset.setValue(CUSTOM_WSTR); loadLastPreset(); } //---------------------------------------------------------------------------------------------- void PerspectiveTool::loadLastPreset() { m_perspectiveObjs = m_lastPreset; invalidate(); } //---------------------------------------------------------------------------------------------- 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); 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); double totalDistance = std::sqrt(std::pow(xDistance, 2) + std::pow(yDistance, 2)); double step = getSpacing(); int rays = isHorizon() ? 360 : (360 / step); if (rays == 0) rays = 1; if (!isHorizon()) step = 360.0 / (double)rays; double startAngle = 270.0 + getRotation(); double endAngle = (isHorizon() ? 180.0 : 90.0) + getRotation(); // Draw starting angle double yLength = std::sin(startAngle * (3.14159 / 180)) * totalDistance; double xLength = std::cos(startAngle * (3.14159 / 180)) * totalDistance; end.x = p.x + xLength; end.y = p.y + yLength; tglDrawSegment(p, end); int raySpacing = 0; if (isHorizon()) { 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; raySpacing = std::abs((int)((totalDistance - p.y) / slope)); if (!raySpacing) raySpacing = 1; } double lAngle = startAngle; double rAngle = startAngle; for (i = 2; i <= rays; i++) { bool drawRight = true; if (isHorizon()) { double angle = std::atan(-totalDistance / (double)(raySpacing * (i - 1))) / (3.14159 / 180); rAngle = getRotation() + 360 + angle; lAngle = getRotation() + 180 - angle; } else { rAngle += step; lAngle -= step; // 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; if (l == e || r == e || l == r) drawRight = false; else if (l < e) break; } // 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; tglDrawSegment(p, end); // 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 = p.x + xLength; end.y = p.y + yLength; tglDrawSegment(p, end); } 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); double totalDistance = std::sqrt(std::pow(xDistance, 2) + std::pow(yDistance, 2)); 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()) { TPointD above = p; TPointD below = p; double distance = 0.0; double step = getSpacing(); double stepspeed = isHorizon() ? 1.5 : 1.0; double distanceFromCP = 0.0; while (std::abs(distanceFromCP) <= totalDistance) { distance += (step * stepspeed); step = step * stepspeed; distanceFromCP = distance / Stage::standardDpi * Stage::inch; 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); } } 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; }