/************************************************************************ ** ** @file vtoolsplinepath.cpp ** @author Roman Telezhynskyi ** @date November 15, 2013 ** ** @brief ** @copyright ** This source code is part of the Valentina project, a pattern making ** program, whose allow create and modeling patterns of clothing. ** Copyright (C) 2013-2015 Valentina project ** All Rights Reserved. ** ** Valentina is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** Valentina is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with Valentina. If not, see . ** *************************************************************************/ #include "vtoolsplinepath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../dialogs/tools/dialogtool.h" #include "../../../dialogs/tools/dialogsplinepath.h" #include "../../../undocommands/movesplinepath.h" #include "../../../undocommands/vundocommand.h" #include "../../../visualization/visualization.h" #include "../../../visualization/path/vistoolsplinepath.h" #include "../ifc/exception/vexception.h" #include "../ifc/xml/vdomdocument.h" #include "../ifc/ifcdef.h" #include "../qmuparser/qmutokenparser.h" #include "../vgeometry/vabstractcubicbezierpath.h" #include "../vgeometry/vabstractcurve.h" #include "../vgeometry/vgobject.h" #include "../vgeometry/vpointf.h" #include "../vgeometry/vspline.h" #include "../vgeometry/vsplinepoint.h" #include "../vmisc/vabstractapplication.h" #include "../vmisc/vmath.h" #include "../vpatterndb/vcontainer.h" #include "../vwidgets/../vgeometry/vsplinepath.h" #include "../vwidgets/vcontrolpointspline.h" #include "../vwidgets/vmaingraphicsscene.h" #include "../../vabstracttool.h" #include "../vdrawtool.h" #include "vabstractspline.h" const QString VToolSplinePath::ToolType = QStringLiteral("pathInteractive"); const QString VToolSplinePath::OldToolType = QStringLiteral("path"); //--------------------------------------------------------------------------------------------------------------------- /** * @brief VToolSplinePath constructor. * @param initData init data. * @param parent parent object. */ VToolSplinePath::VToolSplinePath(const VToolSplinePathInitData &initData, QGraphicsItem *parent) : VAbstractSpline(initData.doc, initData.data, initData.id, parent), oldPosition(), splIndex(-1), moved(false), oldMoveSplinePath(), newMoveSplinePath() { sceneType = SceneObject::SplinePath; this->setFlag(QGraphicsItem::ItemIsMovable, true); this->setFlag(QGraphicsItem::ItemIsFocusable, true);// For keyboard input focus auto InitControlPoint = [this](VControlPointSpline* cPoint) { connect(cPoint, &VControlPointSpline::ControlPointChangePosition, this, &VToolSplinePath::ControlPointChangePosition); connect(this, &VToolSplinePath::setEnabledPoint, cPoint, &VControlPointSpline::setEnabledPoint); connect(cPoint, &VControlPointSpline::ShowContextMenu, this, &VToolSplinePath::contextMenuEvent); connect(cPoint, &VControlPointSpline::Released, this, &VToolSplinePath::CurveReleased); connect(cPoint, &VControlPointSpline::Selected, this, &VToolSplinePath::CurveSelected); controlPoints.append(cPoint); }; const QSharedPointer splPath = initData.data->GeometricObject(initData.id); for (qint32 i = 1; i<=splPath->CountSubSpl(); ++i) { const VSpline spl = splPath->GetSpline(i); const bool freeAngle1 = qmu::QmuTokenParser::IsSingle(spl.GetStartAngleFormula()); const bool freeLength1 = qmu::QmuTokenParser::IsSingle(spl.GetC1LengthFormula()); auto *controlPoint = new VControlPointSpline(i, SplinePointPosition::FirstPoint, static_cast(spl.GetP2()), freeAngle1, freeLength1, this); InitControlPoint(controlPoint); const bool freeAngle2 = qmu::QmuTokenParser::IsSingle(spl.GetEndAngleFormula()); const bool freeLength2 = qmu::QmuTokenParser::IsSingle(spl.GetC2LengthFormula()); controlPoint = new VControlPointSpline(i, SplinePointPosition::LastPoint, static_cast(spl.GetP3()), freeAngle2, freeLength2, this); InitControlPoint(controlPoint); } ShowHandles(detailsMode); ToolCreation(initData.typeCreation); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief setDialog set dialog when user want change tool option. */ void VToolSplinePath::setDialog() { SCASSERT(not m_dialog.isNull()) QSharedPointer dialogTool = m_dialog.objectCast(); SCASSERT(not dialogTool.isNull()) const QSharedPointer splPath = VAbstractTool::data.GeometricObject(m_id); dialogTool->SetPath(*splPath); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Create help create tool from GUI. * @param dialog dialog. * @param scene pointer to scene. * @param doc dom document container. * @param data container with variables. */ VToolSplinePath* VToolSplinePath::Create(QSharedPointer dialog, VMainGraphicsScene *scene, VAbstractPattern *doc, VContainer *data) { SCASSERT(not dialog.isNull()) QSharedPointer dialogTool = dialog.objectCast(); SCASSERT(not dialogTool.isNull()) VToolSplinePathInitData initData; initData.scene = scene; initData.doc = doc; initData.data = data; initData.parse = Document::FullParse; initData.typeCreation = Source::FromGui; VSplinePath *path = new VSplinePath(dialogTool->GetPath()); for (qint32 i = 0; i < path->CountPoints(); ++i) { doc->IncrementReferens((*path)[i].P().getIdTool()); } VToolSplinePath* spl = Create(initData, path); if (spl != nullptr) { spl->m_dialog = dialogTool; } return spl; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Create help create tool. * @param initData init data. * @param path spline path. */ VToolSplinePath* VToolSplinePath::Create(VToolSplinePathInitData &initData, VSplinePath *path) { if (initData.typeCreation == Source::FromGui) { initData.id = initData.data->AddGObject(path); initData.data->AddCurveWithSegments(initData.data->GeometricObject(initData.id), initData.id); } else { initData.data->UpdateGObject(initData.id, path); initData.data->AddCurveWithSegments(initData.data->GeometricObject(initData.id), initData.id); if (initData.parse != Document::FullParse) { initData.doc->UpdateToolData(initData.id, initData.data); } } if (initData.parse == Document::FullParse) { VAbstractTool::AddRecord(initData.id, Tool::SplinePath, initData.doc); VToolSplinePath *spl = new VToolSplinePath(initData); initData.scene->addItem(spl); InitSplinePathToolConnections(initData.scene, spl); VAbstractPattern::AddTool(initData.id, spl); return spl; } return nullptr; } //--------------------------------------------------------------------------------------------------------------------- VToolSplinePath *VToolSplinePath::Create(VToolSplinePathInitData &initData) { auto path = new VSplinePath(); if (initData.duplicate > 0) { path->SetDuplicate(initData.duplicate); } for (int i = 0; i < initData.points.size(); ++i) { const qreal calcAngle1 = CheckFormula(initData.id, initData.a1[i], initData.data); const qreal calcAngle2 = CheckFormula(initData.id, initData.a2[i], initData.data); const qreal calcLength1 = qApp->toPixel(CheckFormula(initData.id, initData.l1[i], initData.data)); const qreal calcLength2 = qApp->toPixel(CheckFormula(initData.id, initData.l2[i], initData.data)); const auto p = *initData.data->GeometricObject(initData.points.at(i)); path->append(VSplinePoint(p, calcAngle1, initData.a1.at(i), calcAngle2, initData.a2.at(i), calcLength1, initData.l1.at(i), calcLength2, initData.l2.at(i))); } path->SetColor(initData.color); path->SetPenStyle(initData.penStyle); path->SetApproximationScale(initData.approximationScale); return VToolSplinePath::Create(initData, path); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief ControlPointChangePosition handle change position control point. * @param indexSpline position spline in spline list. * @param position position point in spline. * @param pos new position. */ void VToolSplinePath::ControlPointChangePosition(const qint32 &indexSpline, const SplinePointPosition &position, const QPointF &pos) { const QSharedPointer oldSplPath = VAbstractTool::data.GeometricObject(m_id); if (qApp->Settings()->IsFreeCurveMode() && not moved) { oldMoveSplinePath = QSharedPointer::create(*oldSplPath); moved = true; } QSharedPointer newSplPath = QSharedPointer::create(*oldSplPath); const VSpline spl = CorrectedSpline(newSplPath->GetSpline(indexSpline), position, pos); UpdateControlPoints(spl, newSplPath, indexSpline); if (not qApp->Settings()->IsFreeCurveMode()) { UndoCommandMove(*oldSplPath, *newSplPath); } else { newMoveSplinePath = newSplPath; VAbstractTool::data.UpdateGObject(m_id, newMoveSplinePath); RefreshGeometry(); if (QGraphicsScene *sc = scene()) { VMainGraphicsView::NewSceneRect(sc, qApp->getSceneView(), this); } } } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::EnableToolMove(bool move) { this->setFlag(QGraphicsItem::ItemIsMovable, move); foreach (auto *point, controlPoints) { point->setFlag(QGraphicsItem::ItemIsMovable, move); } } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::ShowContextMenu(QGraphicsSceneContextMenuEvent *event, quint32 id) { Q_UNUSED(id) try { ContextMenu(event); } catch(const VExceptionToolWasDeleted &e) { Q_UNUSED(e) return;//Leave this method immediately!!! } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief UpdateControlPoints update position points control points in file. * @param spl spline what was changed. * @param splPath spline path. * @param indexSpline index spline in spline path. */ void VToolSplinePath::UpdateControlPoints(const VSpline &spl, QSharedPointer &splPath, qint32 indexSpline) const { VSplinePoint p = splPath->GetSplinePoint(indexSpline, SplinePointPosition::FirstPoint); p.SetAngle2(spl.GetStartAngle(), spl.GetStartAngleFormula()); p.SetLength2(spl.GetC1Length(), spl.GetC1LengthFormula()); splPath->UpdatePoint(indexSpline, SplinePointPosition::FirstPoint, p); p = splPath->GetSplinePoint(indexSpline, SplinePointPosition::LastPoint); p.SetAngle1(spl.GetEndAngle(), spl.GetEndAngleFormula()); p.SetLength1(spl.GetC2Length(), spl.GetC2LengthFormula()); splPath->UpdatePoint(indexSpline, SplinePointPosition::LastPoint, p); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::SetSplinePathAttributes(QDomElement &domElement, const VSplinePath &path) { doc->SetAttribute(domElement, AttrType, ToolType); if (path.GetDuplicate() > 0) { doc->SetAttribute(domElement, AttrDuplicate, path.GetDuplicate()); } else { if (domElement.hasAttribute(AttrDuplicate)) { domElement.removeAttribute(AttrDuplicate); } } if (domElement.hasAttribute(AttrKCurve)) { domElement.removeAttribute(AttrKCurve); } doc->SetAttribute(domElement, AttrColor, path.GetColor()); doc->SetAttribute(domElement, AttrPenStyle, path.GetPenStyle()); doc->SetAttribute(domElement, AttrAScale, path.GetApproximationScale()); UpdatePathPoints(doc, domElement, path); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::UndoCommandMove(const VSplinePath &oldPath, const VSplinePath &newPath) { MoveSplinePath *moveSplPath = new MoveSplinePath(doc, oldPath, newPath, m_id); connect(moveSplPath, &VUndoCommand::NeedLiteParsing, doc, &VAbstractPattern::LiteParseTree); qApp->getUndoStack()->push(moveSplPath); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief UpdatePathPoints update spline path in pattern file. * @param doc dom document container. * @param element tag in file. * @param path spline path. */ void VToolSplinePath::UpdatePathPoints(VAbstractPattern *doc, QDomElement &element, const VSplinePath &path) { VDomDocument::RemoveAllChildren(element); for (qint32 i = 0; i < path.CountPoints(); ++i) { AddPathPoint(doc, element, path.at(i)); } } //--------------------------------------------------------------------------------------------------------------------- VSplinePath VToolSplinePath::getSplinePath() const { QSharedPointer splPath = VAbstractTool::data.GeometricObject(m_id); return *splPath.data(); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::setSplinePath(const VSplinePath &splPath) { QSharedPointer obj = VAbstractTool::data.GetGObject(m_id); QSharedPointer splinePath = qSharedPointerDynamicCast(obj); *splinePath.data() = splPath; SaveOption(obj); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::ShowVisualization(bool show) { ShowToolVisualization(show); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief AddPathPoint write path point to pattern file. * @param domElement dom element. * @param splPoint spline path point. */ void VToolSplinePath::AddPathPoint(VAbstractPattern *doc, QDomElement &domElement, const VSplinePoint &splPoint) { SCASSERT(doc != nullptr) QDomElement pathPoint = doc->createElement(AttrPathPoint); doc->SetAttribute(pathPoint, AttrPSpline, splPoint.P().id()); doc->SetAttribute(pathPoint, AttrLength1, splPoint.Length1Formula()); doc->SetAttribute(pathPoint, AttrLength2, splPoint.Length2Formula()); doc->SetAttribute(pathPoint, AttrAngle1, splPoint.Angle1Formula()); doc->SetAttribute(pathPoint, AttrAngle2, splPoint.Angle2Formula()); if (domElement.hasAttribute(AttrKAsm1)) { domElement.removeAttribute(AttrKAsm1); } if (domElement.hasAttribute(AttrKAsm2)) { domElement.removeAttribute(AttrKAsm2); } if (domElement.hasAttribute(AttrAngle)) { domElement.removeAttribute(AttrAngle); } domElement.appendChild(pathPoint); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief RemoveReferens decrement value of reference. */ void VToolSplinePath::RemoveReferens() { const QSharedPointer splPath = VAbstractTool::data.GeometricObject(m_id); for (qint32 i = 0; i < splPath->CountPoints(); ++i) { doc->DecrementReferens(splPath->at(i).P().getIdTool()); } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief SaveDialog save options into file after change in dialog. */ void VToolSplinePath::SaveDialog(QDomElement &domElement, QList &oldDependencies, QList &newDependencies) { SCASSERT(not m_dialog.isNull()) QSharedPointer dialogTool = m_dialog.objectCast(); SCASSERT(not dialogTool.isNull()) const auto oldSplPath = VAbstractTool::data.GeometricObject(m_id); for (qint32 i = 0; i < oldSplPath->CountPoints(); ++i) { AddDependence(oldDependencies, oldSplPath->at(i).P().id()); } const VSplinePath splPath = dialogTool->GetPath(); for (qint32 i = 0; i < splPath.CountPoints(); ++i) { AddDependence(newDependencies, splPath.at(i).P().id()); } for (qint32 i = 1; i <= splPath.CountSubSpl(); ++i) { VSpline spl = splPath.GetSpline(i); qint32 j = i*2; controlPoints[j-2]->blockSignals(true); controlPoints[j-1]->blockSignals(true); controlPoints[j-2]->setPos(static_cast(spl.GetP2())); controlPoints[j-1]->setPos(static_cast(spl.GetP3())); controlPoints[j-2]->blockSignals(false); controlPoints[j-1]->blockSignals(false); } SetSplinePathAttributes(domElement, splPath); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::SaveOptions(QDomElement &tag, QSharedPointer &obj) { VAbstractSpline::SaveOptions(tag, obj); QSharedPointer splPath = qSharedPointerDynamicCast(obj); SCASSERT(splPath.isNull() == false) SetSplinePathAttributes(tag, *splPath); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (flags() & QGraphicsItem::ItemIsMovable) { if (event->button() == Qt::LeftButton && event->type() != QEvent::GraphicsSceneMouseDoubleClick) { oldPosition = event->scenePos(); const auto splPath = VAbstractTool::data.GeometricObject(m_id); splIndex = splPath->Segment(oldPosition); if (IsMovable(splIndex)) { SetItemOverrideCursor(this, cursorArrowCloseHand, 1, 1); event->accept(); } } } VAbstractSpline::mousePressEvent(event); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (flags() & QGraphicsItem::ItemIsMovable) { if (event->button() == Qt::LeftButton && event->type() != QEvent::GraphicsSceneMouseDoubleClick) { oldPosition = event->scenePos(); SetItemOverrideCursor(this, cursorArrowOpenHand, 1, 1); CurveReleased(); } } VAbstractSpline::mouseReleaseEvent(event); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { // Don't need to check if left mouse button was pressed. According to the Qt documentation "If you do receive this // event, you can be certain that this item also received a mouse press event, and that this item is the current // mouse grabber.". if (IsMovable(splIndex)) { const auto oldSplPath = VAbstractTool::data.GeometricObject(m_id); if (qApp->Settings()->IsFreeCurveMode() && not moved) { oldMoveSplinePath = QSharedPointer::create(*oldSplPath); moved = true; } newMoveSplinePath = QSharedPointer::create(*oldSplPath); VSpline spline = newMoveSplinePath->GetSpline(splIndex); const qreal t = spline.ParamT(oldPosition); if (qFloor(t) == -1) { return; } // Magic Bezier Drag Equations follow! // "weight" describes how the influence of the drag should be distributed // among the handles; 0 = front handle only, 1 = back handle only. double weight; if (t <= 1.0 / 6.0) { weight = 0; } else if (t <= 0.5) { weight = (pow((6 * t - 1) / 2.0, 3)) / 2; } else if (t <= 5.0 / 6.0) { weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5; } else { weight = 1; } const QPointF delta = event->scenePos() - oldPosition; const QPointF offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta; const QPointF offset1 = (weight/(3*t*t*(1-t))) * delta; const QPointF p2 = static_cast(spline.GetP2()) + offset0; const QPointF p3 = static_cast(spline.GetP3()) + offset1; oldPosition = event->scenePos(); // Now mouse here const VSpline spl = VSpline(spline.GetP1(), p2, p3, spline.GetP4()); UpdateControlPoints(spl, newMoveSplinePath, splIndex); if (not qApp->Settings()->IsFreeCurveMode()) { UndoCommandMove(*oldSplPath, *newMoveSplinePath); } else { VAbstractTool::data.UpdateGObject(m_id, newMoveSplinePath); RefreshGeometry(); if (QGraphicsScene *sc = scene()) { VMainGraphicsView::NewSceneRect(sc, qApp->getSceneView(), this); } } // Each time we move something we call recalculation scene rect. In some cases this can cause moving // objects positions. And this cause infinite redrawing. That's why we wait the finish of saving the last move. static bool changeFinished = true; if (changeFinished) { changeFinished = false; const QList viewList = scene()->views(); if (not viewList.isEmpty()) { if (VMainGraphicsView *view = qobject_cast(viewList.at(0))) { VMainGraphicsScene *currentScene = qobject_cast(scene()); SCASSERT(currentScene) const QPointF cursorPosition = currentScene->getScenePos(); const qreal scale = SceneScale(scene()); view->EnsureVisibleWithDelay(QRectF(cursorPosition.x()-5/scale, cursorPosition.y()-5/scale, 10/scale, 10/scale), VMainGraphicsView::scrollDelay); } } changeFinished = true; } } } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { if (m_acceptHoverEvents) { if (flags() & QGraphicsItem::ItemIsMovable) { oldPosition = event->scenePos(); const auto splPath = VAbstractTool::data.GeometricObject(m_id); splIndex = splPath->Segment(oldPosition); if (IsMovable(splIndex)) { SetItemOverrideCursor(this, cursorArrowOpenHand, 1, 1); } else { setCursor(qApp->getSceneView()->viewport()->cursor()); } } else { setCursor(qApp->getSceneView()->viewport()->cursor()); } VAbstractSpline::hoverEnterEvent(event); } else { setCursor(qApp->getSceneView()->viewport()->cursor()); } } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { if (m_acceptHoverEvents) { VAbstractSpline::hoverLeaveEvent(event); } } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::SetVisualization() { if (not vis.isNull()) { VisToolSplinePath *visual = qobject_cast(vis); SCASSERT(visual != nullptr) QSharedPointer splPath = VAbstractTool::data.GeometricObject(m_id); visual->setPath(*splPath.data()); visual->setLineStyle(LineStyleToPenStyle(splPath->GetPenStyle())); visual->SetMode(Mode::Show); visual->RefreshGeometry(); } } //--------------------------------------------------------------------------------------------------------------------- bool VToolSplinePath::IsMovable(int index) const { const auto splPath = VAbstractTool::data.GeometricObject(m_id); //index == -1 - can delete, but decided to left if (index == -1 || index < 1 || index > splPath->CountSubSpl()) { return false; } const VSplinePoint p1 = splPath->GetSplinePoint(index, SplinePointPosition::FirstPoint); const VSplinePoint p2 = splPath->GetSplinePoint(index, SplinePointPosition::LastPoint); return p1.IsMovable() && p2.IsMovable(); } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::RefreshCtrlPoints() { // Very important to disable control points. Without it the pogram can't move the curve. foreach (auto *point, controlPoints) { point->setFlag(QGraphicsItem::ItemSendsGeometryChanges, false); } const auto splPath = VAbstractTool::data.GeometricObject(m_id); for (qint32 i = 1; i<=splPath->CountSubSpl(); ++i) { const qint32 j = i*2; controlPoints[j-2]->blockSignals(true); controlPoints[j-1]->blockSignals(true); const auto spl = splPath->GetSpline(i); { const bool freeAngle1 = qmu::QmuTokenParser::IsSingle(spl.GetStartAngleFormula()); const bool freeLength1 = qmu::QmuTokenParser::IsSingle(spl.GetC1LengthFormula()); const auto splinePoint = spl.GetP1(); controlPoints[j-2]->RefreshCtrlPoint(i, SplinePointPosition::FirstPoint, static_cast(spl.GetP2()), static_cast(splinePoint), freeAngle1, freeLength1); } { const bool freeAngle2 = qmu::QmuTokenParser::IsSingle(spl.GetEndAngleFormula()); const bool freeLength2 = qmu::QmuTokenParser::IsSingle(spl.GetC2LengthFormula()); const auto splinePoint = spl.GetP4(); controlPoints[j-1]->RefreshCtrlPoint(i, SplinePointPosition::LastPoint, static_cast(spl.GetP3()), static_cast(splinePoint), freeAngle2, freeLength2); } controlPoints[j-2]->blockSignals(false); controlPoints[j-1]->blockSignals(false); } foreach (auto *point, controlPoints) { point->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); } } //--------------------------------------------------------------------------------------------------------------------- void VToolSplinePath::CurveReleased() { if (qApp->Settings()->IsFreeCurveMode() && moved) { UndoCommandMove(*oldMoveSplinePath, *newMoveSplinePath); oldMoveSplinePath.clear(); newMoveSplinePath.clear(); moved = false; } }