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