1 /************************************************************************
2 **
3 ** @file vtoolsplinepath.cpp
4 ** @author Roman Telezhynskyi <dismine(at)gmail.com>
5 ** @date November 15, 2013
6 **
7 ** @brief
8 ** @copyright
9 ** This source code is part of the Valentina project, a pattern making
10 ** program, whose allow create and modeling patterns of clothing.
11 ** Copyright (C) 2013-2015 Valentina project
12 ** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13 **
14 ** Valentina is free software: you can redistribute it and/or modify
15 ** it under the terms of the GNU General Public License as published by
16 ** the Free Software Foundation, either version 3 of the License, or
17 ** (at your option) any later version.
18 **
19 ** Valentina is distributed in the hope that it will be useful,
20 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ** GNU General Public License for more details.
23 **
24 ** You should have received a copy of the GNU General Public License
25 ** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
26 **
27 *************************************************************************/
28
29 #include "vtoolsplinepath.h"
30
31 #include <QDomElement>
32 #include <QEvent>
33 #include <QFlags>
34 #include <QGraphicsScene>
35 #include <QGraphicsSceneHoverEvent>
36 #include <QGraphicsSceneMouseEvent>
37 #include <QGraphicsView>
38 #include <QList>
39 #include <QPen>
40 #include <QPoint>
41 #include <QRectF>
42 #include <QSharedPointer>
43 #include <QStaticStringData>
44 #include <QStringData>
45 #include <QStringDataPtr>
46 #include <QUndoStack>
47 #include <Qt>
48 #include <new>
49
50 #include "../../../dialogs/tools/dialogtool.h"
51 #include "../../../dialogs/tools/dialogsplinepath.h"
52 #include "../../../undocommands/movesplinepath.h"
53 #include "../../../undocommands/vundocommand.h"
54 #include "../../../visualization/visualization.h"
55 #include "../../../visualization/path/vistoolsplinepath.h"
56 #include "../ifc/exception/vexception.h"
57 #include "../ifc/xml/vdomdocument.h"
58 #include "../ifc/ifcdef.h"
59 #include "../qmuparser/qmutokenparser.h"
60 #include "../vgeometry/vabstractcubicbezierpath.h"
61 #include "../vgeometry/vabstractcurve.h"
62 #include "../vgeometry/vgobject.h"
63 #include "../vgeometry/vpointf.h"
64 #include "../vgeometry/vspline.h"
65 #include "../vgeometry/vsplinepoint.h"
66 #include "../vmisc/vabstractapplication.h"
67 #include "../vmisc/vmath.h"
68 #include "../vpatterndb/vcontainer.h"
69 #include "../vwidgets/../vgeometry/vsplinepath.h"
70 #include "../vwidgets/vcontrolpointspline.h"
71 #include "../vwidgets/vmaingraphicsscene.h"
72 #include "../../vabstracttool.h"
73 #include "../vdrawtool.h"
74 #include "vabstractspline.h"
75
76 const QString VToolSplinePath::ToolType = QStringLiteral("pathInteractive");
77 const QString VToolSplinePath::OldToolType = QStringLiteral("path");
78
79 //---------------------------------------------------------------------------------------------------------------------
80 /**
81 * @brief VToolSplinePath constructor.
82 * @param initData init data.
83 * @param parent parent object.
84 */
VToolSplinePath(const VToolSplinePathInitData & initData,QGraphicsItem * parent)85 VToolSplinePath::VToolSplinePath(const VToolSplinePathInitData &initData, QGraphicsItem *parent)
86 : VAbstractSpline(initData.doc, initData.data, initData.id, initData.notes, parent),
87 oldPosition(),
88 splIndex(-1),
89 moved(false),
90 oldMoveSplinePath(),
91 newMoveSplinePath()
92 {
93 sceneType = SceneObject::SplinePath;
94
95 this->setFlag(QGraphicsItem::ItemIsMovable, true);
96 this->setFlag(QGraphicsItem::ItemIsFocusable, true);// For keyboard input focus
97
98 auto InitControlPoint = [this](VControlPointSpline* cPoint)
99 {
100 connect(cPoint, &VControlPointSpline::ControlPointChangePosition, this,
101 &VToolSplinePath::ControlPointChangePosition);
102 connect(this, &VToolSplinePath::setEnabledPoint, cPoint, &VControlPointSpline::setEnabledPoint);
103 connect(cPoint, &VControlPointSpline::ShowContextMenu, this, &VToolSplinePath::contextMenuEvent);
104 connect(cPoint, &VControlPointSpline::Released, this, &VToolSplinePath::CurveReleased);
105 connect(cPoint, &VControlPointSpline::Selected, this, &VToolSplinePath::CurveSelected);
106 controlPoints.append(cPoint);
107 };
108
109 const QSharedPointer<VSplinePath> splPath = initData.data->GeometricObject<VSplinePath>(initData.id);
110 for (qint32 i = 1; i<=splPath->CountSubSpl(); ++i)
111 {
112 const VSpline spl = splPath->GetSpline(i);
113
114 bool freeAngle1 = true;
115
116 if (i > 1)
117 {
118 const VSpline prevSpl = splPath->GetSpline(i-1);
119 freeAngle1 = qmu::QmuTokenParser::IsSingle(spl.GetStartAngleFormula()) &&
120 qmu::QmuTokenParser::IsSingle(prevSpl.GetEndAngleFormula());
121 }
122 else
123 {
124 freeAngle1 = qmu::QmuTokenParser::IsSingle(spl.GetStartAngleFormula());
125 }
126
127 const bool freeLength1 = qmu::QmuTokenParser::IsSingle(spl.GetC1LengthFormula());
128
129 auto *controlPoint = new VControlPointSpline(i, SplinePointPosition::FirstPoint,
130 static_cast<QPointF>(spl.GetP2()), freeAngle1, freeLength1, this);
131 InitControlPoint(controlPoint);
132
133 bool freeAngle2 = true;
134
135 if (i < splPath->CountSubSpl())
136 {
137 const VSpline nextSpl = splPath->GetSpline(i+1);
138 freeAngle2 = qmu::QmuTokenParser::IsSingle(nextSpl.GetStartAngleFormula()) &&
139 qmu::QmuTokenParser::IsSingle(spl.GetEndAngleFormula());
140 }
141 else
142 {
143 freeAngle2 = qmu::QmuTokenParser::IsSingle(spl.GetEndAngleFormula());
144 }
145
146 const bool freeLength2 = qmu::QmuTokenParser::IsSingle(spl.GetC2LengthFormula());
147
148 controlPoint = new VControlPointSpline(i, SplinePointPosition::LastPoint, static_cast<QPointF>(spl.GetP3()),
149 freeAngle2, freeLength2, this);
150 InitControlPoint(controlPoint);
151 }
152
153 VToolSplinePath::RefreshCtrlPoints();
154
155 ShowHandles(detailsMode);
156
157 ToolCreation(initData.typeCreation);
158 }
159
160 //---------------------------------------------------------------------------------------------------------------------
161 /**
162 * @brief setDialog set dialog when user want change tool option.
163 */
setDialog()164 void VToolSplinePath::setDialog()
165 {
166 SCASSERT(not m_dialog.isNull())
167 const QPointer<DialogSplinePath> dialogTool = qobject_cast<DialogSplinePath *>(m_dialog);
168 SCASSERT(not dialogTool.isNull())
169 const QSharedPointer<VSplinePath> splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
170 dialogTool->SetPath(*splPath);
171 dialogTool->SetNotes(m_notes);
172 }
173
174 //---------------------------------------------------------------------------------------------------------------------
175 /**
176 * @brief Create help create tool from GUI.
177 * @param dialog dialog.
178 * @param scene pointer to scene.
179 * @param doc dom document container.
180 * @param data container with variables.
181 */
Create(const QPointer<DialogTool> & dialog,VMainGraphicsScene * scene,VAbstractPattern * doc,VContainer * data)182 VToolSplinePath* VToolSplinePath::Create(const QPointer<DialogTool> &dialog, VMainGraphicsScene *scene,
183 VAbstractPattern *doc, VContainer *data)
184 {
185 SCASSERT(not dialog.isNull())
186 const QPointer<DialogSplinePath> dialogTool = qobject_cast<DialogSplinePath *>(dialog);
187 SCASSERT(not dialogTool.isNull())
188
189 VToolSplinePathInitData initData;
190 initData.scene = scene;
191 initData.doc = doc;
192 initData.data = data;
193 initData.parse = Document::FullParse;
194 initData.typeCreation = Source::FromGui;
195 initData.notes = dialogTool->GetNotes();
196
197 VSplinePath *path = new VSplinePath(dialogTool->GetPath());
198 for (qint32 i = 0; i < path->CountPoints(); ++i)
199 {
200 doc->IncrementReferens((*path)[i].P().getIdTool());
201 }
202
203 VToolSplinePath* spl = Create(initData, path);
204 if (spl != nullptr)
205 {
206 spl->m_dialog = dialog;
207 }
208 return spl;
209 }
210
211 //---------------------------------------------------------------------------------------------------------------------
212 /**
213 * @brief Create help create tool.
214 * @param initData init data.
215 * @param path spline path.
216 */
Create(VToolSplinePathInitData & initData,VSplinePath * path)217 VToolSplinePath* VToolSplinePath::Create(VToolSplinePathInitData &initData, VSplinePath *path)
218 {
219 if (initData.typeCreation == Source::FromGui)
220 {
221 initData.id = initData.data->AddGObject(path);
222 initData.data->AddCurveWithSegments(initData.data->GeometricObject<VAbstractCubicBezierPath>(initData.id),
223 initData.id);
224 }
225 else
226 {
227 initData.data->UpdateGObject(initData.id, path);
228 initData.data->AddCurveWithSegments(initData.data->GeometricObject<VAbstractCubicBezierPath>(initData.id),
229 initData.id);
230 if (initData.parse != Document::FullParse)
231 {
232 initData.doc->UpdateToolData(initData.id, initData.data);
233 }
234 }
235
236 if (initData.parse == Document::FullParse)
237 {
238 VAbstractTool::AddRecord(initData.id, Tool::SplinePath, initData.doc);
239 VToolSplinePath *spl = new VToolSplinePath(initData);
240 initData.scene->addItem(spl);
241 InitSplinePathToolConnections(initData.scene, spl);
242 VAbstractPattern::AddTool(initData.id, spl);
243 return spl;
244 }
245 return nullptr;
246 }
247
248 //---------------------------------------------------------------------------------------------------------------------
Create(VToolSplinePathInitData & initData)249 VToolSplinePath *VToolSplinePath::Create(VToolSplinePathInitData &initData)
250 {
251 auto* path = new VSplinePath();
252
253 if (initData.duplicate > 0)
254 {
255 path->SetDuplicate(initData.duplicate);
256 }
257
258 for (int i = 0; i < initData.points.size(); ++i)
259 {
260 const qreal calcAngle1 = CheckFormula(initData.id, initData.a1[i], initData.data);
261 const qreal calcAngle2 = CheckFormula(initData.id, initData.a2[i], initData.data);
262
263 const qreal calcLength1 =
264 VAbstractValApplication::VApp()->toPixel(CheckFormula(initData.id, initData.l1[i], initData.data));
265 const qreal calcLength2 =
266 VAbstractValApplication::VApp()->toPixel(CheckFormula(initData.id, initData.l2[i], initData.data));
267
268 const auto p = *initData.data->GeometricObject<VPointF>(initData.points.at(i));
269
270 path->append(VSplinePoint(p, calcAngle1, initData.a1.at(i), calcAngle2, initData.a2.at(i), calcLength1,
271 initData.l1.at(i), calcLength2, initData.l2.at(i)));
272 }
273
274 path->SetColor(initData.color);
275 path->SetPenStyle(initData.penStyle);
276 path->SetApproximationScale(initData.approximationScale);
277 path->SetAliasSuffix(initData.aliasSuffix);
278
279 return VToolSplinePath::Create(initData, path);
280 }
281
282 //---------------------------------------------------------------------------------------------------------------------
283 /**
284 * @brief ControlPointChangePosition handle change position control point.
285 * @param indexSpline position spline in spline list.
286 * @param position position point in spline.
287 * @param pos new position.
288 */
ControlPointChangePosition(const qint32 & indexSpline,const SplinePointPosition & position,const QPointF & pos)289 void VToolSplinePath::ControlPointChangePosition(const qint32 &indexSpline, const SplinePointPosition &position,
290 const QPointF &pos)
291 {
292 const QSharedPointer<VSplinePath> oldSplPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
293
294 if (VAbstractApplication::VApp()->Settings()->IsFreeCurveMode() && not moved)
295 {
296 oldMoveSplinePath = QSharedPointer<VSplinePath>::create(*oldSplPath);
297 moved = true;
298 }
299
300 QSharedPointer<VSplinePath> newSplPath = QSharedPointer<VSplinePath>::create(*oldSplPath);
301 const VSpline spl = CorrectedSpline(newSplPath->GetSpline(indexSpline), position, pos);
302
303 UpdateControlPoints(spl, newSplPath, indexSpline);
304
305 if (not VAbstractApplication::VApp()->Settings()->IsFreeCurveMode())
306 {
307 UndoCommandMove(*oldSplPath, *newSplPath);
308 }
309 else
310 {
311 newMoveSplinePath = newSplPath;
312 VAbstractTool::data.UpdateGObject(m_id, newMoveSplinePath);
313 RefreshGeometry();
314
315 if (QGraphicsScene *sc = scene())
316 {
317 VMainGraphicsView::NewSceneRect(sc, VAbstractValApplication::VApp()->getSceneView(), this);
318 }
319 }
320 }
321
322 //---------------------------------------------------------------------------------------------------------------------
EnableToolMove(bool move)323 void VToolSplinePath::EnableToolMove(bool move)
324 {
325 this->setFlag(QGraphicsItem::ItemIsMovable, move);
326
327 for (auto point : qAsConst(controlPoints))
328 {
329 point->setFlag(QGraphicsItem::ItemIsMovable, move);
330 }
331 }
332
333 //---------------------------------------------------------------------------------------------------------------------
ShowContextMenu(QGraphicsSceneContextMenuEvent * event,quint32 id)334 void VToolSplinePath::ShowContextMenu(QGraphicsSceneContextMenuEvent *event, quint32 id)
335 {
336 Q_UNUSED(id)
337 try
338 {
339 ContextMenu<DialogSplinePath>(event);
340 }
341 catch(const VExceptionToolWasDeleted &e)
342 {
343 Q_UNUSED(e)
344 return;//Leave this method immediately!!!
345 }
346 }
347
348 //---------------------------------------------------------------------------------------------------------------------
349 /**
350 * @brief UpdateControlPoints update position control points in file.
351 * @param spl spline that was changed.
352 * @param splPath spline path.
353 * @param indexSpline index spline in spline path.
354 */
UpdateControlPoints(const VSpline & spl,QSharedPointer<VSplinePath> & splPath,qint32 indexSpline) const355 void VToolSplinePath::UpdateControlPoints(const VSpline &spl, QSharedPointer<VSplinePath> &splPath,
356 qint32 indexSpline) const
357 {
358 VSplinePoint p = splPath->GetSplinePoint(indexSpline, SplinePointPosition::FirstPoint);
359
360 if (indexSpline >1)
361 {
362 VSpline prevSpline = splPath->GetSpline(indexSpline-1);
363 if (qmu::QmuTokenParser::IsSingle(prevSpline.GetEndAngleFormula()))
364 {
365 p.SetAngle2(spl.GetStartAngle(), spl.GetStartAngleFormula());
366 }
367 }
368 else
369 {
370 p.SetAngle2(spl.GetStartAngle(), spl.GetStartAngleFormula());
371 }
372
373
374 p.SetLength2(spl.GetC1Length(), spl.GetC1LengthFormula());
375 splPath->UpdatePoint(indexSpline, SplinePointPosition::FirstPoint, p);
376
377 p = splPath->GetSplinePoint(indexSpline, SplinePointPosition::LastPoint);
378
379 if (indexSpline < splPath->CountSubSpl())
380 {
381 VSpline nextSpline = splPath->GetSpline(indexSpline+1);
382 if (qmu::QmuTokenParser::IsSingle(nextSpline.GetStartAngleFormula()))
383 {
384 p.SetAngle1(spl.GetEndAngle(), spl.GetEndAngleFormula());
385 }
386 }
387 else
388 {
389 p.SetAngle1(spl.GetEndAngle(), spl.GetEndAngleFormula());
390 }
391
392 p.SetLength1(spl.GetC2Length(), spl.GetC2LengthFormula());
393 splPath->UpdatePoint(indexSpline, SplinePointPosition::LastPoint, p);
394 }
395
396 //---------------------------------------------------------------------------------------------------------------------
SetSplinePathAttributes(QDomElement & domElement,const VSplinePath & path)397 void VToolSplinePath::SetSplinePathAttributes(QDomElement &domElement, const VSplinePath &path)
398 {
399 doc->SetAttribute(domElement, AttrType, ToolType);
400 doc->SetAttributeOrRemoveIf(domElement, AttrDuplicate, path.GetDuplicate(), path.GetDuplicate() <= 0);
401
402 if (domElement.hasAttribute(AttrKCurve))
403 {
404 domElement.removeAttribute(AttrKCurve);
405 }
406
407 doc->SetAttribute(domElement, AttrColor, path.GetColor());
408 doc->SetAttribute(domElement, AttrPenStyle, path.GetPenStyle());
409 doc->SetAttribute(domElement, AttrAScale, path.GetApproximationScale());
410 doc->SetAttributeOrRemoveIf(domElement, AttrAlias, path.GetAliasSuffix(), path.GetAliasSuffix().isEmpty());
411
412 UpdatePathPoints(doc, domElement, path);
413 }
414
415 //---------------------------------------------------------------------------------------------------------------------
UndoCommandMove(const VSplinePath & oldPath,const VSplinePath & newPath)416 void VToolSplinePath::UndoCommandMove(const VSplinePath &oldPath, const VSplinePath &newPath)
417 {
418 MoveSplinePath *moveSplPath = new MoveSplinePath(doc, oldPath, newPath, m_id);
419 connect(moveSplPath, &VUndoCommand::NeedLiteParsing, doc, &VAbstractPattern::LiteParseTree);
420 VAbstractApplication::VApp()->getUndoStack()->push(moveSplPath);
421 }
422
423 //---------------------------------------------------------------------------------------------------------------------
424 /**
425 * @brief UpdatePathPoints update spline path in pattern file.
426 * @param doc dom document container.
427 * @param element tag in file.
428 * @param path spline path.
429 */
UpdatePathPoints(VAbstractPattern * doc,QDomElement & element,const VSplinePath & path)430 void VToolSplinePath::UpdatePathPoints(VAbstractPattern *doc, QDomElement &element, const VSplinePath &path)
431 {
432 VDomDocument::RemoveAllChildren(element);
433 for (qint32 i = 0; i < path.CountPoints(); ++i)
434 {
435 AddPathPoint(doc, element, path.at(i));
436 }
437 }
438
439 //---------------------------------------------------------------------------------------------------------------------
getSplinePath() const440 VSplinePath VToolSplinePath::getSplinePath() const
441 {
442 QSharedPointer<VSplinePath> splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
443 return *splPath.data();
444 }
445
446 //---------------------------------------------------------------------------------------------------------------------
setSplinePath(const VSplinePath & splPath)447 void VToolSplinePath::setSplinePath(const VSplinePath &splPath)
448 {
449 QSharedPointer<VGObject> obj = VAbstractTool::data.GetGObject(m_id);
450 QSharedPointer<VSplinePath> splinePath = qSharedPointerDynamicCast<VSplinePath>(obj);
451 *splinePath.data() = splPath;
452 SaveOption(obj);
453 }
454
455 //---------------------------------------------------------------------------------------------------------------------
ShowVisualization(bool show)456 void VToolSplinePath::ShowVisualization(bool show)
457 {
458 ShowToolVisualization<VisToolSplinePath>(show);
459 }
460
461 //---------------------------------------------------------------------------------------------------------------------
462 /**
463 * @brief AddPathPoint write path point to pattern file.
464 * @param domElement dom element.
465 * @param splPoint spline path point.
466 */
AddPathPoint(VAbstractPattern * doc,QDomElement & domElement,const VSplinePoint & splPoint)467 void VToolSplinePath::AddPathPoint(VAbstractPattern *doc, QDomElement &domElement, const VSplinePoint &splPoint)
468 {
469 SCASSERT(doc != nullptr)
470 QDomElement pathPoint = doc->createElement(AttrPathPoint);
471
472 doc->SetAttribute(pathPoint, AttrPSpline, splPoint.P().id());
473 doc->SetAttribute(pathPoint, AttrLength1, splPoint.Length1Formula());
474 doc->SetAttribute(pathPoint, AttrLength2, splPoint.Length2Formula());
475 doc->SetAttribute(pathPoint, AttrAngle1, splPoint.Angle1Formula());
476 doc->SetAttribute(pathPoint, AttrAngle2, splPoint.Angle2Formula());
477
478 if (domElement.hasAttribute(AttrKAsm1))
479 {
480 domElement.removeAttribute(AttrKAsm1);
481 }
482
483 if (domElement.hasAttribute(AttrKAsm2))
484 {
485 domElement.removeAttribute(AttrKAsm2);
486 }
487
488 if (domElement.hasAttribute(AttrAngle))
489 {
490 domElement.removeAttribute(AttrAngle);
491 }
492
493 domElement.appendChild(pathPoint);
494 }
495
496 //---------------------------------------------------------------------------------------------------------------------
497 /**
498 * @brief RemoveReferens decrement value of reference.
499 */
RemoveReferens()500 void VToolSplinePath::RemoveReferens()
501 {
502 const QSharedPointer<VSplinePath> splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
503 for (qint32 i = 0; i < splPath->CountPoints(); ++i)
504 {
505 doc->DecrementReferens(splPath->at(i).P().getIdTool());
506 }
507 }
508
509 //---------------------------------------------------------------------------------------------------------------------
510 /**
511 * @brief SaveDialog save options into file after change in dialog.
512 */
SaveDialog(QDomElement & domElement,QList<quint32> & oldDependencies,QList<quint32> & newDependencies)513 void VToolSplinePath::SaveDialog(QDomElement &domElement, QList<quint32> &oldDependencies,
514 QList<quint32> &newDependencies)
515 {
516 SCASSERT(not m_dialog.isNull())
517 const QPointer<DialogSplinePath> dialogTool = qobject_cast<DialogSplinePath *>(m_dialog);
518 SCASSERT(not dialogTool.isNull())
519
520 const auto oldSplPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
521 for (qint32 i = 0; i < oldSplPath->CountPoints(); ++i)
522 {
523 AddDependence(oldDependencies, oldSplPath->at(i).P().id());
524 }
525
526 const VSplinePath splPath = dialogTool->GetPath();
527 for (qint32 i = 0; i < splPath.CountPoints(); ++i)
528 {
529 AddDependence(newDependencies, splPath.at(i).P().id());
530 }
531
532 for (qint32 i = 1; i <= splPath.CountSubSpl(); ++i)
533 {
534 VSpline spl = splPath.GetSpline(i);
535 qint32 j = i*2;
536
537 controlPoints[j-2]->blockSignals(true);
538 controlPoints[j-1]->blockSignals(true);
539
540 controlPoints[j-2]->setPos(static_cast<QPointF>(spl.GetP2()));
541 controlPoints[j-1]->setPos(static_cast<QPointF>(spl.GetP3()));
542
543 controlPoints[j-2]->blockSignals(false);
544 controlPoints[j-1]->blockSignals(false);
545 }
546
547 const QString notes = dialogTool->GetNotes();
548 doc->SetAttributeOrRemoveIf(domElement, AttrNotes, notes, notes.isEmpty());
549
550 SetSplinePathAttributes(domElement, splPath);
551 }
552
553 //---------------------------------------------------------------------------------------------------------------------
SaveOptions(QDomElement & tag,QSharedPointer<VGObject> & obj)554 void VToolSplinePath::SaveOptions(QDomElement &tag, QSharedPointer<VGObject> &obj)
555 {
556 VAbstractSpline::SaveOptions(tag, obj);
557
558 QSharedPointer<VSplinePath> splPath = qSharedPointerDynamicCast<VSplinePath>(obj);
559 SCASSERT(splPath.isNull() == false)
560
561 SetSplinePathAttributes(tag, *splPath);
562 }
563
564 //---------------------------------------------------------------------------------------------------------------------
mousePressEvent(QGraphicsSceneMouseEvent * event)565 void VToolSplinePath::mousePressEvent(QGraphicsSceneMouseEvent *event)
566 {
567 if (flags() & QGraphicsItem::ItemIsMovable)
568 {
569 if (event->button() == Qt::LeftButton && event->type() != QEvent::GraphicsSceneMouseDoubleClick)
570 {
571 oldPosition = event->scenePos();
572 const auto splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
573 splIndex = splPath->Segment(oldPosition);
574 if (IsMovable(splIndex))
575 {
576 SetItemOverrideCursor(this, cursorArrowCloseHand, 1, 1);
577 event->accept();
578 }
579 }
580 }
581 VAbstractSpline::mousePressEvent(event);
582 }
583
584 //---------------------------------------------------------------------------------------------------------------------
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)585 void VToolSplinePath::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
586 {
587 if (flags() & QGraphicsItem::ItemIsMovable)
588 {
589 if (event->button() == Qt::LeftButton && event->type() != QEvent::GraphicsSceneMouseDoubleClick)
590 {
591 oldPosition = event->scenePos();
592 SetItemOverrideCursor(this, cursorArrowOpenHand, 1, 1);
593
594 CurveReleased();
595 }
596 }
597 VAbstractSpline::mouseReleaseEvent(event);
598 }
599
600 //---------------------------------------------------------------------------------------------------------------------
mouseMoveEvent(QGraphicsSceneMouseEvent * event)601 void VToolSplinePath::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
602 {
603 // Don't need to check if left mouse button was pressed. According to the Qt documentation "If you do receive this
604 // event, you can be certain that this item also received a mouse press event, and that this item is the current
605 // mouse grabber.".
606
607 if (IsMovable(splIndex))
608 {
609 const auto oldSplPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
610
611 if (VAbstractApplication::VApp()->Settings()->IsFreeCurveMode() && not moved)
612 {
613 oldMoveSplinePath = QSharedPointer<VSplinePath>::create(*oldSplPath);
614 moved = true;
615 }
616
617 newMoveSplinePath = QSharedPointer<VSplinePath>::create(*oldSplPath);
618
619 VSpline spline = newMoveSplinePath->GetSpline(splIndex);
620 const qreal t = spline.ParamT(oldPosition);
621
622 if (qFloor(t) == -1)
623 {
624 return;
625 }
626
627 // Magic Bezier Drag Equations follow!
628 // "weight" describes how the influence of the drag should be distributed
629 // among the handles; 0 = front handle only, 1 = back handle only.
630
631 double weight;
632 if (t <= 1.0 / 6.0)
633 {
634 weight = 0;
635 }
636 else if (t <= 0.5)
637 {
638 weight = (pow((6 * t - 1) / 2.0, 3)) / 2;
639 }
640 else if (t <= 5.0 / 6.0)
641 {
642 weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
643 }
644 else
645 {
646 weight = 1;
647 }
648
649 const QPointF delta = event->scenePos() - oldPosition;
650 const QPointF offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta;
651 const QPointF offset1 = (weight/(3*t*t*(1-t))) * delta;
652
653 const QPointF p2 = static_cast<QPointF>(spline.GetP2()) + offset0;
654 const QPointF p3 = static_cast<QPointF>(spline.GetP3()) + offset1;
655
656 oldPosition = event->scenePos(); // Now mouse here
657
658 const VSpline spl = VSpline(spline.GetP1(), p2, p3, spline.GetP4());
659
660 UpdateControlPoints(spl, newMoveSplinePath, splIndex);
661
662 if (not VAbstractApplication::VApp()->Settings()->IsFreeCurveMode())
663 {
664 UndoCommandMove(*oldSplPath, *newMoveSplinePath);
665 }
666 else
667 {
668 VAbstractTool::data.UpdateGObject(m_id, newMoveSplinePath);
669 RefreshGeometry();
670
671 if (QGraphicsScene *sc = scene())
672 {
673 VMainGraphicsView::NewSceneRect(sc, VAbstractValApplication::VApp()->getSceneView(), this);
674 }
675 }
676
677 // Each time we move something we call recalculation scene rect. In some cases this can cause moving
678 // objects positions. And this cause infinite redrawing. That's why we wait the finish of saving the last move.
679 static bool changeFinished = true;
680 if (changeFinished)
681 {
682 changeFinished = false;
683
684 const QList<QGraphicsView *> viewList = scene()->views();
685 if (not viewList.isEmpty())
686 {
687 if (VMainGraphicsView *view = qobject_cast<VMainGraphicsView *>(viewList.at(0)))
688 {
689 VMainGraphicsScene *currentScene = qobject_cast<VMainGraphicsScene *>(scene());
690 SCASSERT(currentScene)
691 const QPointF cursorPosition = currentScene->getScenePos();
692 const qreal scale = SceneScale(scene());
693 view->EnsureVisibleWithDelay(QRectF(cursorPosition.x()-5/scale, cursorPosition.y()-5/scale, 10/scale,
694 10/scale),
695 VMainGraphicsView::scrollDelay);
696 }
697 }
698 changeFinished = true;
699 }
700 }
701 }
702
703 //---------------------------------------------------------------------------------------------------------------------
hoverEnterEvent(QGraphicsSceneHoverEvent * event)704 void VToolSplinePath::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
705 {
706 if (m_acceptHoverEvents)
707 {
708 if (flags() & QGraphicsItem::ItemIsMovable)
709 {
710 oldPosition = event->scenePos();
711 const auto splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
712 splIndex = splPath->Segment(oldPosition);
713 if (IsMovable(splIndex))
714 {
715 SetItemOverrideCursor(this, cursorArrowOpenHand, 1, 1);
716 }
717 else
718 {
719 setCursor(VAbstractValApplication::VApp()->getSceneView()->viewport()->cursor());
720 }
721 }
722 else
723 {
724 setCursor(VAbstractValApplication::VApp()->getSceneView()->viewport()->cursor());
725 }
726
727 VAbstractSpline::hoverEnterEvent(event);
728 }
729 else
730 {
731 setCursor(VAbstractValApplication::VApp()->getSceneView()->viewport()->cursor());
732 }
733 }
734
735 //---------------------------------------------------------------------------------------------------------------------
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)736 void VToolSplinePath::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
737 {
738 if (m_acceptHoverEvents)
739 {
740 VAbstractSpline::hoverLeaveEvent(event);
741 }
742 }
743
744 //---------------------------------------------------------------------------------------------------------------------
SetVisualization()745 void VToolSplinePath::SetVisualization()
746 {
747 if (not vis.isNull())
748 {
749 VisToolSplinePath *visual = qobject_cast<VisToolSplinePath *>(vis);
750 SCASSERT(visual != nullptr)
751
752 QSharedPointer<VSplinePath> splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
753 visual->setPath(*splPath.data());
754 visual->setLineStyle(LineStyleToPenStyle(splPath->GetPenStyle()));
755 visual->SetMode(Mode::Show);
756 visual->RefreshGeometry();
757 }
758 }
759
760 //---------------------------------------------------------------------------------------------------------------------
IsMovable(int index) const761 bool VToolSplinePath::IsMovable(int index) const
762 {
763 const auto splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
764
765 if (index < 1 || index > splPath->CountSubSpl())
766 {
767 return false;
768 }
769
770 const VSplinePoint p1 = splPath->GetSplinePoint(index, SplinePointPosition::FirstPoint);
771 const VSplinePoint p2 = splPath->GetSplinePoint(index, SplinePointPosition::LastPoint);
772
773 return p1.IsMovable() && p2.IsMovable();
774 }
775
776 //---------------------------------------------------------------------------------------------------------------------
RefreshCtrlPoints()777 void VToolSplinePath::RefreshCtrlPoints()
778 {
779 // Very important to disable control points. Without it the pogram can't move the curve.
780 for (auto point : qAsConst(controlPoints))
781 {
782 point->setFlag(QGraphicsItem::ItemSendsGeometryChanges, false);
783 }
784
785 const auto splPath = VAbstractTool::data.GeometricObject<VSplinePath>(m_id);
786
787 for (qint32 i = 1; i<=splPath->CountSubSpl(); ++i)
788 {
789 const qint32 j = i*2;
790
791 controlPoints[j-2]->blockSignals(true);
792 controlPoints[j-1]->blockSignals(true);
793
794 const auto spl = splPath->GetSpline(i);
795
796 {
797 bool freeAngle1 = true;
798
799 if (i > 1)
800 {
801 const VSpline prevSpl = splPath->GetSpline(i-1);
802 freeAngle1 = qmu::QmuTokenParser::IsSingle(spl.GetStartAngleFormula()) &&
803 qmu::QmuTokenParser::IsSingle(prevSpl.GetEndAngleFormula());
804 }
805 else
806 {
807 freeAngle1 = qmu::QmuTokenParser::IsSingle(spl.GetStartAngleFormula());
808 }
809
810 const bool freeLength1 = qmu::QmuTokenParser::IsSingle(spl.GetC1LengthFormula());
811
812 const auto splinePoint = spl.GetP1();
813 controlPoints[j-2]->RefreshCtrlPoint(i, SplinePointPosition::FirstPoint, static_cast<QPointF>(spl.GetP2()),
814 static_cast<QPointF>(splinePoint), freeAngle1, freeLength1);
815 }
816
817 {
818 bool freeAngle2 = true;
819
820 if (i < splPath->CountSubSpl())
821 {
822 const VSpline nextSpl = splPath->GetSpline(i+1);
823 freeAngle2 = qmu::QmuTokenParser::IsSingle(nextSpl.GetStartAngleFormula()) &&
824 qmu::QmuTokenParser::IsSingle(spl.GetEndAngleFormula());
825 }
826 else
827 {
828 freeAngle2 = qmu::QmuTokenParser::IsSingle(spl.GetEndAngleFormula());
829 }
830
831 const bool freeLength2 = qmu::QmuTokenParser::IsSingle(spl.GetC2LengthFormula());
832
833 const auto splinePoint = spl.GetP4();
834 controlPoints[j-1]->RefreshCtrlPoint(i, SplinePointPosition::LastPoint, static_cast<QPointF>(spl.GetP3()),
835 static_cast<QPointF>(splinePoint), freeAngle2, freeLength2);
836 }
837
838 controlPoints[j-2]->blockSignals(false);
839 controlPoints[j-1]->blockSignals(false);
840 }
841
842 for (auto point : qAsConst(controlPoints))
843 {
844 point->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
845 }
846 }
847
848 //---------------------------------------------------------------------------------------------------------------------
CurveReleased()849 void VToolSplinePath::CurveReleased()
850 {
851 if (VAbstractApplication::VApp()->Settings()->IsFreeCurveMode() && moved)
852 {
853 UndoCommandMove(*oldMoveSplinePath, *newMoveSplinePath);
854
855 oldMoveSplinePath.clear();
856 newMoveSplinePath.clear();
857
858 moved = false;
859 }
860 }
861