diff --git a/toonz/sources/include/tools/tooloptions.h b/toonz/sources/include/tools/tooloptions.h index c14981c..66fe951 100644 --- a/toonz/sources/include/tools/tooloptions.h +++ b/toonz/sources/include/tools/tooloptions.h @@ -373,6 +373,9 @@ class GeometricToolOptionsBox final : public ToolOptionsBox { ToolOptionCombo *m_shapeField; ToolOptionCheckbox *m_pencilMode; ToolOptionIntSlider *m_miterField; + ToolOptionCheckbox *m_snapCheckbox; + ToolOptionCombo *m_snapSensitivityCombo; + TTool *m_tool; public: GeometricToolOptionsBox(QWidget *parent, TTool *tool, diff --git a/toonz/sources/tnztools/geometrictool.cpp b/toonz/sources/tnztools/geometrictool.cpp index 081dc91..bef51c0 100644 --- a/toonz/sources/tnztools/geometrictool.cpp +++ b/toonz/sources/tnztools/geometrictool.cpp @@ -51,6 +51,8 @@ TEnv::DoubleVar GeometricOpacity("InknpaintGeometricOpacity", 100); TEnv::IntVar GeometricCapStyle("InknpaintGeometricCapStyle", 0); TEnv::IntVar GeometricJoinStyle("InknpaintGeometricJoinStyle", 0); TEnv::IntVar GeometricMiterValue("InknpaintGeometricMiterValue", 4); +TEnv::IntVar GeometricSnap("InknpaintGeometricSnap", 0); +TEnv::IntVar GeometricSnapSensitivity("InknpaintGeometricSnapSensitivity", 0); //------------------------------------------------------------------- @@ -60,6 +62,13 @@ TEnv::IntVar GeometricMiterValue("InknpaintGeometricMiterValue", 4); #define ROUNDJ_WSTR L"round_join" #define BEVEL_WSTR L"bevel_join" #define MITER_WSTR L"miter_join" +#define LOW_WSTR L"Low" +#define MEDIUM_WSTR L"Medium" +#define HIGH_WSTR L"High" + +const double SNAPPING_LOW = 5.0; +const double SNAPPING_MEDIUM = 25.0; +const double SNAPPING_HIGH = 100.0; //============================================================================= // Utility Functions @@ -302,11 +311,18 @@ public: TEnumProperty m_capStyle; TEnumProperty m_joinStyle; TIntProperty m_miterJoinLimit; - + TBoolProperty m_snap; + TEnumProperty m_snapSensitivity; TPropertyGroup m_prop[2]; int m_targetType; + // for snapping + int m_strokeIndex1; + double m_w1, m_pixelSize, m_currThickness, m_minDistance2; + bool m_foundSnap = false; + TPointD m_snapPoint; + PrimitiveParam(int targetType) : m_type("Shape:") // "W_ToolOptions_ShapeType") , m_toolSize("Size:", 0, 100, @@ -322,6 +338,8 @@ public: , m_capStyle("Cap") , m_joinStyle("Join") , m_miterJoinLimit("Miter:", 0, 100, 4) + , m_snap("Snap", false) + , m_snapSensitivity("Sensitivity:") , m_targetType(targetType) { if (targetType & TTool::Vectors) m_prop[0].bind(m_toolSize); if (targetType & TTool::ToonzImage || targetType & TTool::RasterImage) { @@ -332,10 +350,16 @@ public: m_prop[0].bind(m_type); m_prop[0].bind(m_edgeCount); - + m_snapSensitivity.addValue(LOW_WSTR); + m_snapSensitivity.addValue(MEDIUM_WSTR); + m_snapSensitivity.addValue(HIGH_WSTR); if (targetType & TTool::Vectors) { m_prop[0].bind(m_autogroup); m_prop[0].bind(m_autofill); + m_prop[0].bind(m_snap); + m_snap.setId("Snap"); + m_prop[0].bind(m_snapSensitivity); + m_snapSensitivity.setId("SnapSensitivity"); } if (targetType & TTool::ToonzImage) { m_prop[0].bind(m_selective); @@ -380,6 +404,8 @@ public: m_capStyle.setQStringName(tr("Cap")); m_joinStyle.setQStringName(tr("Join")); m_miterJoinLimit.setQStringName(tr("Miter:")); + m_snap.setQStringName(tr("Snap")); + m_snapSensitivity.setQStringName(tr("")); } }; @@ -426,10 +452,74 @@ public: virtual void onActivate(){}; virtual void onDeactivate(){}; virtual void onImageChanged(){}; + TPointD calculateSnap(TPointD pos); + void drawSnap(); + TPointD getSnap(TPointD pos); + void resetSnap(); + TPointD checkGuideSnapping(TPointD pos); virtual TStroke *makeStroke() const = 0; }; +//----------------------------------------------------------------------------- + +void Primitive::resetSnap() { + m_param->m_strokeIndex1 = -1; + m_param->m_w1 = -1; + m_param->m_foundSnap = false; +} + +//----------------------------------------------------------------------------- + +TPointD Primitive::calculateSnap(TPointD pos) { + m_param->m_foundSnap = false; + if (Preferences::instance()->getVectorSnappingTarget() == 1) return pos; + TVectorImageP vi(TTool::getImage(false)); + TPointD snapPoint = pos; + if (vi && m_param->m_snap.getValue()) { + double minDistance2 = m_param->m_minDistance2; + m_param->m_strokeIndex1 = -1; + + int i, strokeNumber = vi->getStrokeCount(); + + TStroke *stroke; + double distance2, outW; + + for (i = 0; i < strokeNumber; i++) { + stroke = vi->getStroke(i); + if (stroke->getNearestW(pos, outW, distance2) && + distance2 < minDistance2) { + minDistance2 = distance2; + m_param->m_strokeIndex1 = i; + if (areAlmostEqual(outW, 0.0, 1e-3)) + m_param->m_w1 = 0.0; + else if (areAlmostEqual(outW, 1.0, 1e-3)) + m_param->m_w1 = 1.0; + else + m_param->m_w1 = outW; + TThickPoint point1 = stroke->getPoint(m_param->m_w1); + snapPoint = TPointD(point1.x, point1.y); + m_param->m_foundSnap = true; + m_param->m_snapPoint = snapPoint; + } + } + } + return snapPoint; +} + +//----------------------------------------------------------------------------- + +TPointD Primitive::getSnap(TPointD pos) { + if (m_param->m_foundSnap) + return m_param->m_snapPoint; + else + return pos; +} + +// Primitive::drawSnap and Primitive::checkGuideSnapping are below the +// Geometric Tool definition since they use the m_tool property of Primitive +// but it isn't defined yet up here. + //============================================================================= // Rectangle Primitive Class Declaration //----------------------------------------------------------------------------- @@ -585,6 +675,7 @@ public: void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override; void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override; void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override; + void mouseMove(const TPointD &pos, const TMouseEvent &e) override; void leftButtonDoubleClick(const TPointD &, const TMouseEvent &e) override {} }; @@ -666,6 +757,7 @@ public: void leftButtonDown(const TPointD &pos, const TMouseEvent &) override; void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override; void leftButtonUp(const TPointD &pos, const TMouseEvent &) override; + void mouseMove(const TPointD &pos, const TMouseEvent &e) override; }; //============================================================================= @@ -800,7 +892,21 @@ public: m_param.m_joinStyle.setIndex(GeometricJoinStyle); m_param.m_miterJoinLimit.setValue(GeometricMiterValue); m_firstTime = false; + m_param.m_snap.setValue(GeometricSnap); + m_param.m_snapSensitivity.setIndex(GeometricSnapSensitivity); + switch (GeometricSnapSensitivity) { + case 0: + m_param.m_minDistance2 = SNAPPING_LOW; + break; + case 1: + m_param.m_minDistance2 = SNAPPING_MEDIUM; + break; + case 2: + m_param.m_minDistance2 = SNAPPING_HIGH; + break; + } } + m_primitive->resetSnap(); /*-- ショートカットでいきなりスタート(=onEnterを通らない場合)のとき、 LineToolが反応しないことがある対策 --*/ @@ -887,6 +993,22 @@ public: GeometricJoinStyle = m_param.m_joinStyle.getIndex(); else if (propertyName == m_param.m_miterJoinLimit.getName()) GeometricMiterValue = m_param.m_miterJoinLimit.getValue(); + else if (propertyName == m_param.m_snap.getName()) + GeometricSnap = m_param.m_snap.getValue(); + else if (propertyName == m_param.m_snapSensitivity.getName()) { + GeometricSnapSensitivity = m_param.m_snapSensitivity.getIndex(); + switch (GeometricSnapSensitivity) { + case 0: + m_param.m_minDistance2 = SNAPPING_LOW; + break; + case 1: + m_param.m_minDistance2 = SNAPPING_MEDIUM; + break; + case 2: + m_param.m_minDistance2 = SNAPPING_HIGH; + break; + } + } return false; } @@ -1013,11 +1135,102 @@ GeometricTool GeometricRasterTool(TTool::ToonzImage | TTool::EmptyTarget); GeometricTool GeometricRasterFullColorTool(TTool::RasterImage | TTool::EmptyTarget); +//------------------------------------------------------------------------------------------------------------- + +void Primitive::drawSnap() { + // snapping + if ((m_param->m_targetType & TTool::Vectors) && m_param->m_snap.getValue()) { + m_param->m_pixelSize = m_tool->getPixelSize(); + double thick = 6.0 * m_param->m_pixelSize; + if (m_param->m_foundSnap) { + tglColor(TPixelD(0.1, 0.9, 0.1)); + tglDrawCircle(m_param->m_snapPoint, thick); + } + } +} + +//------------------------------------------------------------------------------------------------------------- + +TPointD Primitive::checkGuideSnapping(TPointD pos) { + if (Preferences::instance()->getVectorSnappingTarget() == 0) { + if (m_param->m_foundSnap) + return m_param->m_snapPoint; + else + return pos; + } + if ((m_param->m_targetType & TTool::Vectors) && m_param->m_snap.getValue()) { + int vGuideCount = 0, hGuideCount = 0; + double guideDistance = sqrt(m_param->m_minDistance2); + TTool::Viewer *viewer = m_tool->getViewer(); + if (viewer) { + vGuideCount = viewer->getVGuideCount(); + hGuideCount = viewer->getHGuideCount(); + } + double distanceToVGuide = -1.0, distanceToHGuide = -1.0; + double vGuide, hGuide; + bool useGuides = false; + if (vGuideCount) { + for (int j = 0; j < vGuideCount; j++) { + double guide = viewer->getVGuide(j); + double tempDistance = abs(guide - pos.y); + if (tempDistance < guideDistance && + (distanceToVGuide < 0 || tempDistance < distanceToVGuide)) { + distanceToVGuide = tempDistance; + vGuide = guide; + useGuides = true; + } + } + } + if (hGuideCount) { + for (int j = 0; j < hGuideCount; j++) { + double guide = viewer->getHGuide(j); + double tempDistance = abs(guide - pos.x); + if (tempDistance < guideDistance && + (distanceToHGuide < 0 || tempDistance < distanceToHGuide)) { + distanceToHGuide = tempDistance; + hGuide = guide; + useGuides = true; + } + } + } + if (useGuides && m_param->m_foundSnap) { + double currYDistance = abs(m_param->m_snapPoint.y - pos.y); + double currXDistance = abs(m_param->m_snapPoint.x - pos.x); + double hypotenuse = + sqrt(pow(currYDistance, 2.0) + pow(currXDistance, 2.0)); + if ((distanceToVGuide >= 0 && distanceToVGuide < hypotenuse) || + (distanceToHGuide >= 0 && distanceToHGuide < hypotenuse)) + useGuides = true; + else + useGuides = false; + } + if (useGuides) { + assert(distanceToHGuide >= 0 || distanceToVGuide >= 0); + if (distanceToHGuide < 0 || + (distanceToVGuide <= distanceToHGuide && distanceToVGuide >= 0)) { + m_param->m_snapPoint.y = vGuide; + m_param->m_snapPoint.x = pos.x; + + } else { + m_param->m_snapPoint.y = pos.y; + m_param->m_snapPoint.x = hGuide; + } + m_param->m_foundSnap = true; + } + if (m_param->m_foundSnap) + return m_param->m_snapPoint; + else + return pos; + } else + return pos; +} + //============================================================================= // Rectangle Primitive Class Implementation //----------------------------------------------------------------------------- void RectanglePrimitive::draw() { + drawSnap(); if (m_isEditing || m_isPrompting || areAlmostEqual(m_selectingRect.x0, m_selectingRect.x1) || areAlmostEqual(m_selectingRect.y0, m_selectingRect.y1)) { @@ -1047,7 +1260,7 @@ void RectanglePrimitive::leftButtonDown(const TPointD &pos, } if (!m_isEditing) return; - + TPointD newPos = getSnap(pos); if (m_param->m_pencil.getValue() && (m_param->m_targetType & TTool::ToonzImage || m_param->m_targetType & TTool::RasterImage)) { @@ -1056,7 +1269,7 @@ void RectanglePrimitive::leftButtonDown(const TPointD &pos, else m_startPoint = TPointD((int)pos.x + 0.5, (int)pos.y + 0.5); } else - m_startPoint = pos; + m_startPoint = newPos; m_selectingRect.x0 = m_startPoint.x; m_selectingRect.y0 = m_startPoint.y; m_selectingRect.x1 = m_startPoint.x; @@ -1077,7 +1290,8 @@ void RectanglePrimitive::leftButtonDrag(const TPointD &realPos, pos.y = (realPos.y > m_startPoint.y) ? m_startPoint.y + distance : m_startPoint.y - distance; } else { - pos = realPos; + pos = calculateSnap(realPos); + pos = checkGuideSnapping(realPos); } if (m_param->m_pencil.getValue() && @@ -1170,12 +1384,16 @@ void RectanglePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { if (!m_isEditing) return; m_isEditing = false; m_tool->addStroke(); + resetSnap(); } //----------------------------------------------------------------------------- void RectanglePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { - m_pos = pos; + TPointD newPos = calculateSnap(pos); + newPos = checkGuideSnapping(pos); + m_pos = newPos; + m_tool->invalidate(); } //----------------------------------------------------------------------------- @@ -1197,6 +1415,7 @@ void RectanglePrimitive::onEnter() { //----------------------------------------------------------------------------- void CirclePrimitive::draw() { + drawSnap(); if (m_isEditing || m_isPrompting) { tglColor(m_isEditing ? m_color : TPixel32::Green); tglDrawCircle(m_centre, m_radius); @@ -1206,8 +1425,8 @@ void CirclePrimitive::draw() { //----------------------------------------------------------------------------- void CirclePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &) { - m_pos = pos; - m_centre = pos; + m_pos = getSnap(pos); + m_centre = m_pos; TTool::Application *app = TTool::getApplication(); if (!app) return; @@ -1235,7 +1454,9 @@ void CirclePrimitive::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { if (!m_isEditing) return; m_pos = pos; - m_radius = tdistance(m_centre, pos); + m_pos = calculateSnap(pos); + m_pos = checkGuideSnapping(pos); + m_radius = tdistance(m_centre, m_pos); } //----------------------------------------------------------------------------- @@ -1258,7 +1479,9 @@ void CirclePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { //----------------------------------------------------------------------------- void CirclePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { - m_pos = pos; + m_pos = calculateSnap(pos); + m_pos = checkGuideSnapping(pos); + m_tool->invalidate(); } //----------------------------------------------------------------------------- @@ -1366,6 +1589,8 @@ void MultiLinePrimitive::moveSpeed(const TPointD &delta) { void MultiLinePrimitive::draw() { UINT size = m_vertex.size(); + drawSnap(); + if ((m_isEditing || m_isPrompting) && size > 0) { tglColor(m_isEditing ? m_color : TPixel32::Green); std::vector points; @@ -1448,6 +1673,9 @@ void MultiLinePrimitive::leftButtonDown(const TPointD &pos, TUndoManager::manager()->add(m_undo); m_mousePosition = pos; + TPointD newPos; + newPos = getSnap(pos); + // Se clicco nell'ultimo vertice chiudo la linea. TPointD _pos = pos; if (m_closed) _pos = m_vertex.front(); @@ -1455,7 +1683,7 @@ void MultiLinePrimitive::leftButtonDown(const TPointD &pos, if (e.isShiftPressed() && !m_vertex.empty()) addVertex(rectify(m_vertex.back(), _pos)); else - addVertex(_pos); + addVertex(newPos); m_undo->setNewVertex(m_vertex); m_beforeSpeedMoved = m_speedMoved; @@ -1482,35 +1710,41 @@ void MultiLinePrimitive::leftButtonDrag(const TPointD &pos, void MultiLinePrimitive::leftButtonDoubleClick(const TPointD &, const TMouseEvent &e) { endLine(); + resetSnap(); } //----------------------------------------------------------------------------- void MultiLinePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { if (m_closed) endLine(); + resetSnap(); } //----------------------------------------------------------------------------- void MultiLinePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { m_ctrlDown = e.isCtrlPressed(); + TPointD newPos; + newPos = calculateSnap(pos); + newPos = checkGuideSnapping(pos); + if (m_isEditing) { if (e.isShiftPressed() && !m_vertex.empty()) - m_mousePosition = rectify(m_vertex.back(), pos); + m_mousePosition = rectify(m_vertex.back(), newPos); else - m_mousePosition = pos; + m_mousePosition = newPos; double dist = joinDistance * joinDistance; - if (!m_vertex.empty() && (tdistance2(m_mousePosition, m_vertex.front()) < - dist * m_tool->getPixelSize())) { + if (!m_vertex.empty() && + (tdistance2(pos, m_vertex.front()) < dist * m_tool->getPixelSize())) { m_closed = true; m_mousePosition = m_vertex.front(); } else m_closed = false; } else - m_mousePosition = pos; + m_mousePosition = newPos; m_tool->invalidate(); } @@ -1623,6 +1857,8 @@ void MultiLinePrimitive::onImageChanged() { onActivate(); } void LinePrimitive::draw() { UINT size = m_vertex.size(); + drawSnap(); + tglColor(TPixel32::Red); if (m_isEditing || m_isPrompting) { @@ -1636,6 +1872,14 @@ void LinePrimitive::draw() { //----------------------------------------------------------------------------- +void LinePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { + TPointD newPos = calculateSnap(pos); + newPos = checkGuideSnapping(pos); + m_tool->invalidate(); +} + +//----------------------------------------------------------------------------- + void LinePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { TTool::Application *app = TTool::getApplication(); if (!app) return; @@ -1656,24 +1900,26 @@ void LinePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { if (!m_isEditing) return; - m_mousePosition = pos; + TPointD newPos = getSnap(pos); + + m_mousePosition = newPos; - TPointD _pos = pos; + TPointD _pos = newPos; if (m_param->m_pencil.getValue() && (m_param->m_targetType & TTool::ToonzImage || m_param->m_targetType & TTool::RasterImage)) { if (m_param->m_rasterToolSize.getValue() % 2 != 0) - _pos = TPointD((int)pos.x, (int)pos.y); + _pos = TPointD((int)newPos.x, (int)newPos.y); else - _pos = TPointD((int)pos.x + 0.5, (int)pos.y + 0.5); + _pos = TPointD((int)newPos.x + 0.5, (int)newPos.y + 0.5); } if (m_vertex.size() == 0) addVertex(_pos); else { if (e.isShiftPressed() && !m_vertex.empty()) - addVertex(rectify(m_vertex.back(), _pos)); + addVertex(rectify(m_vertex.back(), pos)); else addVertex(_pos); endLine(); @@ -1684,19 +1930,26 @@ void LinePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { void LinePrimitive::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { if (!m_isEditing) return; + TPointD newPos = calculateSnap(pos); + newPos = checkGuideSnapping(pos); - m_mousePosition = pos; + m_mousePosition = newPos; } //----------------------------------------------------------------------------- void LinePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &e) { - m_mousePosition = pos; + // snapping + TPointD newPos = getSnap(pos); + + m_mousePosition = newPos; if (e.isShiftPressed() && !m_vertex.empty()) m_vertex.push_back(rectify(m_vertex.back(), pos)); else - m_vertex.push_back(pos); + m_vertex.push_back(newPos); endLine(); + + resetSnap(); } //----------------------------------------------------------------------------- @@ -1705,6 +1958,7 @@ void LinePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &e) { //----------------------------------------------------------------------------- void EllipsePrimitive::draw() { + drawSnap(); if (m_isEditing || m_isPrompting || areAlmostEqual(m_selectingRect.x0, m_selectingRect.x1) || areAlmostEqual(m_selectingRect.y0, m_selectingRect.y1)) { @@ -1727,12 +1981,12 @@ void EllipsePrimitive::draw() { void EllipsePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &) { TTool::Application *app = TTool::getApplication(); if (!app) return; - - m_startPoint = pos; - m_selectingRect.x0 = pos.x; - m_selectingRect.y0 = pos.y; - m_selectingRect.x1 = pos.x; - m_selectingRect.y1 = pos.y; + TPointD newPos = getSnap(pos); + m_startPoint = newPos; + m_selectingRect.x0 = newPos.x; + m_selectingRect.y0 = newPos.y; + m_selectingRect.x1 = newPos.x; + m_selectingRect.y1 = newPos.y; if (app->getCurrentObject()->isSpline()) { m_isEditing = true; @@ -1765,7 +2019,8 @@ void EllipsePrimitive::leftButtonDrag(const TPointD &realPos, pos.y = (realPos.y > m_startPoint.y) ? m_startPoint.y + distance : m_startPoint.y - distance; } else { - pos = realPos; + pos = calculateSnap(realPos); + pos = checkGuideSnapping(realPos); } m_pos = pos; @@ -1806,7 +2061,9 @@ void EllipsePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { //----------------------------------------------------------------------------- void EllipsePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { - m_pos = pos; + m_pos = calculateSnap(pos); + m_pos = checkGuideSnapping(pos); + m_tool->invalidate(); } //----------------------------------------------------------------------------- @@ -1828,6 +2085,8 @@ void EllipsePrimitive::onEnter() { //----------------------------------------------------------------------------- void ArcPrimitive::draw() { + drawSnap(); + switch (m_clickNumber) { case 1: tglColor(m_color); @@ -1864,6 +2123,8 @@ void ArcPrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { TTool::Application *app = TTool::getApplication(); if (!app) return; + TPointD newPos = getSnap(pos); + std::vector points(9); double thick = getThickness(); @@ -1885,13 +2146,13 @@ void ArcPrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { if (!m_isEditing) return; - m_endPoint = m_startPoint = pos; + m_endPoint = m_startPoint = newPos; m_clickNumber++; break; case 1: points[0] = TThickPoint(m_startPoint, thick); - points[8] = TThickPoint(pos, thick); + points[8] = TThickPoint(newPos, thick); points[4] = TThickPoint(0.5 * (points[0] + points[8]), thick); points[2] = TThickPoint(0.5 * (points[0] + points[4]), thick); points[6] = TThickPoint(0.5 * (points[4] + points[8]), thick); @@ -1911,21 +2172,24 @@ void ArcPrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { m_clickNumber = 0; break; } + resetSnap(); } //----------------------------------------------------------------------------- void ArcPrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { + TPointD newPos = calculateSnap(pos); + newPos = checkGuideSnapping(pos); + switch (m_clickNumber) { case 1: if (e.isShiftPressed()) m_endPoint = rectify(m_startPoint, pos); else - m_endPoint = pos; - m_tool->invalidate(); + m_endPoint = newPos; break; case 2: - m_centralPoint = TThickPoint(pos, getThickness()); + m_centralPoint = TThickPoint(newPos, getThickness()); TThickQuadratic q(m_stroke->getControlPoint(0), m_centralPoint, m_stroke->getControlPoint(8)); TThickQuadratic q0, q1, q00, q01, q10, q11; @@ -1947,10 +2211,9 @@ void ArcPrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { m_stroke->setControlPoint(6, q10.getP2()); m_stroke->setControlPoint(7, q11.getP1()); m_stroke->setControlPoint(8, q11.getP2()); - - m_tool->invalidate(); break; } + m_tool->invalidate(); } //----------------------------------------------------------------------------- @@ -1972,6 +2235,7 @@ void ArcPrimitive::onEnter() { //----------------------------------------------------------------------------- void PolygonPrimitive::draw() { + drawSnap(); if (!m_isEditing && !m_isPrompting) return; tglColor(m_isEditing ? m_color : TPixel32::Green); @@ -2011,7 +2275,7 @@ void PolygonPrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &) { if (!m_isEditing) return; - m_centre = pos; + m_centre = getSnap(pos); m_radius = 0; } @@ -2020,7 +2284,9 @@ void PolygonPrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &) { void PolygonPrimitive::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { if (!m_isEditing) return; - m_radius = tdistance(m_centre, pos); + TPointD newPos = calculateSnap(pos); + newPos = checkGuideSnapping(pos); + m_radius = tdistance(m_centre, newPos); } //----------------------------------------------------------------------------- @@ -2085,3 +2351,11 @@ void PolygonPrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { m_tool->addStroke(); } + +//----------------------------------------------------------------------------- + +void PolygonPrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { + TPointD newPos = calculateSnap(pos); + newPos = checkGuideSnapping(pos); + m_tool->invalidate(); +} \ No newline at end of file diff --git a/toonz/sources/tnztools/tooloptions.cpp b/toonz/sources/tnztools/tooloptions.cpp index 00f872d..100d283 100644 --- a/toonz/sources/tnztools/tooloptions.cpp +++ b/toonz/sources/tnztools/tooloptions.cpp @@ -1223,6 +1223,9 @@ GeometricToolOptionsBox::GeometricToolOptionsBox(QWidget *parent, TTool *tool, , m_hardnessField(0) , m_poligonSideField(0) , m_shapeField(0) + , m_snapCheckbox(0) + , m_snapSensitivityCombo(0) + , m_tool(tool) , m_pencilMode(0) { setFrameStyle(QFrame::StyledPanel); setFixedHeight(26); @@ -1266,6 +1269,14 @@ GeometricToolOptionsBox::GeometricToolOptionsBox(QWidget *parent, TTool *tool, SLOT(onPencilModeToggled(bool))); } + if (tool->getTargetType() & TTool::Vectors) { + m_snapCheckbox = + dynamic_cast(m_controls.value("Snap")); + m_snapSensitivityCombo = + dynamic_cast(m_controls.value("Sensitivity:")); + m_snapSensitivityCombo->setHidden(!m_snapCheckbox->isChecked()); + } + ToolOptionPopupButton *m_joinStyle = dynamic_cast(m_controls.value("Join")); m_miterField = @@ -1286,6 +1297,9 @@ void GeometricToolOptionsBox::updateStatus() { QMap::iterator it; for (it = m_controls.begin(); it != m_controls.end(); it++) it.value()->updateStatus(); + if (m_tool->getTargetType() & TTool::Vectors) { + m_snapSensitivityCombo->setHidden(!m_snapCheckbox->isChecked()); + } } //-----------------------------------------------------------------------------