1 /************************************************************************
2  **
3  **  @file
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   22 11, 2016
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) 2016 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 "vpiecepath.h"
30 #include "vpiecepath_p.h"
31 #include "vcontainer.h"
32 #include "../vgeometry/vpointf.h"
33 #include "../vlayout/vabstractpiece.h"
34 #include "calculator.h"
35 #include "../vmisc/vabstractvalapplication.h"
36 #include "../vmisc/compatibility.h"
37 #include "../ifc/exception/vexceptionobjecterror.h"
38 
39 #include <qnumeric.h>
40 #include <QPainterPath>
41 
42 namespace
43 {
44 //---------------------------------------------------------------------------------------------------------------------
CurvePoint(VSAPoint candidate,const VContainer * data,const VPieceNode & node,const QVector<QPointF> & curvePoints)45 VSAPoint CurvePoint(VSAPoint candidate, const VContainer *data, const VPieceNode &node,
46                     const QVector<QPointF> &curvePoints)
47 {
48     if (node.GetTypeTool() == Tool::NodePoint)
49     {
50         const QPointF p = static_cast<QPointF>(*data->GeometricObject<VPointF>(node.GetId()));
51         if (VAbstractCurve::IsPointOnCurve(curvePoints, p))
52         {
53             candidate = VSAPoint(p);
54             candidate.SetSAAfter(node.GetSAAfter(data, *data->GetPatternUnit()));
55             candidate.SetSABefore(node.GetSABefore(data, *data->GetPatternUnit()));
56             candidate.SetAngleType(node.GetAngleType());
57         }
58     }
59     return candidate;
60 }
61 
62 //---------------------------------------------------------------------------------------------------------------------
CurveStartPoint(VSAPoint candidate,const VContainer * data,const VPieceNode & node,const QVector<QPointF> & curvePoints)63 VSAPoint CurveStartPoint(VSAPoint candidate, const VContainer *data, const VPieceNode &node,
64                          const QVector<QPointF> &curvePoints)
65 {
66     if (node.GetTypeTool() == Tool::NodePoint)
67     {
68         return CurvePoint(candidate, data, node, curvePoints);
69     }
70     else
71     {
72         // See issue #620. Detail path not correct. Previous curve also should cut segment.
73         const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(node.GetId());
74 
75         const QVector<QPointF> points = curve->GetPoints();
76         if (not points.isEmpty())
77         {
78             QPointF end; // Last point for this curve show start of next segment
79             node.GetReverse() ? end = points.first() : end = points.last();
80             if (VAbstractCurve::IsPointOnCurve(curvePoints, end))
81             {
82                 candidate = VSAPoint(end);
83             }
84         }
85     }
86     return candidate;
87 }
88 
89 //---------------------------------------------------------------------------------------------------------------------
CurveEndPoint(VSAPoint candidate,const VContainer * data,const VPieceNode & node,const QVector<QPointF> & curvePoints)90 VSAPoint CurveEndPoint(VSAPoint candidate, const VContainer *data, const VPieceNode &node,
91                        const QVector<QPointF> &curvePoints)
92 {
93     if (node.GetTypeTool() == Tool::NodePoint)
94     {
95         return CurvePoint(candidate, data, node, curvePoints);
96     }
97     else
98     {
99         // See issue #620. Detail path not correct. Previous curve also should cut segment.
100         const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(node.GetId());
101 
102         const QVector<QPointF> points = curve->GetPoints();
103         if (not points.isEmpty())
104         {
105             QPointF begin;// First point for this curve show finish of previous segment
106             node.GetReverse() ? begin = points.last() : begin = points.first();
107             if (VAbstractCurve::IsPointOnCurve(curvePoints, begin))
108             {
109                 candidate = VSAPoint(begin);
110             }
111         }
112     }
113     return candidate;
114 }
115 
116 //---------------------------------------------------------------------------------------------------------------------
117 /**
118  * @brief indexOfNode return index in list node using id object.
119  * @param list list nodes detail.
120  * @param id object (arc, point, spline, splinePath) id.
121  * @return index in list or -1 id can't find.
122  */
IndexOfNode(const QVector<VPieceNode> & list,quint32 id)123 int IndexOfNode(const QVector<VPieceNode> &list, quint32 id)
124 {
125     for (int i = 0; i < list.size(); ++i)
126     {
127         if (list.at(i).GetId() == id)
128         {
129             return i;
130         }
131     }
132     qDebug()<<"Can't find node.";
133     return -1;
134 }
135 
136 //---------------------------------------------------------------------------------------------------------------------
MakePainterPath(const QVector<QPointF> & points)137 QPainterPath MakePainterPath(const QVector<QPointF> &points)
138 {
139     QPainterPath path;
140 
141     if (not points.isEmpty())
142     {
143         path.addPolygon(QPolygonF(points));
144         path.setFillRule(Qt::WindingFill);
145     }
146 
147     return path;
148 }
149 
150 //---------------------------------------------------------------------------------------------------------------------
FindTipDirection(const QVector<QPointF> & points)151 qreal FindTipDirection(const QVector<QPointF> &points)
152 {
153     if (points.size() <= 1)
154     {
155         return 0;
156     }
157 
158     const QPointF first = points.first();
159 
160     for(int i = 1; i < points.size(); ++i)
161     {
162         if (first != points.at(i))
163         {
164             QLineF line(first, points.at(i));
165             line.setAngle(line.angle() + 180);
166             return line.angle();
167         }
168     }
169 
170     return 0;
171 }
172 
173 //---------------------------------------------------------------------------------------------------------------------
IntersectionWithCuttingCountour(const QVector<QPointF> & cuttingPath,const QVector<QPointF> & points,QPointF * firstConnection)174 bool IntersectionWithCuttingCountour(const QVector<QPointF> &cuttingPath, const QVector<QPointF> &points,
175                                      QPointF *firstConnection)
176 {
177     if (points.size() <= 1)
178     {
179         return false;
180     }
181 
182     const QPointF first = points.first();
183 
184     if (VAbstractCurve::IsPointOnCurve(cuttingPath, first))
185     { // Point is already part of a cutting countour
186         *firstConnection = first;
187         return true;
188     }
189     else
190     {
191         return VAbstractCurve::CurveIntersectAxis(first, FindTipDirection(points), cuttingPath, firstConnection);
192     }
193 }
194 }
195 
196 //---------------------------------------------------------------------------------------------------------------------
VPiecePath()197 VPiecePath::VPiecePath()
198     : d(new VPiecePathData)
199 {}
200 
201 //---------------------------------------------------------------------------------------------------------------------
VPiecePath(PiecePathType type)202 VPiecePath::VPiecePath(PiecePathType type)
203     : d(new VPiecePathData(type))
204 {}
205 
206 //---------------------------------------------------------------------------------------------------------------------
VPiecePath(const VPiecePath & path)207 VPiecePath::VPiecePath(const VPiecePath &path)
208     : d (path.d)
209 {}
210 
211 //---------------------------------------------------------------------------------------------------------------------
operator =(const VPiecePath & path)212 VPiecePath &VPiecePath::operator=(const VPiecePath &path)
213 {
214     if ( &path == this )
215     {
216         return *this;
217     }
218     d = path.d;
219     return *this;
220 }
221 
222 #ifdef Q_COMPILER_RVALUE_REFS
223 //---------------------------------------------------------------------------------------------------------------------
VPiecePath(const VPiecePath && path)224 VPiecePath::VPiecePath(const VPiecePath &&path) Q_DECL_NOTHROW
225     : d (path.d)
226 {}
227 
228 //---------------------------------------------------------------------------------------------------------------------
operator =(VPiecePath && path)229 VPiecePath &VPiecePath::operator=(VPiecePath &&path) Q_DECL_NOTHROW
230 {
231     std::swap(d, path.d);
232     return *this;
233 }
234 #endif
235 
236 //---------------------------------------------------------------------------------------------------------------------
~VPiecePath()237 VPiecePath::~VPiecePath()
238 {}
239 
240 //---------------------------------------------------------------------------------------------------------------------
Append(const VPieceNode & node)241 void VPiecePath::Append(const VPieceNode &node)
242 {
243     d->m_nodes.append(node);
244 }
245 
246 //---------------------------------------------------------------------------------------------------------------------
Clear()247 void VPiecePath::Clear()
248 {
249     d->m_nodes.clear();
250 }
251 
252 //---------------------------------------------------------------------------------------------------------------------
CountNodes() const253 qint32 VPiecePath::CountNodes() const
254 {
255     return d->m_nodes.size();
256 }
257 
258 //---------------------------------------------------------------------------------------------------------------------
operator [](int indx)259 VPieceNode &VPiecePath::operator[](int indx)
260 {
261     return d->m_nodes[indx];
262 }
263 
264 //---------------------------------------------------------------------------------------------------------------------
at(int indx) const265 const VPieceNode &VPiecePath::at(int indx) const
266 {
267     return d->m_nodes.at(indx);
268 }
269 
270 //---------------------------------------------------------------------------------------------------------------------
GetNodes() const271 QVector<VPieceNode> VPiecePath::GetNodes() const
272 {
273     return d->m_nodes;
274 }
275 
276 //---------------------------------------------------------------------------------------------------------------------
SetNodes(const QVector<VPieceNode> & nodes)277 void VPiecePath::SetNodes(const QVector<VPieceNode> &nodes)
278 {
279     d->m_nodes = nodes;
280 }
281 
282 //---------------------------------------------------------------------------------------------------------------------
GetType() const283 PiecePathType VPiecePath::GetType() const
284 {
285     return d->m_type;
286 }
287 
288 //---------------------------------------------------------------------------------------------------------------------
SetType(PiecePathType type)289 void VPiecePath::SetType(PiecePathType type)
290 {
291     d->m_type = type;
292 }
293 
294 //---------------------------------------------------------------------------------------------------------------------
GetName() const295 QString VPiecePath::GetName() const
296 {
297     return d->m_name;
298 }
299 
300 //---------------------------------------------------------------------------------------------------------------------
SetName(const QString & name)301 void VPiecePath::SetName(const QString &name)
302 {
303     d->m_name = name;
304 }
305 
306 //---------------------------------------------------------------------------------------------------------------------
GetPenType() const307 Qt::PenStyle VPiecePath::GetPenType() const
308 {
309     return d->m_penType;
310 }
311 
312 //---------------------------------------------------------------------------------------------------------------------
SetPenType(const Qt::PenStyle & type)313 void VPiecePath::SetPenType(const Qt::PenStyle &type)
314 {
315     d->m_penType = type;
316 }
317 
318 //---------------------------------------------------------------------------------------------------------------------
IsCutPath() const319 bool VPiecePath::IsCutPath() const
320 {
321     return d->m_cut;
322 }
323 
324 //---------------------------------------------------------------------------------------------------------------------
SetCutPath(bool cut)325 void VPiecePath::SetCutPath(bool cut)
326 {
327     d->m_cut = cut;
328 }
329 
330 //---------------------------------------------------------------------------------------------------------------------
GetVisibilityTrigger() const331 QString VPiecePath::GetVisibilityTrigger() const
332 {
333     return d->m_visibilityTrigger;
334 }
335 
336 //---------------------------------------------------------------------------------------------------------------------
SetVisibilityTrigger(const QString & formula)337 void VPiecePath::SetVisibilityTrigger(const QString &formula)
338 {
339     d->m_visibilityTrigger = formula;
340 }
341 
342 //---------------------------------------------------------------------------------------------------------------------
SetFirstToCuttingCountour(bool value)343 void VPiecePath::SetFirstToCuttingCountour(bool value)
344 {
345     d->m_firstToCuttingCountour = value;
346 }
347 
348 //---------------------------------------------------------------------------------------------------------------------
IsFirstToCuttingCountour() const349 bool VPiecePath::IsFirstToCuttingCountour() const
350 {
351     return d->m_firstToCuttingCountour;
352 }
353 
354 //---------------------------------------------------------------------------------------------------------------------
SetLastToCuttingCountour(bool value)355 void VPiecePath::SetLastToCuttingCountour(bool value)
356 {
357     d->m_lastToCuttingCountour = value;
358 }
359 
360 //---------------------------------------------------------------------------------------------------------------------
IsLastToCuttingCountour() const361 bool VPiecePath::IsLastToCuttingCountour() const
362 {
363     return d->m_lastToCuttingCountour;
364 }
365 
366 //---------------------------------------------------------------------------------------------------------------------
PathPoints(const VContainer * data,const QVector<QPointF> & cuttingPath) const367 QVector<QPointF> VPiecePath::PathPoints(const VContainer *data, const QVector<QPointF> &cuttingPath) const
368 {
369     QVector<QPointF> points = NodesToPoints(data, d->m_nodes, GetName());
370 
371     if (GetType() == PiecePathType::InternalPath && not cuttingPath.isEmpty() && points.size() > 1)
372     {
373         QVector<QPointF> extended = points;
374 
375         if (IsFirstToCuttingCountour())
376         {
377             QPointF firstConnection;
378             if (IntersectionWithCuttingCountour(cuttingPath, points, &firstConnection))
379             {
380                 extended.prepend(firstConnection);
381             }
382             else
383             {
384                 const QString errorMsg = QObject::tr("Error in internal path '%1'. There is no intersection of first "
385                                                      "point with cutting countour")
386                         .arg(GetName());
387                 VAbstractApplication::VApp()->IsPedantic() ? throw VExceptionObjectError(errorMsg) :
388                                               qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
389             }
390         }
391 
392         if (IsLastToCuttingCountour())
393         {
394             QPointF lastConnection;
395             if (IntersectionWithCuttingCountour(cuttingPath, Reverse(points), &lastConnection))
396             {
397                 extended.append(lastConnection);
398             }
399             else
400             {
401                 const QString errorMsg = QObject::tr("Error in internal path '%1'. There is no intersection of last "
402                                                      "point with cutting countour")
403                         .arg(GetName());
404                 VAbstractApplication::VApp()->IsPedantic() ? throw VExceptionObjectError(errorMsg) :
405                                               qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
406             }
407         }
408 
409         points = extended;
410     }
411 
412     return points;
413 }
414 
415 //---------------------------------------------------------------------------------------------------------------------
PathNodePoints(const VContainer * data,bool showExcluded) const416 QVector<VPointF> VPiecePath::PathNodePoints(const VContainer *data, bool showExcluded) const
417 {
418     QVector<VPointF> points;
419     for (int i = 0; i < CountNodes(); ++i)
420     {
421         switch (at(i).GetTypeTool())
422         {
423             case Tool::NodePoint:
424             {
425                 if (showExcluded || not at(i).IsExcluded())
426                 {
427                     const QSharedPointer<VPointF> point = data->GeometricObject<VPointF>(at(i).GetId());
428                     points.append(*point);
429                 }
430             }
431             break;
432             case Tool::NodeArc:
433             case Tool::NodeElArc:
434             case Tool::NodeSpline:
435             case Tool::NodeSplinePath:
436             default:
437                 break;
438         }
439     }
440 
441     return points;
442 }
443 
444 //---------------------------------------------------------------------------------------------------------------------
PathCurvePoints(const VContainer * data) const445 QVector<QVector<QPointF> > VPiecePath::PathCurvePoints(const VContainer *data) const
446 {
447     QVector<QVector<QPointF> > curves;
448     for (int i = 0; i < CountNodes(); ++i)
449     {
450         if (at(i).IsExcluded())
451         {
452             continue;// skip excluded node
453         }
454 
455         switch (at(i).GetTypeTool())
456         {
457             case (Tool::NodeArc):
458             case (Tool::NodeElArc):
459             case (Tool::NodeSpline):
460             case (Tool::NodeSplinePath):
461             {
462                 const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(at(i).GetId());
463 
464                 const QPointF begin = StartSegment(data, i, at(i).GetReverse());
465                 const QPointF end = EndSegment(data, i, at(i).GetReverse());
466 
467                 curves.append(curve->GetSegmentPoints(begin, end, at(i).GetReverse(), GetName()));
468                 break;
469             }
470             case (Tool::NodePoint):
471             default:
472                 break;
473         }
474     }
475 
476     return curves;
477 }
478 
479 //---------------------------------------------------------------------------------------------------------------------
SeamAllowancePoints(const VContainer * data,qreal width,bool reverse) const480 QVector<VSAPoint> VPiecePath::SeamAllowancePoints(const VContainer *data, qreal width, bool reverse) const
481 {
482     SCASSERT(data != nullptr);
483 
484     QVector<VSAPoint> pointsEkv;
485     for (int i = 0; i< d->m_nodes.size(); ++i)
486     {
487         const VPieceNode &node = d->m_nodes.at(i);
488         switch (node.GetTypeTool())
489         {
490             case (Tool::NodePoint):
491             {
492                 pointsEkv.append(PreparePointEkv(node, data));
493             }
494             break;
495             case (Tool::NodeArc):
496             case (Tool::NodeElArc):
497             case (Tool::NodeSpline):
498             case (Tool::NodeSplinePath):
499             {
500                 const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(node.GetId());
501                 pointsEkv += CurveSeamAllowanceSegment(data, d->m_nodes, curve, i, node.GetReverse(), width, GetName());
502             }
503             break;
504             default:
505                 qDebug()<<"Get wrong tool type. Ignore."<< static_cast<char>(node.GetTypeTool());
506                 break;
507         }
508     }
509 
510     if (reverse)
511     {
512         pointsEkv = Reverse(pointsEkv);
513     }
514 
515     return pointsEkv;
516 }
517 
518 //---------------------------------------------------------------------------------------------------------------------
PainterPath(const VContainer * data,const QVector<QPointF> & cuttingPath) const519 QPainterPath VPiecePath::PainterPath(const VContainer *data, const QVector<QPointF> &cuttingPath) const
520 {
521     return MakePainterPath(PathPoints(data, cuttingPath));
522 }
523 
524 //---------------------------------------------------------------------------------------------------------------------
CurvesPainterPath(const VContainer * data) const525 QVector<QPainterPath> VPiecePath::CurvesPainterPath(const VContainer *data) const
526 {
527     const QVector<QVector<QPointF> > curves = PathCurvePoints(data);
528     QVector<QPainterPath> paths;
529     paths.reserve(curves.size());
530 
531     for(auto &curve : curves)
532     {
533         paths.append(MakePainterPath(curve));
534     }
535     return paths;
536 }
537 
538 //---------------------------------------------------------------------------------------------------------------------
StartSegment(const VContainer * data,const QVector<VPieceNode> & nodes,int i,bool reverse)539 VSAPoint VPiecePath::StartSegment(const VContainer *data, const QVector<VPieceNode> &nodes, int i, bool reverse)
540 {
541     if (i < 0 || i > nodes.size()-1)
542     {
543         return VSAPoint();
544     }
545 
546     const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(nodes.at(i).GetId());
547 
548     const QVector<QPointF> points = curve->GetPoints();
549     if (points.isEmpty())
550     {
551         return VSAPoint();
552     }
553 
554     VSAPoint begin;
555     reverse ? begin = VSAPoint(points.last()) : begin = VSAPoint(points.first());
556 
557     if (nodes.size() > 1)
558     {
559         const int index = FindInLoopNotExcludedUp(i, nodes);
560 
561         if (index != i && index != -1)
562         {
563             begin = CurveStartPoint(begin, data, nodes.at(index), points);
564         }
565     }
566     return begin;
567 }
568 
569 //---------------------------------------------------------------------------------------------------------------------
EndSegment(const VContainer * data,const QVector<VPieceNode> & nodes,int i,bool reverse)570 VSAPoint VPiecePath::EndSegment(const VContainer *data, const QVector<VPieceNode> &nodes, int i, bool reverse)
571 {
572     if (i < 0 || i > nodes.size()-1)
573     {
574         return VSAPoint();
575     }
576 
577     const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(nodes.at(i).GetId());
578 
579     const QVector<QPointF> points = curve->GetPoints();
580     if (points.isEmpty())
581     {
582         return VSAPoint();
583     }
584 
585     VSAPoint end;
586     reverse ? end = VSAPoint(points.first()) : end = VSAPoint(points.last());
587 
588     if (nodes.size() > 2)
589     {
590         const int index = FindInLoopNotExcludedDown(i, nodes);
591 
592         if (index != i && index != -1)
593         {
594             end = CurveEndPoint(end, data, nodes.at(index), points);
595         }
596     }
597     return end;
598 }
599 
600 //---------------------------------------------------------------------------------------------------------------------
Dependencies() const601 QList<quint32> VPiecePath::Dependencies() const
602 {
603     QList<quint32> list;
604     list.reserve(d->m_nodes.size());
605     for (auto &node : d->m_nodes)
606     {
607         list.append(node.GetId());
608     }
609     return list;
610 }
611 
612 //---------------------------------------------------------------------------------------------------------------------
MissingNodes(const VPiecePath & path) const613 QVector<quint32> VPiecePath::MissingNodes(const VPiecePath &path) const
614 {
615     if (d->m_nodes.size() == path.CountNodes()) //-V807
616     {
617         return QVector<quint32>();
618     }
619 
620     QSet<quint32> set1;
621     for (qint32 i = 0; i < d->m_nodes.size(); ++i)
622     {
623         set1.insert(d->m_nodes.at(i).GetId());
624     }
625 
626     QSet<quint32> set2;
627     for (qint32 j = 0; j < path.CountNodes(); ++j)
628     {
629         set2.insert(path.at(j).GetId());
630     }
631 
632     const QList<quint32> set3 = set1.subtract(set2).values();
633     QVector<quint32> nodes;
634     nodes.reserve(set3.size());
635     for (qint32 i = 0; i < set3.size(); ++i)
636     {
637         const int index = indexOfNode(set3.at(i));
638         if (index != -1)
639         {
640             nodes.append(d->m_nodes.at(index).GetId());
641         }
642     }
643 
644     return nodes;
645 }
646 
647 //---------------------------------------------------------------------------------------------------------------------
NodeName(int nodeIndex,const VContainer * data) const648 QString VPiecePath::NodeName(int nodeIndex, const VContainer *data) const
649 {
650     return NodeName(d->m_nodes, nodeIndex, data);
651 }
652 
653 //---------------------------------------------------------------------------------------------------------------------
indexOfNode(quint32 id) const654 int VPiecePath::indexOfNode(quint32 id) const
655 {
656     return indexOfNode(d->m_nodes, id);
657 }
658 
659 //---------------------------------------------------------------------------------------------------------------------
660 /**
661  * @brief NodeOnEdge return nodes located on edge with index.
662  * @param index index of edge.
663  * @param p1 first node.
664  * @param p2 second node.
665  */
NodeOnEdge(quint32 index,VPieceNode & p1,VPieceNode & p2) const666 void VPiecePath::NodeOnEdge(quint32 index, VPieceNode &p1, VPieceNode &p2) const
667 {
668     const QVector<VPieceNode> list = ListNodePoint();
669     if (index > static_cast<quint32>(list.size()))
670     {
671         qDebug()<<"Wrong edge index index ="<<index;
672         return;
673     }
674     p1 = list.at(static_cast<int>(index));
675     if (index + 1 > static_cast<quint32>(list.size()) - 1)
676     {
677         p2 = list.at(0);
678     }
679     else
680     {
681         p2 = list.at(static_cast<int>(index+1));
682     }
683 }
684 
685 //---------------------------------------------------------------------------------------------------------------------
Contains(quint32 id) const686 bool VPiecePath::Contains(quint32 id) const
687 {
688     for (auto &node : d->m_nodes)
689     {
690         if (node.GetId() == id)
691         {
692             return true;
693         }
694     }
695     return false;
696 }
697 
698 //---------------------------------------------------------------------------------------------------------------------
699 /**
700  * @brief OnEdge checks if two poins located on the edge. Edge is line between two points. If between two points
701  * located arcs or splines ignore this.
702  * @param p1 id first point.
703  * @param p2 id second point.
704  * @return true - on edge, false - no.
705  */
OnEdge(quint32 p1,quint32 p2) const706 bool VPiecePath::OnEdge(quint32 p1, quint32 p2) const
707 {
708     const QVector<VPieceNode> list = ListNodePoint();
709     if (list.size() < 2)
710     {
711         qDebug()<<"Not enough points.";
712         return false;
713     }
714     int i = IndexOfNode(list, p1);
715     int j1 = 0, j2 = 0;
716 
717     if (i == list.size() - 1)
718     {
719         j1 = i-1;
720         j2 = 0;
721     }
722     else if (i == 0)
723     {
724         j1 = list.size() - 1;
725         j2 = i + 1;
726     }
727     else
728     {
729         j1 = i - 1;
730         j2 = i + 1;
731     }
732 
733     if (list.at(j1).GetId() == p2 || list.at(j2).GetId() == p2)
734     {
735         return true;
736     }
737     else
738     {
739         return false;
740     }
741 }
742 
743 //---------------------------------------------------------------------------------------------------------------------
744 /**
745  * @brief Edge return edge index in detail. Edge is line between two points. If between two points
746  * located arcs or splines ignore this.
747  * @param p1 id first point.
748  * @param p2 id second point.
749  * @return edge index or -1 if points don't located on edge
750  */
Edge(quint32 p1,quint32 p2) const751 int VPiecePath::Edge(quint32 p1, quint32 p2) const
752 {
753     if (OnEdge(p1, p2) == false)
754     {
755         qDebug()<<"Points don't on edge.";
756         return -1;
757     }
758 
759     const QVector<VPieceNode> list = ListNodePoint();
760     int i = IndexOfNode(list, p1);
761     int j = IndexOfNode(list, p2);
762 
763     int min = qMin(i, j);
764 
765     if (min == 0 && (i == list.size() - 1 || j == list.size() - 1))
766     {
767         return list.size() - 1;
768     }
769     else
770     {
771         return min;
772     }
773 }
774 
775 //---------------------------------------------------------------------------------------------------------------------
776 /**
777  * @brief listNodePoint return list nodes only with points.
778  * @return list points node.
779  */
ListNodePoint() const780 QVector<VPieceNode> VPiecePath::ListNodePoint() const
781 {
782     QVector<VPieceNode> list;
783     for (auto &node : d->m_nodes) //-V807
784     {
785         if (node.GetTypeTool() == Tool::NodePoint)
786         {
787             list.append(node);
788         }
789     }
790     return list;
791 }
792 
793 //---------------------------------------------------------------------------------------------------------------------
794 /**
795  * @brief RemoveEdge return path without edge with index.
796  * @param index idex of edge.
797  * @return path without edge with index.
798  */
RemoveEdge(quint32 index) const799 VPiecePath VPiecePath::RemoveEdge(quint32 index) const
800 {
801     VPiecePath path(*this);
802     path.Clear();
803 
804     // Edge can be only segment. We ignore all curves inside segments.
805     const quint32 edges = static_cast<quint32>(ListNodePoint().size());
806     for (quint32 i=0; i<edges; ++i)
807     {
808         VPieceNode p1;
809         VPieceNode p2;
810         this->NodeOnEdge(i, p1, p2);
811         const int j1 = this->indexOfNode(p1.GetId());
812 
813         if (i == index)
814         {
815             path.Append(this->at(j1));
816         }
817         else
818         {
819             const int j2 = this->indexOfNode(p2.GetId());
820             int j = j1;
821             do
822             {
823                 // Add "segment" except last point. Inside can be curves too.
824                 path.Append(this->at(j));
825                 ++j;
826 
827                 if (j2 < j1 && j == this->CountNodes())
828                 {
829                     j = 0;
830                 }
831             }
832             while (j != j2);
833         }
834     }
835     return path;
836 }
837 
838 //---------------------------------------------------------------------------------------------------------------------
StartSegment(const VContainer * data,int i,bool reverse) const839 VSAPoint VPiecePath::StartSegment(const VContainer *data, int i, bool reverse) const
840 {
841     return StartSegment(data, d->m_nodes, i, reverse);
842 }
843 
844 //---------------------------------------------------------------------------------------------------------------------
EndSegment(const VContainer * data,int i,bool reverse) const845 VSAPoint VPiecePath::EndSegment(const VContainer *data, int i, bool reverse) const
846 {
847     return EndSegment(data, d->m_nodes, i, reverse);
848 }
849 
850 //---------------------------------------------------------------------------------------------------------------------
NodePreviousPoint(const VContainer * data,int i) const851 QPointF VPiecePath::NodePreviousPoint(const VContainer *data, int i) const
852 {
853     if (i < 0 || i > d->m_nodes.size()-1)
854     {
855         return QPointF();
856     }
857 
858     if (d->m_nodes.size() > 1)
859     {
860         int index = 0;
861         if (i == 0)
862         {
863             index = d->m_nodes.size()-1;
864         }
865         else
866         {
867             index = i-1;
868         }
869 
870         const VPieceNode &node = d->m_nodes.at(index);
871         switch (node.GetTypeTool())
872         {
873             case (Tool::NodePoint):
874                 return static_cast<QPointF>(*data->GeometricObject<VPointF>(node.GetId()));
875             case (Tool::NodeArc):
876             case (Tool::NodeElArc):
877             case (Tool::NodeSpline):
878             case (Tool::NodeSplinePath):
879             {
880                 const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(node.GetId());
881 
882                 const VSAPoint begin = StartSegment(data, d->m_nodes, index, node.GetReverse());
883                 const VSAPoint end = EndSegment(data, d->m_nodes, index, node.GetReverse());
884 
885                 const QVector<QPointF> points = curve->GetSegmentPoints(begin, end, node.GetReverse(), GetName());
886                 if (points.size() > 1)
887                 {
888                     return points.at(points.size()-2);
889                 }
890             }
891                 break;
892             default:
893                 qDebug()<<"Get wrong tool type. Ignore."<< static_cast<char>(node.GetTypeTool());
894                 break;
895         }
896     }
897 
898     return QPointF();
899 }
900 
901 //---------------------------------------------------------------------------------------------------------------------
NodeNextPoint(const VContainer * data,int i) const902 QPointF VPiecePath::NodeNextPoint(const VContainer *data, int i) const
903 {
904     QPointF point;
905     if (i < 0 || i > d->m_nodes.size()-1)
906     {
907         return point;
908     }
909 
910     if (d->m_nodes.size() > 1)
911     {
912         int index = 0;
913         if (i == d->m_nodes.size() - 1)
914         {
915             index = 0;
916         }
917         else
918         {
919             index = i+1;
920         }
921 
922         const VPieceNode &node = d->m_nodes.at(index);
923         switch (node.GetTypeTool())
924         {
925             case (Tool::NodePoint):
926                 return static_cast<QPointF>(*data->GeometricObject<VPointF>(node.GetId()));
927             case (Tool::NodeArc):
928             case (Tool::NodeElArc):
929             case (Tool::NodeSpline):
930             case (Tool::NodeSplinePath):
931             {
932                 const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(node.GetId());
933 
934                 const VSAPoint begin = StartSegment(data, d->m_nodes, index, node.GetReverse());
935                 const VSAPoint end = EndSegment(data, d->m_nodes, index, node.GetReverse());
936 
937                 const QVector<QPointF> points = curve->GetSegmentPoints(begin, end, node.GetReverse(), GetName());
938                 if (points.size() > 1)
939                 {
940                     return points.at(1);
941                 }
942             }
943                 break;
944             default:
945                 qDebug()<<"Get wrong tool type. Ignore."<< static_cast<char>(node.GetTypeTool());
946                 break;
947         }
948     }
949 
950     return point;
951 }
952 
953 //---------------------------------------------------------------------------------------------------------------------
IsVisible(const QHash<QString,QSharedPointer<VInternalVariable>> * vars) const954 bool VPiecePath::IsVisible(const QHash<QString, QSharedPointer<VInternalVariable>> *vars) const
955 {
956     SCASSERT(vars != nullptr)
957     bool visible = true;
958     try
959     {
960         QScopedPointer<Calculator> cal(new Calculator());
961         const qreal result = cal->EvalFormula(vars, GetVisibilityTrigger());
962 
963         if (qIsInf(result) || qIsNaN(result))
964         {
965             qWarning() << QObject::tr("Visibility trigger contains error and will be ignored");
966         }
967 
968         if (qFuzzyIsNull(result))
969         {
970             visible = false;
971         }
972     }
973     catch (qmu::QmuParserError &e)
974     {
975         qDebug() << "Parser error: " << e.GetMsg();
976         qWarning() << QObject::tr("Visibility trigger contains error and will be ignored");
977     }
978     return visible;
979 }
980 
981 //---------------------------------------------------------------------------------------------------------------------
indexOfNode(const QVector<VPieceNode> & nodes,quint32 id)982 int VPiecePath::indexOfNode(const QVector<VPieceNode> &nodes, quint32 id)
983 {
984     for (int i = 0; i < nodes.size(); ++i)
985     {
986         if (nodes.at(i).GetId() == id)
987         {
988             return i;
989         }
990     }
991     qDebug()<<"Can't find node.";
992     return -1;
993 }
994 
995 //---------------------------------------------------------------------------------------------------------------------
FindInLoopNotExcludedUp(int start,const QVector<VPieceNode> & nodes)996 int VPiecePath::FindInLoopNotExcludedUp(int start, const QVector<VPieceNode> &nodes)
997 {
998     if (start < 0 || start >= nodes.size())
999     {
1000         return -1;
1001     }
1002 
1003     int i = (start == 0) ? nodes.size()-1 : start-1;
1004 
1005     if (i < 0 || i >= nodes.size())
1006     {
1007         return -1;
1008     }
1009 
1010     int checked = 0;
1011     bool found = false;
1012     do
1013     {
1014         if (not nodes.at(i).IsExcluded())
1015         {
1016             found = true;
1017             break;
1018         }
1019 
1020         ++checked;
1021         --i;
1022         if (i < 0)
1023         {
1024             i = nodes.size() - 1;
1025         }
1026     } while (checked < nodes.size());
1027 
1028     return (not found) ? -1 : i;
1029 }
1030 
1031 //---------------------------------------------------------------------------------------------------------------------
FindInLoopNotExcludedDown(int start,const QVector<VPieceNode> & nodes)1032 int VPiecePath::FindInLoopNotExcludedDown(int start, const QVector<VPieceNode> &nodes)
1033 {
1034     if (start < 0 || start >= nodes.size())
1035     {
1036         return -1;
1037     }
1038 
1039     int i = (start == nodes.size()-1) ? 0 : start+1;
1040 
1041     if (i < 0 || i >= nodes.size())
1042     {
1043         return -1;
1044     }
1045 
1046     int checked = 0;
1047     bool found = false;
1048     do
1049     {
1050         if (not nodes.at(i).IsExcluded())
1051         {
1052             found = true;
1053             break;
1054         }
1055 
1056         ++checked;
1057         ++i;
1058         if (i >= nodes.size())
1059         {
1060             i = 0;
1061         }
1062     } while (checked < nodes.size());
1063 
1064     return (not found) ? -1 : i;
1065 }
1066 
1067 //---------------------------------------------------------------------------------------------------------------------
PreparePointEkv(const VPieceNode & node,const VContainer * data)1068 VSAPoint VPiecePath::PreparePointEkv(const VPieceNode &node, const VContainer *data)
1069 {
1070     SCASSERT(data !=nullptr)
1071 
1072     const QSharedPointer<VPointF> point = data->GeometricObject<VPointF>(node.GetId());
1073     VSAPoint p(point->toQPointF());
1074 
1075     p.SetSAAfter(node.GetSAAfter(data, *data->GetPatternUnit()));
1076     p.SetSABefore(node.GetSABefore(data, *data->GetPatternUnit()));
1077     p.SetAngleType(node.GetAngleType());
1078     p.SetManualPasskmarkLength(node.IsManualPassmarkLength());
1079     p.SetPasskmarkLength(node.GetPassmarkLength(data, *data->GetPatternUnit()));
1080 
1081     return p;
1082 }
1083 
1084 //---------------------------------------------------------------------------------------------------------------------
CurveSeamAllowanceSegment(const VContainer * data,const QVector<VPieceNode> & nodes,const QSharedPointer<VAbstractCurve> & curve,int i,bool reverse,qreal width,const QString & piece)1085 QVector<VSAPoint> VPiecePath::CurveSeamAllowanceSegment(const VContainer *data, const QVector<VPieceNode> &nodes,
1086                                                         const QSharedPointer<VAbstractCurve> &curve, int i,
1087                                                         bool reverse, qreal width, const QString &piece)
1088 {
1089     QVector<VSAPoint> pointsEkv;
1090 
1091     const VSAPoint begin = StartSegment(data, nodes, i, reverse);
1092     const VSAPoint end = EndSegment(data, nodes, i, reverse);
1093 
1094     const QVector<QPointF> points = curve->GetSegmentPoints(begin, end, reverse, piece);
1095     if (points.isEmpty())
1096     {
1097         return pointsEkv;
1098     }
1099 
1100     pointsEkv.reserve(points.size());
1101 
1102     qreal w1 = begin.GetSAAfter();
1103     qreal w2 = end.GetSABefore();
1104     if (w1 < 0 && w2 < 0)
1105     {// no local widths
1106         for(int i = 0; i < points.size(); ++i)
1107         {
1108             VSAPoint p(points.at(i));
1109             p.SetAngleType(PieceNodeAngle::ByLengthCurve);
1110             if (i == 0)
1111             { // first point
1112                 p.SetSAAfter(begin.GetSAAfter());
1113                 p.SetSABefore(begin.GetSABefore());
1114                 p.SetAngleType(begin.GetAngleType());
1115             }
1116             else if (i == points.size() - 1)
1117             { // last point
1118                 p.SetSAAfter(end.GetSAAfter());
1119                 p.SetSABefore(end.GetSABefore());
1120                 p.SetAngleType(end.GetAngleType());
1121             }
1122             pointsEkv.append(p);
1123         }
1124     }
1125     else
1126     {
1127         if (w1 < 0)
1128         {
1129             w1 = width;
1130         }
1131 
1132         if (w2 < 0)
1133         {
1134             w2 = width;
1135         }
1136 
1137         const qreal wDiff = w2 - w1;// Difference between to local widths
1138         const qreal fullLength = VAbstractCurve::PathLength(points);
1139 
1140         VSAPoint p(points.at(0));//First point in the list
1141         p.SetSAAfter(begin.GetSAAfter());
1142         p.SetSABefore(begin.GetSABefore());
1143         p.SetAngleType(begin.GetAngleType());
1144         pointsEkv.append(p);
1145 
1146         qreal length = 0; // how much we handle
1147 
1148         for(int i = 1; i < points.size(); ++i)
1149         {
1150             p = VSAPoint(points.at(i));
1151 
1152             if (i == points.size() - 1)
1153             {// last point
1154                 p.SetSAAfter(end.GetSAAfter());
1155                 p.SetSABefore(end.GetSABefore());
1156                 p.SetAngleType(end.GetAngleType());
1157             }
1158             else
1159             {
1160                 length += QLineF(points.at(i-1), points.at(i)).length();
1161                 const qreal localWidth = w1 + wDiff*(length/fullLength);
1162 
1163                 p.SetSAAfter(localWidth);
1164                 p.SetSABefore(localWidth);
1165                 p.SetAngleType(PieceNodeAngle::ByLengthCurve);
1166             }
1167 
1168             pointsEkv.append(p);
1169         }
1170     }
1171 
1172     return pointsEkv;
1173 }
1174 
1175 //---------------------------------------------------------------------------------------------------------------------
NodeName(const QVector<VPieceNode> & nodes,int nodeIndex,const VContainer * data)1176 QString VPiecePath::NodeName(const QVector<VPieceNode> &nodes, int nodeIndex, const VContainer *data)
1177 {
1178     if (not nodes.isEmpty() && (nodeIndex < 0 || nodeIndex >= nodes.size()))
1179     {
1180         return QString();
1181     }
1182 
1183     try
1184     {
1185         QSharedPointer<VGObject> obj = data->GetGObject(nodes.at(nodeIndex).GetId());
1186         return obj->name();
1187     }
1188     catch (const VExceptionBadId& )
1189     {
1190         // ignore
1191     }
1192     return QString();
1193 }
1194 
1195 //---------------------------------------------------------------------------------------------------------------------
NodesToPoints(const VContainer * data,const QVector<VPieceNode> & nodes,const QString & piece)1196 QVector<QPointF> VPiecePath::NodesToPoints(const VContainer *data, const QVector<VPieceNode> &nodes,
1197                                            const QString &piece)
1198 {
1199     QVector<QPointF> points;
1200     for (int i = 0; i < nodes.size(); ++i)
1201     {
1202         const VPieceNode &node = nodes.at(i);
1203         if (node.IsExcluded())
1204         {
1205             continue;// skip excluded node
1206         }
1207 
1208         switch (node.GetTypeTool())
1209         {
1210             case (Tool::NodePoint):
1211             {
1212                 const QSharedPointer<VPointF> point = data->GeometricObject<VPointF>(node.GetId());
1213                 points.append(static_cast<QPointF>(*point));
1214             }
1215             break;
1216             case (Tool::NodeArc):
1217             case (Tool::NodeElArc):
1218             case (Tool::NodeSpline):
1219             case (Tool::NodeSplinePath):
1220             {
1221                 const QSharedPointer<VAbstractCurve> curve = data->GeometricObject<VAbstractCurve>(node.GetId());
1222 
1223 
1224                 const QPointF begin = StartSegment(data, nodes, i, node.GetReverse());
1225                 const QPointF end = EndSegment(data, nodes, i, node.GetReverse());
1226 
1227                 points << curve->GetSegmentPoints(begin, end, node.GetReverse(), piece);
1228             }
1229             break;
1230             default:
1231                 qDebug()<<"Get wrong tool type. Ignore."<< static_cast<char>(node.GetTypeTool());
1232                 break;
1233         }
1234     }
1235 
1236     return points;
1237 }
1238