1 /************************************************************************
2  **
3  **  @file   vabstractspline.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   4 3, 2014
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 "vabstractspline.h"
30 
31 #include <QColor>
32 #include <QFlags>
33 #include <QGraphicsScene>
34 #include <QGraphicsSceneMouseEvent>
35 #include <QGuiApplication>
36 #include <QKeyEvent>
37 #include <QLineF>
38 #include <QPen>
39 #include <QSharedPointer>
40 #include <Qt>
41 #include <new>
42 
43 #include "../ifc/exception/vexception.h"
44 #include "../ifc/exception/vexceptionbadid.h"
45 #include "../ifc/xml/vabstractpattern.h"
46 #include "../qmuparser/qmutokenparser.h"
47 #include "../vgeometry/vgobject.h"
48 #include "../vgeometry/vpointf.h"
49 #include "../vgeometry/vspline.h"
50 #include "../vgeometry/vabstractarc.h"
51 #include "../vpatterndb/vcontainer.h"
52 #include "../vwidgets/vcontrolpointspline.h"
53 #include "../../../visualization/line/visline.h"
54 #include "../../vabstracttool.h"
55 #include "../vdrawtool.h"
56 
57 //---------------------------------------------------------------------------------------------------------------------
VAbstractSpline(VAbstractPattern * doc,VContainer * data,quint32 id,const QString & notes,QGraphicsItem * parent)58 VAbstractSpline::VAbstractSpline(VAbstractPattern *doc, VContainer *data, quint32 id, const QString &notes,
59                                  QGraphicsItem *parent)
60     :VDrawTool(doc, data, id, notes),
61       QGraphicsPathItem(parent),
62       controlPoints(),
63       sceneType(SceneObject::Unknown),
64       m_isHovered(false),
65       detailsMode(VAbstractApplication::VApp()->Settings()->IsShowCurveDetails()),
66       m_acceptHoverEvents(true)
67 {
68     InitDefShape();
69     setAcceptHoverEvents(m_acceptHoverEvents);
70 }
71 
72 //---------------------------------------------------------------------------------------------------------------------
shape() const73 QPainterPath VAbstractSpline::shape() const
74 {
75     const QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
76     const QVector<QPointF> points = curve->GetPoints();
77 
78     QPainterPath path;
79     for (qint32 i = 0; i < points.count()-1; ++i)
80     {
81         path.moveTo(points.at(i));
82         path.lineTo(points.at(i+1));
83     }
84 
85     if (m_isHovered || detailsMode)
86     {
87         path.addPath(VAbstractCurve::ShowDirection(curve->DirectionArrows(),
88                                                    ScaleWidth(VAbstractCurve::LengthCurveDirectionArrow(),
89                                                               SceneScale(scene()))));
90     }
91     path.setFillRule(Qt::WindingFill);
92     return ItemShapeFromPath(path, pen());
93 }
94 
95 //---------------------------------------------------------------------------------------------------------------------
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)96 void VAbstractSpline::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
97 {
98     const qreal width = ScaleWidth(m_isHovered ? VAbstractApplication::VApp()->Settings()->WidthMainLine()
99                                                : VAbstractApplication::VApp()->Settings()->WidthHairLine(),
100                                    SceneScale(scene()));
101 
102     const QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
103     setPen(QPen(CorrectColor(this, curve->GetColor()), width, LineStyleToPenStyle(curve->GetPenStyle()), Qt::RoundCap));
104 
105     auto PaintSpline = [this, curve](QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
106     {
107         if (m_isHovered || detailsMode)
108         {
109             painter->save();
110 
111             QPen arrowPen(pen());
112             arrowPen.setStyle(Qt::SolidLine);
113 
114             painter->setPen(arrowPen);
115             painter->setBrush(brush());
116 
117             painter->drawPath(VAbstractCurve::ShowDirection(curve->DirectionArrows(),
118                                                             ScaleWidth(VAbstractCurve::LengthCurveDirectionArrow(),
119                                                                        SceneScale(scene()))));
120 
121             painter->restore();
122         }
123 
124         PaintWithFixItemHighlightSelected<QGraphicsPathItem>(this, painter, option, widget);
125     };
126 
127     PaintSpline(painter, option, widget);
128 }
129 
130 //---------------------------------------------------------------------------------------------------------------------
getTagName() const131 QString VAbstractSpline::getTagName() const
132 {
133     return VAbstractPattern::TagSpline;
134 }
135 
136 //---------------------------------------------------------------------------------------------------------------------
137 /**
138  * @brief FullUpdateFromFile update tool data form file.
139  */
FullUpdateFromFile()140 void VAbstractSpline::FullUpdateFromFile()
141 {
142     ReadAttributes();
143     RefreshGeometry();
144 }
145 
146 //---------------------------------------------------------------------------------------------------------------------
Disable(bool disable,const QString & namePP)147 void VAbstractSpline::Disable(bool disable, const QString &namePP)
148 {
149     const bool enabled = !CorrectDisable(disable, namePP);
150     this->setEnabled(enabled);
151     emit setEnabledPoint(enabled);
152 }
153 
154 //---------------------------------------------------------------------------------------------------------------------
DetailsMode(bool mode)155 void VAbstractSpline::DetailsMode(bool mode)
156 {
157     detailsMode = mode;
158     RefreshGeometry();
159     ShowHandles(detailsMode);
160 }
161 
162 //---------------------------------------------------------------------------------------------------------------------
AllowHover(bool enabled)163 void VAbstractSpline::AllowHover(bool enabled)
164 {
165     // Manually handle hover events. Need for setting cursor for not selectable paths.
166     m_acceptHoverEvents = enabled;
167 
168     for (auto point : qAsConst(controlPoints))
169     {
170         point->setAcceptHoverEvents(enabled);
171     }
172 }
173 
174 //---------------------------------------------------------------------------------------------------------------------
AllowSelecting(bool enabled)175 void VAbstractSpline::AllowSelecting(bool enabled)
176 {
177     setFlag(QGraphicsItem::ItemIsSelectable, enabled);
178 
179     for (auto point : qAsConst(controlPoints))
180     {
181         point->setFlag(QGraphicsItem::ItemIsSelectable, enabled);
182     }
183 }
184 
185 //---------------------------------------------------------------------------------------------------------------------
MakeToolTip() const186 QString VAbstractSpline::MakeToolTip() const
187 {
188     const QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
189 
190     const QString toolTip = QString("<table>"
191                                     "<tr> <td><b>%4:</b> %5</td> </tr>"
192                                     "<tr> <td><b>%1:</b> %2 %3</td> </tr>"
193                                     "</table>")
194             .arg(tr("Length"))
195             .arg(VAbstractValApplication::VApp()->fromPixel(curve->GetLength()))
196             .arg(UnitsToStr(VAbstractValApplication::VApp()->patternUnits(), true), tr("Label"), curve->ObjectName());
197     return toolTip;
198 }
199 
200 //---------------------------------------------------------------------------------------------------------------------
201 /**
202  * @brief ShowTool highlight tool.
203  * @param id object id in container
204  * @param enable enable or disable highlight.
205  */
ShowTool(quint32 id,bool enable)206 void VAbstractSpline::ShowTool(quint32 id, bool enable)
207 {
208     ShowItem(this, id, enable);
209 }
210 
211 //---------------------------------------------------------------------------------------------------------------------
RefreshGeometry()212 void VAbstractSpline::RefreshGeometry()
213 {
214     InitDefShape();
215     RefreshCtrlPoints();
216     SetVisualization();
217 }
218 
219 //---------------------------------------------------------------------------------------------------------------------
220 /**
221  * @brief hoverEnterEvent handle hover enter events.
222  * @param event hover enter event.
223  */
224 // cppcheck-suppress unusedFunction
hoverEnterEvent(QGraphicsSceneHoverEvent * event)225 void VAbstractSpline::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
226 {
227     m_isHovered = true;
228     setToolTip(MakeToolTip());
229     QGraphicsPathItem::hoverEnterEvent(event);
230 }
231 
232 //---------------------------------------------------------------------------------------------------------------------
233 /**
234  * @brief hoverLeaveEvent handle hover leave events.
235  * @param event hover leave event.
236  */
237 // cppcheck-suppress unusedFunction
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)238 void VAbstractSpline::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
239 {
240     m_isHovered = false;
241     QGraphicsPathItem::hoverLeaveEvent(event);
242 }
243 
244 //---------------------------------------------------------------------------------------------------------------------
245 /**
246  * @brief itemChange hadle item change.
247  * @param change change.
248  * @param value value.
249  * @return value.
250  */
itemChange(QGraphicsItem::GraphicsItemChange change,const QVariant & value)251 QVariant VAbstractSpline::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
252 {
253     if (change == QGraphicsItem::ItemSelectedHasChanged)
254     {
255         emit ChangedToolSelection(value.toBool(), m_id, m_id);
256     }
257 
258     return QGraphicsPathItem::itemChange(change, value);
259 }
260 
261 //---------------------------------------------------------------------------------------------------------------------
262 /**
263  * @brief keyReleaseEvent handle key release events.
264  * @param event key release event.
265  */
keyReleaseEvent(QKeyEvent * event)266 void VAbstractSpline::keyReleaseEvent(QKeyEvent *event)
267 {
268     switch (event->key())
269     {
270         case Qt::Key_Delete:
271             try
272             {
273                 DeleteToolWithConfirm();
274             }
275             catch(const VExceptionToolWasDeleted &e)
276             {
277                 Q_UNUSED(e)
278                 return;//Leave this method immediately!!!
279             }
280             break;
281         default:
282             break;
283     }
284     QGraphicsPathItem::keyReleaseEvent ( event );
285 }
286 
287 //---------------------------------------------------------------------------------------------------------------------
mousePressEvent(QGraphicsSceneMouseEvent * event)288 void VAbstractSpline::mousePressEvent(QGraphicsSceneMouseEvent *event)
289 {
290     // Special for not selectable item first need to call standard mousePressEvent then accept event
291     QGraphicsPathItem::mousePressEvent(event);
292 
293     // Somehow clicking on notselectable object do not clean previous selections.
294     if (not (flags() & ItemIsSelectable) && scene())
295     {
296         scene()->clearSelection();
297     }
298 
299     event->accept();// Special for not selectable item first need to call standard mousePressEvent then accept event
300 }
301 
302 //---------------------------------------------------------------------------------------------------------------------
303 /**
304  * @brief mouseReleaseEvent  handle mouse release events.
305  * @param event mouse release event.
306  */
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)307 void VAbstractSpline::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
308 {
309     if (IsSelectedByReleaseEvent(this, event))
310     {
311         emit ChoosedTool(m_id, sceneType);
312     }
313     QGraphicsPathItem::mouseReleaseEvent(event);
314 }
315 
316 //---------------------------------------------------------------------------------------------------------------------
SaveOptions(QDomElement & tag,QSharedPointer<VGObject> & obj)317 void VAbstractSpline::SaveOptions(QDomElement &tag, QSharedPointer<VGObject> &obj)
318 {
319     VDrawTool::SaveOptions(tag, obj);
320 
321     const QSharedPointer<VAbstractCurve> curve = qSharedPointerCast<VAbstractCurve>(obj);
322     doc->SetAttribute(tag, AttrColor, curve->GetColor());
323     doc->SetAttribute(tag, AttrPenStyle, curve->GetPenStyle());
324     doc->SetAttribute(tag, AttrAScale, curve->GetApproximationScale());
325     doc->SetAttributeOrRemoveIf(tag, AttrAlias, curve->GetAliasSuffix(), curve->GetAliasSuffix().isEmpty());
326 }
327 
328 //---------------------------------------------------------------------------------------------------------------------
RefreshCtrlPoints()329 void VAbstractSpline::RefreshCtrlPoints()
330 {
331     // do nothing
332 }
333 
334 //---------------------------------------------------------------------------------------------------------------------
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)335 void VAbstractSpline::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
336 {
337     ShowContextMenu(event);
338 }
339 
340 //---------------------------------------------------------------------------------------------------------------------
CorrectedSpline(const VSpline & spline,const SplinePointPosition & position,const QPointF & pos) const341 VSpline VAbstractSpline::CorrectedSpline(const VSpline &spline, const SplinePointPosition &position,
342                                          const QPointF &pos) const
343 {
344     VSpline spl;
345     if (position == SplinePointPosition::FirstPoint)
346     {
347         QLineF line(static_cast<QPointF>(spline.GetP1()), pos);
348         if (QGuiApplication::keyboardModifiers() == Qt::ShiftModifier)
349         {
350             line.setAngle(VisLine::CorrectAngle(line.angle()));
351         }
352 
353         qreal newAngle1 = line.angle();
354         QString newAngle1F = QString().setNum(newAngle1);
355 
356         qreal newLength1 = line.length();
357         QString newLength1F = QString().setNum(VAbstractValApplication::VApp()->fromPixel(newLength1));
358 
359         if (not qmu::QmuTokenParser::IsSingle(spline.GetStartAngleFormula()))
360         {
361             newAngle1 = spline.GetStartAngle();
362             newAngle1F = spline.GetStartAngleFormula();
363         }
364 
365         if (not qmu::QmuTokenParser::IsSingle(spline.GetC1LengthFormula()))
366         {
367             newLength1 = spline.GetC1Length();
368             newLength1F = spline.GetC1LengthFormula();
369         }
370 
371         spl = VSpline(spline.GetP1(), spline.GetP4(), newAngle1, newAngle1F, spline.GetEndAngle(),
372                       spline.GetEndAngleFormula(), newLength1, newLength1F, spline.GetC2Length(),
373                       spline.GetC2LengthFormula());
374     }
375     else
376     {
377         QLineF line(static_cast<QPointF>(spline.GetP4()), pos);
378         if (QGuiApplication::keyboardModifiers() == Qt::ShiftModifier)
379         {
380             line.setAngle(VisLine::CorrectAngle(line.angle()));
381         }
382 
383         qreal newAngle2 = line.angle();
384         QString newAngle2F = QString().setNum(newAngle2);
385 
386         qreal newLength2 = line.length();
387         QString newLength2F = QString().setNum(VAbstractValApplication::VApp()->fromPixel(newLength2));
388 
389         if (not qmu::QmuTokenParser::IsSingle(spline.GetEndAngleFormula()))
390         {
391             newAngle2 = spline.GetEndAngle();
392             newAngle2F = spline.GetEndAngleFormula();
393         }
394 
395         if (not qmu::QmuTokenParser::IsSingle(spline.GetC2LengthFormula()))
396         {
397             newLength2 = spline.GetC2Length();
398             newLength2F = spline.GetC2LengthFormula();
399         }
400         spl = VSpline(spline.GetP1(), spline.GetP4(), spline.GetStartAngle(), spline.GetStartAngleFormula(),
401                       newAngle2, newAngle2F, spline.GetC1Length(), spline.GetC1LengthFormula(),
402                       newLength2, newLength2F);
403     }
404 
405     return spl;
406 }
407 
408 //---------------------------------------------------------------------------------------------------------------------
CurveSelected(bool selected)409 void VAbstractSpline::CurveSelected(bool selected)
410 {
411     setSelected(selected);
412 
413     for (auto point : qAsConst(controlPoints))
414     {
415         point->blockSignals(true);
416         point->setSelected(selected);
417         point->blockSignals(false);
418     }
419 }
420 
421 //---------------------------------------------------------------------------------------------------------------------
InitDefShape()422 void VAbstractSpline::InitDefShape()
423 {
424     const QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
425     this->setPath(curve->GetPath());
426 }
427 
428 //---------------------------------------------------------------------------------------------------------------------
ShowHandles(bool show)429 void VAbstractSpline::ShowHandles(bool show)
430 {
431     for (auto point : qAsConst(controlPoints))
432     {
433         point->setVisible(show);
434     }
435     update();// Show direction
436 }
437 
438 //---------------------------------------------------------------------------------------------------------------------
GetLineColor() const439 QString VAbstractSpline::GetLineColor() const
440 {
441     const QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
442     return curve->GetColor();
443 }
444 
445 //---------------------------------------------------------------------------------------------------------------------
SetLineColor(const QString & value)446 void VAbstractSpline::SetLineColor(const QString &value)
447 {
448     QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
449     curve->SetColor(value);
450     QSharedPointer<VGObject> obj = qSharedPointerCast<VGObject>(curve);
451     SaveOption(obj);
452 }
453 
454 //---------------------------------------------------------------------------------------------------------------------
GetPenStyle() const455 QString VAbstractSpline::GetPenStyle() const
456 {
457     const QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
458     return curve->GetPenStyle();
459 }
460 
461 //---------------------------------------------------------------------------------------------------------------------
SetPenStyle(const QString & value)462 void VAbstractSpline::SetPenStyle(const QString &value)
463 {
464     QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
465     curve->SetPenStyle(value);
466     QSharedPointer<VGObject> obj = qSharedPointerCast<VGObject>(curve);
467     SaveOption(obj);
468 }
469 
470 //---------------------------------------------------------------------------------------------------------------------
name() const471 QString VAbstractSpline::name() const
472 {
473     return ObjectName<VAbstractCurve>(m_id);
474 }
475 
476 //---------------------------------------------------------------------------------------------------------------------
GetApproximationScale() const477 qreal VAbstractSpline::GetApproximationScale() const
478 {
479     return VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id)->GetApproximationScale();
480 }
481 
482 //---------------------------------------------------------------------------------------------------------------------
GetDuplicate() const483 quint32 VAbstractSpline::GetDuplicate() const
484 {
485     return VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id)->GetDuplicate();
486 }
487 
488 //---------------------------------------------------------------------------------------------------------------------
GetAliasSuffix() const489 QString VAbstractSpline::GetAliasSuffix() const
490 {
491     return ObjectAliasSuffix<VAbstractCurve>(m_id);
492 }
493 
494 //---------------------------------------------------------------------------------------------------------------------
SetAliasSuffix(QString alias)495 void VAbstractSpline::SetAliasSuffix(QString alias)
496 {
497     QSharedPointer<VAbstractCurve> curve = VAbstractTool::data.GeometricObject<VAbstractCurve>(m_id);
498 
499     const QString oldAliasSuffix = curve->GetAliasSuffix();
500     alias = alias.simplified().replace(QChar(QChar::Space), QChar('_'));
501     curve->SetAliasSuffix(alias);
502 
503     QRegularExpression rx(NameRegExp());
504 
505     if (alias.isEmpty() || (rx.match(curve->GetAlias()).hasMatch() && VAbstractTool::data.IsUnique(curve->GetAlias())))
506     {
507         QSharedPointer<VGObject> obj = qSharedPointerCast<VGObject>(curve);
508         SaveOption(obj);
509     }
510     else
511     {
512         curve->SetAliasSuffix(oldAliasSuffix);
513     }
514 }
515 
516 //---------------------------------------------------------------------------------------------------------------------
GroupVisibility(quint32 object,bool visible)517 void VAbstractSpline::GroupVisibility(quint32 object, bool visible)
518 {
519     Q_UNUSED(object)
520     setVisible(visible);
521 }
522 
523 // VToolAbstractArc
524 //---------------------------------------------------------------------------------------------------------------------
VToolAbstractArc(VAbstractPattern * doc,VContainer * data,quint32 id,const QString & notes,QGraphicsItem * parent)525 VToolAbstractArc::VToolAbstractArc(VAbstractPattern *doc, VContainer *data, quint32 id, const QString &notes,
526                                    QGraphicsItem *parent)
527     : VAbstractSpline(doc, data, id, notes, parent)
528 {}
529 
530 //---------------------------------------------------------------------------------------------------------------------
CenterPointName() const531 QString VToolAbstractArc::CenterPointName() const
532 {
533     QSharedPointer<VAbstractArc> arc = VAbstractTool::data.GeometricObject<VAbstractArc>(m_id);
534     SCASSERT(arc.isNull() == false)
535 
536     return VAbstractTool::data.GetGObject(arc->GetCenter().id())->name();
537 }
538