1 /************************************************************************
2  **
3  **  @file   vpassmark.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   1 5, 2019
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) 2019 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 <QPainterPath>
30 
31 #include "vpassmark.h"
32 #include "../vmisc/vabstractvalapplication.h"
33 #include "../ifc/exception/vexceptioninvalidnotch.h"
34 #include "../vgeometry/vabstractcurve.h"
35 #include "../vgeometry/varc.h"
36 #include "testpassmark.h"
37 #include "../vlayout/vrawsapoint.h"
38 
39 const qreal VPassmark::passmarkRadiusFactor = 0.45;
40 
41 namespace
42 {
43 //---------------------------------------------------------------------------------------------------------------------
GetSeamPassmarkSAPoint(const VPiecePassmarkData & passmarkData,const QVector<QPointF> & seamAllowance,QPointF & point)44 PassmarkStatus GetSeamPassmarkSAPoint(const VPiecePassmarkData &passmarkData, const QVector<QPointF> &seamAllowance,
45                                       QPointF &point)
46 {
47     bool needRollback = false; // no need for rollback
48     QVector<VRawSAPoint> ekvPoints;
49     ekvPoints = VAbstractPiece::EkvPoint(ekvPoints, passmarkData.previousSAPoint, passmarkData.passmarkSAPoint,
50                                          passmarkData.nextSAPoint, passmarkData.passmarkSAPoint, passmarkData.saWidth,
51                                          &needRollback);
52 
53     if (needRollback && not seamAllowance.isEmpty())
54     {
55         ekvPoints.clear();
56         ekvPoints += seamAllowance.at(seamAllowance.size()-2);
57     }
58 
59     if (ekvPoints.isEmpty())
60     { // Just in case
61         return PassmarkStatus::Error; // Something wrong
62     }
63 
64     if (ekvPoints.size() == 1 || ekvPoints.size() > 2)
65     {
66         point = ekvPoints.first();
67     }
68     else if (ekvPoints.size() == 2)
69     {
70         if(passmarkData.passmarkSAPoint.GetAngleType() == PieceNodeAngle::ByFirstEdgeSymmetry ||
71                 passmarkData.passmarkSAPoint.GetAngleType() == PieceNodeAngle::ByFirstEdgeRightAngle)
72         {
73             point = ekvPoints.first();
74         }
75         else
76         {
77             QLineF line = QLineF(ekvPoints.at(0), ekvPoints.at(1));
78             line.setLength(line.length()/2.);
79             point = line.p2();
80         }
81     }
82     return needRollback ? PassmarkStatus::Rollback : PassmarkStatus::Common;
83 }
84 
85 //---------------------------------------------------------------------------------------------------------------------
FixNotchPoint(const QVector<QPointF> & seamAllowance,const QPointF & notchBase,QPointF * notch)86 bool FixNotchPoint(const QVector<QPointF> &seamAllowance, const QPointF &notchBase, QPointF *notch)
87 {
88     bool fixed = true;
89     if (not VAbstractCurve::IsPointOnCurve(seamAllowance, *notch))
90     {
91         fixed = false;
92         QLineF axis = QLineF(notchBase, *notch);
93         axis.setLength(ToPixel(50, Unit::Cm));
94         const QVector<QPointF> points = VAbstractCurve::CurveIntersectLine(seamAllowance, axis);
95 
96         if (points.size() > 0)
97         {
98             if (points.size() == 1)
99             {
100                 *notch = points.at(0);
101                 fixed = true;
102             }
103             else
104             {
105                 QMap<qreal, int> forward;
106 
107                 for ( qint32 i = 0; i < points.size(); ++i )
108                 {
109                     if (points.at(i) == notchBase)
110                     { // Always seek unique intersection
111                         continue;
112                     }
113 
114                     const QLineF length(notchBase, points.at(i));
115                     if (qAbs(length.angle() - axis.angle()) < 0.1)
116                     {
117                         forward.insert(length.length(), i);
118                     }
119                 }
120 
121 
122                 // Closest point is not always want we need. First return point in forward direction if exists.
123                 if (not forward.isEmpty())
124                 {
125                     *notch = points.at(forward.first());
126                     fixed = true;
127                 }
128             }
129         }
130     }
131     else
132     { // Fixing distortion
133         QLineF axis = QLineF(notchBase, *notch);
134         axis.setLength(axis.length() + accuracyPointOnLine * 10);
135         const QVector<QPointF> points = VAbstractCurve::CurveIntersectLine(seamAllowance, axis);
136         if (points.size() == 1)
137         {
138             *notch = points.first();
139         }
140     }
141 
142     return fixed;
143 }
144 const qreal passmarkGap = (1.5/*mm*/ / 25.4) * PrintDPI;
145 
146 //---------------------------------------------------------------------------------------------------------------------
CreateOnePassmarkLines(const QLineF & line)147 QVector<QLineF> CreateOnePassmarkLines(const QLineF &line)
148 {
149     return QVector<QLineF>({line});
150 }
151 
152 //---------------------------------------------------------------------------------------------------------------------
CreateTwoPassmarkLines(const QLineF & line,const QVector<QPointF> & seamAllowance)153 QVector<QLineF> CreateTwoPassmarkLines(const QLineF &line, const QVector<QPointF> &seamAllowance)
154 {
155     QPointF l1p1;
156     {
157         QLineF line1 = line;
158         line1.setAngle(line1.angle() + 90);
159         line1.setLength(passmarkGap/2.);
160         l1p1 = line1.p2();
161     }
162 
163     QPointF l2p1;
164     {
165         QLineF line2 = line;
166         line2.setAngle(line2.angle() - 90);
167         line2.setLength(passmarkGap/2.);
168         l2p1 = line2.p2();
169     }
170 
171     QPointF l1p2;
172     {
173         QLineF line1 = QLineF(line.p2(), line.p1());
174         line1.setAngle(line1.angle() - 90);
175         line1.setLength(passmarkGap/2.);
176         l1p2 = line1.p2();
177     }
178 
179     QPointF l2p2;
180     {
181         QLineF line2 = QLineF(line.p2(), line.p1());
182         line2.setAngle(line2.angle() + 90);
183         line2.setLength(passmarkGap/2.);
184         l2p2 = line2.p2();
185     }
186 
187     QVector<QLineF> lines;
188     QLineF seg = VPassmark::FindIntersection(QLineF(l1p2, l1p1), seamAllowance);
189     lines.append(QLineF(seg.p2(), seg.p1()));
190 
191     seg = VPassmark::FindIntersection(QLineF(l2p2, l2p1), seamAllowance);
192     lines.append(QLineF(seg.p2(), seg.p1()));
193     return lines;
194 }
195 
196 //---------------------------------------------------------------------------------------------------------------------
CreateThreePassmarkLines(const QLineF & line,const QVector<QPointF> & seamAllowance)197 QVector<QLineF> CreateThreePassmarkLines(const QLineF &line, const QVector<QPointF> &seamAllowance)
198 {
199     QPointF l1p1;
200     {
201         QLineF line1 = line;
202         line1.setAngle(line1.angle() + 90);
203         line1.setLength(passmarkGap);
204         l1p1 = line1.p2();
205     }
206 
207     QPointF l2p1;
208     {
209         QLineF line2 = line;
210         line2.setAngle(line2.angle() - 90);
211         line2.setLength(passmarkGap);
212         l2p1 = line2.p2();
213     }
214 
215     QPointF l1p2;
216     {
217         QLineF line1 = QLineF(line.p2(), line.p1());
218         line1.setAngle(line1.angle() - 90);
219         line1.setLength(passmarkGap);
220         l1p2 = line1.p2();
221     }
222 
223     QPointF l2p2;
224     {
225         QLineF line2 = QLineF(line.p2(), line.p1());
226         line2.setAngle(line2.angle() + 90);
227         line2.setLength(passmarkGap);
228         l2p2 = line2.p2();
229     }
230 
231     QVector<QLineF> lines;
232     QLineF seg = VPassmark::FindIntersection(QLineF(l1p2, l1p1), seamAllowance);
233     lines.append(QLineF(seg.p2(), seg.p1()));
234 
235     lines.append(line);
236 
237     seg = VPassmark::FindIntersection(QLineF(l2p2, l2p1), seamAllowance);
238     lines.append(QLineF(seg.p2(), seg.p1()));
239     return lines;
240 }
241 
242 //---------------------------------------------------------------------------------------------------------------------
CreateTMarkPassmark(const QLineF & line)243 QVector<QLineF> CreateTMarkPassmark(const QLineF &line)
244 {
245     QPointF p1;
246     {
247         QLineF tmpLine = QLineF(line.p2(), line.p1());
248         tmpLine.setAngle(tmpLine.angle() - 90);
249         tmpLine.setLength(line.length() * 0.75 / 2);
250         p1 = tmpLine.p2();
251     }
252 
253     QPointF p2;
254     {
255         QLineF tmpLine = QLineF(line.p2(), line.p1());
256         tmpLine.setAngle(tmpLine.angle() + 90);
257         tmpLine.setLength(line.length() * 0.75 / 2);
258         p2 = tmpLine.p2();
259     }
260 
261     QVector<QLineF> lines;
262     lines.append(line);
263     lines.append(QLineF(p1, p2));
264     return lines;
265 }
266 
267 //---------------------------------------------------------------------------------------------------------------------
CreateVMarkPassmark(const QLineF & line)268 QVector<QLineF> CreateVMarkPassmark(const QLineF &line)
269 {
270     QLineF l1 = line;
271     l1.setAngle(l1.angle() - 35);
272 
273     QLineF l2 = line;
274     l2.setAngle(l2.angle() + 35);
275 
276     QVector<QLineF> lines;
277     lines.append(l1);
278     lines.append(l2);
279     return lines;
280 }
281 
282 //---------------------------------------------------------------------------------------------------------------------
CreateVMark2Passmark(const QLineF & line,const QVector<QPointF> & seamAllowance)283 QVector<QLineF> CreateVMark2Passmark(const QLineF &line, const QVector<QPointF> &seamAllowance)
284 {
285     QLineF l1 = QLineF(line.p2(), line.p1());
286     l1.setAngle(l1.angle() + 35);
287 
288     QLineF l2 = QLineF(line.p2(), line.p1());
289     l2.setAngle(l2.angle() - 35);
290 
291     QVector<QLineF> lines;
292     lines.append(VPassmark::FindIntersection(l1, seamAllowance));
293     lines.append(VPassmark::FindIntersection(l2, seamAllowance));
294     return lines;
295 }
296 
297 //---------------------------------------------------------------------------------------------------------------------
PointsToSegments(const QVector<QPointF> & points)298 QVector<QLineF> PointsToSegments(const QVector<QPointF> &points)
299 {
300     QVector<QLineF> lines;
301     if (points.size() >= 2)
302     {
303         for (int i=0; i < points.size()-1; ++i)
304         {
305             QLineF segment = QLineF(points.at(i), points.at(i+1));
306             if (segment.length() > 0)
307             {
308                 lines.append(segment);
309             }
310         }
311     }
312     return lines;
313 }
314 
315 //---------------------------------------------------------------------------------------------------------------------
CreateUMarkPassmark(const QLineF & line,const QVector<QPointF> & seamAllowance)316 QVector<QLineF> CreateUMarkPassmark(const QLineF &line, const QVector<QPointF> &seamAllowance)
317 {
318     const qreal radius = line.length() * VPassmark::passmarkRadiusFactor;
319 
320     QLineF baseLine = line;
321     baseLine.setLength(baseLine.length() - radius); // keep defined depth
322 
323     QPointF l1p1;
324     {
325         QLineF line1 = baseLine;
326         line1.setAngle(line1.angle() + 90);
327         line1.setLength(radius);
328         l1p1 = line1.p2();
329     }
330 
331     QPointF l2p1;
332     {
333         QLineF line2 = baseLine;
334         line2.setAngle(line2.angle() - 90);
335         line2.setLength(radius);
336         l2p1 = line2.p2();
337     }
338 
339     QPointF l1p2;
340     {
341         QLineF line1 = QLineF(baseLine.p2(), baseLine.p1());
342         line1.setAngle(line1.angle() - 90);
343         line1.setLength(radius);
344         l1p2 = line1.p2();
345     }
346 
347     QPointF l2p2;
348     {
349         QLineF line2 = QLineF(baseLine.p2(), baseLine.p1());
350         line2.setAngle(line2.angle() + 90);
351         line2.setLength(radius);
352         l2p2 = line2.p2();
353     }
354 
355     QLineF axis = QLineF(baseLine.p2(), baseLine.p1());
356     axis.setLength(radius);
357 
358     QVector<QPointF> points;
359 
360     QLineF seg = VPassmark::FindIntersection(QLineF(l2p2, l2p1), seamAllowance);
361     seg = QLineF(seg.p2(), seg.p1());
362     points.append(seg.p1());
363     points.append(seg.p2());
364 
365     VArc arc(VPointF(baseLine.p2()), radius, QLineF(baseLine.p2(), l2p2).angle(), QLineF(baseLine.p2(), l1p2).angle());
366     arc.SetApproximationScale(10);
367     points += arc.GetPoints();
368 
369     seg = VPassmark::FindIntersection(QLineF(l1p2, l1p1), seamAllowance);
370     seg = QLineF(seg.p2(), seg.p1());
371     points.append(seg.p2());
372     points.append(seg.p1());
373 
374     return PointsToSegments(points);
375 }
376 
377 //---------------------------------------------------------------------------------------------------------------------
CreateBoxMarkPassmark(const QLineF & line,const QVector<QPointF> & seamAllowance)378 QVector<QLineF> CreateBoxMarkPassmark(const QLineF &line, const QVector<QPointF> &seamAllowance)
379 {
380     const qreal radius = line.length() * VPassmark::passmarkRadiusFactor;
381 
382     QPointF l1p1;
383     {
384         QLineF line1 = line;
385         line1.setAngle(line1.angle() + 90);
386         line1.setLength(radius);
387         l1p1 = line1.p2();
388     }
389 
390     QPointF l2p1;
391     {
392         QLineF line2 = line;
393         line2.setAngle(line2.angle() - 90);
394         line2.setLength(radius);
395         l2p1 = line2.p2();
396     }
397 
398     QPointF l1p2;
399     {
400         QLineF line1 = QLineF(line.p2(), line.p1());
401         line1.setAngle(line1.angle() - 90);
402         line1.setLength(radius);
403         l1p2 = line1.p2();
404     }
405 
406     QPointF l2p2;
407     {
408         QLineF line2 = QLineF(line.p2(), line.p1());
409         line2.setAngle(line2.angle() + 90);
410         line2.setLength(radius);
411         l2p2 = line2.p2();
412     }
413 
414     QVector<QPointF> points;
415 
416     QLineF seg = VPassmark::FindIntersection(QLineF(l1p2, l1p1), seamAllowance);
417     points.append(seg.p2());
418     points.append(seg.p1());
419 
420     seg = VPassmark::FindIntersection(QLineF(l2p2, l2p1), seamAllowance);
421     points.append(seg.p1());
422     points.append(seg.p2());
423 
424     return PointsToSegments(points);
425 }
426 
427 //---------------------------------------------------------------------------------------------------------------------
CreatePassmarkLines(PassmarkLineType lineType,PassmarkAngleType angleType,const QVector<QLineF> & lines,const QVector<QPointF> & seamAllowance,PassmarkSide side)428 QVector<QLineF> CreatePassmarkLines(PassmarkLineType lineType, PassmarkAngleType angleType,
429                                     const QVector<QLineF> &lines, const QVector<QPointF> &seamAllowance,
430                                     PassmarkSide side)
431 {
432     if (lines.isEmpty())
433     {
434         return QVector<QLineF>();
435     }
436 
437     QVector<QLineF> passmarksLines;
438 
439     auto CreateLinesWithCorrection = [&passmarksLines, side, angleType, lines, seamAllowance]
440             (QVector<QLineF> (*create)(const QLineF &, const QVector<QPointF> &))
441     {
442         if (angleType == PassmarkAngleType::Straightforward)
443         {
444             passmarksLines += (*create)(lines.first(), seamAllowance);
445         }
446         else
447         {
448             if (side == PassmarkSide::All || side == PassmarkSide::Left)
449             {
450                 passmarksLines += (*create)(lines.first(), seamAllowance);
451             }
452 
453             if (side == PassmarkSide::All || side == PassmarkSide::Right)
454             {
455                 passmarksLines += (*create)(lines.last(), seamAllowance);
456             }
457         }
458     };
459 
460     auto CreateLines = [&passmarksLines, side, angleType, lines](QVector<QLineF> (*create)(const QLineF &))
461     {
462         if (angleType == PassmarkAngleType::Straightforward)
463         {
464             passmarksLines += (*create)(lines.first());
465         }
466         else
467         {
468             if (side == PassmarkSide::All || side == PassmarkSide::Left)
469             {
470                 passmarksLines += (*create)(lines.first());
471             }
472 
473             if (side == PassmarkSide::All || side == PassmarkSide::Right)
474             {
475                 passmarksLines += (*create)(lines.last());
476             }
477         }
478     };
479 
480     if (angleType == PassmarkAngleType::Straightforward
481             || angleType == PassmarkAngleType::Intersection
482             || angleType == PassmarkAngleType::IntersectionOnlyLeft
483             || angleType == PassmarkAngleType::IntersectionOnlyRight
484             || angleType == PassmarkAngleType::Intersection2
485             || angleType == PassmarkAngleType::Intersection2OnlyLeft
486             || angleType == PassmarkAngleType::Intersection2OnlyRight)
487     {
488         switch (lineType)
489         {
490             case PassmarkLineType::TwoLines:
491                 CreateLinesWithCorrection(CreateTwoPassmarkLines);
492                 break;
493             case PassmarkLineType::ThreeLines:
494                 CreateLinesWithCorrection(CreateThreePassmarkLines);
495                 break;
496             case PassmarkLineType::TMark:
497                 CreateLines(CreateTMarkPassmark);
498                 break;
499             case PassmarkLineType::VMark:
500                 CreateLines(CreateVMarkPassmark);
501                 break;
502             case PassmarkLineType::VMark2:
503                 CreateLinesWithCorrection(CreateVMark2Passmark);
504                 break;
505             case PassmarkLineType::UMark:
506                 CreateLinesWithCorrection(CreateUMarkPassmark);
507                 break;
508             case PassmarkLineType::BoxMark:
509                 CreateLinesWithCorrection(CreateBoxMarkPassmark);
510                 break;
511             case PassmarkLineType::OneLine:
512             default:
513                 CreateLines(CreateOnePassmarkLines);
514                 break;
515         }
516     }
517     else
518     {
519         switch (lineType)
520         {
521             case PassmarkLineType::TMark:
522                 passmarksLines += CreateTMarkPassmark(lines.first());
523                 break;
524             case PassmarkLineType::OneLine:
525             case PassmarkLineType::TwoLines:
526             case PassmarkLineType::ThreeLines:
527             case PassmarkLineType::VMark:
528             case PassmarkLineType::VMark2:
529             case PassmarkLineType::UMark:
530             case PassmarkLineType::BoxMark:
531             default:
532                 passmarksLines.append(lines.first());
533                 break;
534         }
535     }
536 
537     return passmarksLines;
538 }
539 
540 //---------------------------------------------------------------------------------------------------------------------
PassmarkLength(const VPiecePassmarkData & passmarkData,qreal width,bool & ok)541 auto PassmarkLength(const VPiecePassmarkData &passmarkData, qreal width, bool &ok) -> qreal
542 {
543     qreal length = 0;
544     if (not passmarkData.passmarkSAPoint.IsManualPasskmarkLength())
545     {
546         if (passmarkData.globalPassmarkLength > accuracyPointOnLine)
547         {
548             ok = true;
549             return passmarkData.globalPassmarkLength;
550         }
551 
552         length = qMin(width * VSAPoint::passmarkFactor, VSAPoint::maxPassmarkLength);
553 
554         if (length <= accuracyPointOnLine)
555         {
556             const QString errorMsg = QObject::tr("Found null notch for point '%1' in piece '%2'. Length is less "
557                                                  "than minimal allowed.")
558                     .arg(passmarkData.nodeName, passmarkData.pieceName);
559             VAbstractApplication::VApp()->IsPedantic()
560                     ? throw VException(errorMsg)
561                     : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
562 
563             ok = false;
564             return length;
565         }
566 
567         ok = true;
568         return length;
569     }
570 
571     length = passmarkData.passmarkSAPoint.GetPasskmarkLength();
572 
573     ok = true;
574     return length;
575 }
576 
577 //---------------------------------------------------------------------------------------------------------------------
PassmarkBisectorBaseLine(PassmarkStatus seamPassmarkType,const VPiecePassmarkData & passmarkData,const QPointF & seamPassmarkSAPoint,const QVector<QPointF> & seamAllowance)578 QVector<QLineF> PassmarkBisectorBaseLine(PassmarkStatus seamPassmarkType, const VPiecePassmarkData &passmarkData,
579                                          const QPointF &seamPassmarkSAPoint, const QVector<QPointF> &seamAllowance)
580 {
581     QLineF edge1;
582     QLineF edge2;
583 
584     if (seamPassmarkType == PassmarkStatus::Common)
585     {
586         if (passmarkData.passmarkSAPoint.GetAngleType() == PieceNodeAngle::ByFirstEdgeSymmetry)
587         {
588             edge1 = QLineF(seamPassmarkSAPoint, seamAllowance.at(seamAllowance.size() - 2));
589             edge2 = QLineF(seamPassmarkSAPoint, seamAllowance.at(1));
590         }
591         else
592         {
593             const QLineF bigLine1 = VAbstractPiece::ParallelLine(passmarkData.previousSAPoint,
594                                                                  passmarkData.passmarkSAPoint, passmarkData.saWidth );
595             const QLineF bigLine2 = VAbstractPiece::ParallelLine(passmarkData.passmarkSAPoint, passmarkData.nextSAPoint,
596                                                                  passmarkData.saWidth );
597 
598             edge1 = QLineF(seamPassmarkSAPoint, bigLine1.p1());
599             edge2 = QLineF(seamPassmarkSAPoint, bigLine2.p2());
600         }
601     }
602     else if(seamPassmarkType == PassmarkStatus::Rollback)
603     {
604         edge1 = QLineF(seamPassmarkSAPoint, seamAllowance.at(seamAllowance.size() - 3));
605         edge2 = QLineF(seamPassmarkSAPoint, seamAllowance.at(0));
606     }
607     else
608     { // Should never happen
609         return QVector<QLineF>();
610     }
611 
612     bool ok = false;
613     const qreal length = PassmarkLength(passmarkData, passmarkData.passmarkSAPoint.MaxLocalSA(passmarkData.saWidth),
614                                         ok);
615     if (not ok)
616     {
617         return QVector<QLineF>();
618     }
619 
620     edge1.setAngle(edge1.angle() + edge1.angleTo(edge2)/2.);
621     edge1.setLength(length);
622 
623     return QVector<QLineF>({edge1});
624 }
625 
626 //---------------------------------------------------------------------------------------------------------------------
PassmarkToPath(const QVector<QLineF> & passmark)627 QPainterPath PassmarkToPath(const QVector<QLineF> &passmark)
628 {
629     QPainterPath path;
630     if (not passmark.isEmpty())
631     {
632         for (qint32 i = 0; i < passmark.count(); ++i)
633         {
634             path.moveTo(passmark.at(i).p1());
635             path.lineTo(passmark.at(i).p2());
636         }
637 
638         path.setFillRule(Qt::WindingFill);
639     }
640     return path;
641 }
642 }
643 
644 //------------------------------VPiecePassmarkData---------------------------------------------------------------------
toJson() const645 QJsonObject VPiecePassmarkData::toJson() const
646 {
647     QJsonObject dataObject
648     {
649         {"previousSAPoint", previousSAPoint.toJson()},
650         {"passmarkSAPoint", passmarkSAPoint.toJson()},
651         {"nextSAPoint", nextSAPoint.toJson()},
652         {"saWidth", saWidth},
653         {"nodeName", nodeName},
654         {"pieceName", pieceName},
655         {"passmarkLineType", static_cast<int>(passmarkLineType)},
656         {"passmarkAngleType", static_cast<int>(passmarkAngleType)},
657         {"isMainPathNode", isMainPathNode},
658         {"isShowSecondPassmark", isShowSecondPassmark},
659         {"passmarkIndex", passmarkIndex},
660         {"id", static_cast<qint64>(id)},
661         {"globalPassmarkLength", static_cast<qreal>(globalPassmarkLength)},
662     };
663 
664     return dataObject;
665 }
666 
667 //---------------------------------------------------------------------------------------------------------------------
VPassmark()668 VPassmark::VPassmark()
669 {}
670 
671 //---------------------------------------------------------------------------------------------------------------------
VPassmark(const VPiecePassmarkData & data)672 VPassmark::VPassmark(const VPiecePassmarkData &data)
673     : m_data(data),
674       m_null(false)
675 {
676     // Correct distorsion
677     if (VGObject::IsPointOnLineSegment(m_data.passmarkSAPoint, m_data.previousSAPoint,
678                                        m_data.nextSAPoint))
679     {
680         const QPointF p = VGObject::CorrectDistortion(m_data.passmarkSAPoint, m_data.previousSAPoint,
681                                                       m_data.nextSAPoint);
682         m_data.passmarkSAPoint.setX(p.x());
683         m_data.passmarkSAPoint.setY(p.y());
684     }
685 }
686 
687 //---------------------------------------------------------------------------------------------------------------------
FullPassmark(const VPiece & piece,const VContainer * data) const688 QVector<QLineF> VPassmark::FullPassmark(const VPiece &piece, const VContainer *data) const
689 {
690     if (m_null)
691     {
692         return QVector<QLineF>();
693     }
694 
695     if (not piece.IsSeamAllowanceBuiltIn())
696     {
697         QVector<QLineF> lines;
698         lines += SAPassmark(piece, data, PassmarkSide::All);
699         if (VAbstractApplication::VApp()->Settings()->IsDoublePassmark()
700                 && (VAbstractApplication::VApp()->Settings()->IsPieceShowMainPath() || not piece.IsHideMainPath())
701                 && m_data.isMainPathNode
702                 && m_data.passmarkAngleType != PassmarkAngleType::Intersection
703                 && m_data.passmarkAngleType != PassmarkAngleType::IntersectionOnlyLeft
704                 && m_data.passmarkAngleType != PassmarkAngleType::IntersectionOnlyRight
705                 && m_data.passmarkAngleType != PassmarkAngleType::Intersection2
706                 && m_data.passmarkAngleType != PassmarkAngleType::Intersection2OnlyLeft
707                 && m_data.passmarkAngleType != PassmarkAngleType::Intersection2OnlyRight
708                 && m_data.isShowSecondPassmark)
709         {
710             lines += BuiltInSAPassmark(piece, data);
711         }
712         return lines;
713     }
714 
715     return BuiltInSAPassmark(piece, data);
716 }
717 
718 //---------------------------------------------------------------------------------------------------------------------
SAPassmark(const VPiece & piece,const VContainer * data,PassmarkSide side) const719 QVector<QLineF> VPassmark::SAPassmark(const VPiece &piece, const VContainer *data, PassmarkSide side) const
720 {
721     if (m_null)
722     {
723         return QVector<QLineF>();
724     }
725 
726     if (not piece.IsSeamAllowanceBuiltIn())
727     {
728         // Because rollback cannot be calulated if passmark is not first point in main path we rotate it.
729         return SAPassmark(piece.SeamAllowancePointsWithRotation(data, m_data.passmarkIndex), side);
730     }
731 
732     return QVector<QLineF>();
733 }
734 
735 //---------------------------------------------------------------------------------------------------------------------
SAPassmark(const QVector<QPointF> & seamAllowance,PassmarkSide side) const736 QVector<QLineF> VPassmark::SAPassmark(const QVector<QPointF> &seamAllowance, PassmarkSide side) const
737 {
738     if (m_null)
739     {
740         return QVector<QLineF>();
741     }
742 
743     // Because rollback @seamAllowance must be rotated here.
744     return MakeSAPassmark(seamAllowance, side);
745 }
746 
747 //---------------------------------------------------------------------------------------------------------------------
IsNull() const748 bool VPassmark::IsNull() const
749 {
750     return m_null;
751 }
752 
753 //---------------------------------------------------------------------------------------------------------------------
Data() const754 VPiecePassmarkData VPassmark::Data() const
755 {
756     return m_data;
757 }
758 
759 //---------------------------------------------------------------------------------------------------------------------
FindIntersection(const QLineF & line,const QVector<QPointF> & seamAllowance)760 QLineF VPassmark::FindIntersection(const QLineF &line, const QVector<QPointF> &seamAllowance)
761 {
762     QLineF testLine = line;
763     testLine.setLength(testLine.length()*10);
764     QVector<QPointF> intersections = VAbstractCurve::CurveIntersectLine(seamAllowance, testLine);
765     if (not intersections.isEmpty())
766     {
767         return QLineF(line.p1(), intersections.last());
768     }
769 
770     return line;
771 }
772 
773 //---------------------------------------------------------------------------------------------------------------------
MakeSAPassmark(const QVector<QPointF> & seamAllowance,PassmarkSide side) const774 QVector<QLineF> VPassmark::MakeSAPassmark(const QVector<QPointF> &seamAllowance, PassmarkSide side) const
775 {
776 //    DumpVector(seamAllowance, QStringLiteral("seamAllowance.json.XXXXXX")); // Uncomment for dumping test data
777 //    DumpPassmarkData(m_data, QStringLiteral("passmarkData.json.XXXXXX")); // Uncomment for dumping test data
778 
779     QVector<QLineF> lines = SAPassmarkBaseLine(seamAllowance, side);
780     if (lines.isEmpty())
781     {
782         return lines;
783     }
784 
785     lines = CreatePassmarkLines(m_data.passmarkLineType, m_data.passmarkAngleType, lines, seamAllowance, side);
786 //    DumpPassmarkShape(lines, QStringLiteral("passmarkShape.json.XXXXXX")); // Uncomment for dumping test data
787     return lines;
788 }
789 
790 //---------------------------------------------------------------------------------------------------------------------
BuiltInSAPassmark(const VPiece & piece,const VContainer * data) const791 QVector<QLineF> VPassmark::BuiltInSAPassmark(const VPiece &piece, const VContainer *data) const
792 {
793     if (m_null)
794     {
795         return QVector<QLineF>();
796     }
797 
798     const QVector<QLineF> lines = BuiltInSAPassmarkBaseLine(piece);
799     if (lines.isEmpty())
800     {
801         return QVector<QLineF>();
802     }
803 
804     return CreatePassmarkLines(m_data.passmarkLineType, m_data.passmarkAngleType, lines, piece.MainPathPoints(data),
805                                PassmarkSide::All);
806 }
807 
808 //---------------------------------------------------------------------------------------------------------------------
BuiltInSAPassmarkBaseLine(const VPiece & piece) const809 QVector<QLineF> VPassmark::BuiltInSAPassmarkBaseLine(const VPiece &piece) const
810 {
811     if (m_null)
812     {
813         return QVector<QLineF>();
814     }
815 
816     qreal length = 0;
817     if (not piece.IsSeamAllowanceBuiltIn())
818     {
819         bool ok = false;
820         length = PassmarkLength(m_data, m_data.passmarkSAPoint.MaxLocalSA(m_data.saWidth), ok);
821         if (not ok)
822         {
823             return {};
824         }
825     }
826     else
827     {
828         if (m_data.passmarkSAPoint.IsManualPasskmarkLength())
829         {
830             length = m_data.passmarkSAPoint.GetPasskmarkLength();
831         }
832         else
833         {
834             if (m_data.globalPassmarkLength > accuracyPointOnLine)
835             {
836                 length = m_data.globalPassmarkLength;
837             }
838             else
839             {
840                 const QString errorMsg = QObject::tr("Cannot calculate a notch for point '%1' in piece '%2' with built "
841                                                      "in seam allowance. User must manually provide length.")
842                         .arg(m_data.nodeName, m_data.pieceName);
843                 VAbstractApplication::VApp()->IsPedantic()
844                         ? throw VExceptionInvalidNotch(errorMsg)
845                         : qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
846                 return {};
847             }
848         }
849     }
850 
851     QLineF edge1 = QLineF(m_data.passmarkSAPoint, m_data.previousSAPoint);
852     QLineF edge2 = QLineF(m_data.passmarkSAPoint, m_data.nextSAPoint);
853 
854     edge1.setAngle(edge1.angle() + edge1.angleTo(edge2)/2.);
855     edge1.setLength(length);
856 
857     return QVector<QLineF>({edge1});
858 }
859 
860 //---------------------------------------------------------------------------------------------------------------------
SAPassmarkBaseLine(const VPiece & piece,const VContainer * data,PassmarkSide side) const861 QVector<QLineF> VPassmark::SAPassmarkBaseLine(const VPiece &piece, const VContainer *data, PassmarkSide side) const
862 {
863     if (m_null)
864     {
865         return QVector<QLineF>();
866     }
867 
868     if (not piece.IsSeamAllowanceBuiltIn())
869     {
870         // Because rollback cannot be calulated if passmark is not first point in main path we rotate it.
871         return SAPassmarkBaseLine(piece.SeamAllowancePointsWithRotation(data, m_data.passmarkIndex), side);
872     }
873 
874     return QVector<QLineF>();
875 }
876 
877 //---------------------------------------------------------------------------------------------------------------------
SAPassmarkBaseLine(const QVector<QPointF> & seamAllowance,PassmarkSide side) const878 QVector<QLineF> VPassmark::SAPassmarkBaseLine(const QVector<QPointF> &seamAllowance, PassmarkSide side) const
879 {
880     if (m_null)
881     {
882         return QVector<QLineF>();
883     }
884 
885     if (seamAllowance.size() < 2)
886     {
887         const QString errorMsg = QObject::tr("Cannot calculate a notch for point '%1' in piece '%2'. Seam allowance is "
888                                              "empty.").arg(m_data.nodeName, m_data.pieceName);
889         VAbstractApplication::VApp()->IsPedantic() ? throw VExceptionInvalidNotch(errorMsg) :
890                                               qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
891         return QVector<QLineF>(); // Something wrong
892     }
893 
894     QPointF seamPassmarkSAPoint;
895     const PassmarkStatus seamPassmarkType = GetSeamPassmarkSAPoint(m_data, seamAllowance, seamPassmarkSAPoint);
896     if (seamPassmarkType == PassmarkStatus::Error)
897     {
898         const QString errorMsg = QObject::tr("Cannot calculate a notch for point '%1' in piece '%2'. Cannot find "
899                                              "position for a notch.")
900                 .arg(m_data.nodeName, m_data.pieceName);
901         VAbstractApplication::VApp()->IsPedantic() ? throw VExceptionInvalidNotch(errorMsg) :
902                                               qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
903         return QVector<QLineF>(); // Something wrong
904     }
905 
906     if (not FixNotchPoint(seamAllowance, m_data.passmarkSAPoint, &seamPassmarkSAPoint))
907     {
908         const QString errorMsg = QObject::tr("Cannot calculate a notch for point '%1' in piece '%2'. Unable to fix a "
909                                              "notch position.")
910                 .arg(m_data.nodeName, m_data.pieceName);
911         VAbstractApplication::VApp()->IsPedantic() ? throw VExceptionInvalidNotch(errorMsg) :
912                                               qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
913     }
914 
915     auto PassmarkIntersection = [this, seamAllowance] (QLineF line, qreal width)
916     {
917         line.setLength(line.length()*100); // Hope 100 is enough
918 
919 //        DumpVector(seamAllowance, QStringLiteral("points.json.XXXXXX")); // Uncomment for dumping test data
920 
921         const QVector<QPointF> intersections = VAbstractCurve::CurveIntersectLine(seamAllowance, line);
922 
923 //        DumpVector(intersections, QStringLiteral("intersections.json.XXXXXX")); // Uncomment for dumping test data
924 
925         if (not intersections.isEmpty())
926         {
927             if (intersections.last() != m_data.passmarkSAPoint)
928             {
929                 line = QLineF(intersections.last(), m_data.passmarkSAPoint);
930 
931                 bool ok = false;
932                 const qreal length = PassmarkLength(m_data, width, ok);
933                 if (not ok)
934                 {
935                     return QLineF();
936                 }
937                 line.setLength(length);
938 
939                 return line;
940             }
941 
942             const QString errorMsg = QObject::tr("Cannot calculate a notch for point '%1' in piece '%2'. Notch "
943                                                  "collapse.")
944                     .arg(m_data.nodeName, m_data.pieceName);
945             VAbstractApplication::VApp()->IsPedantic() ? throw VExceptionInvalidNotch(errorMsg) :
946                                           qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
947         }
948         else
949         {
950             const QString errorMsg = QObject::tr("Cannot calculate a notch for point '%1' in piece '%2'. Cannot find "
951                                                  "intersection.")
952                     .arg(m_data.nodeName, m_data.pieceName);
953             VAbstractApplication::VApp()->IsPedantic() ? throw VExceptionInvalidNotch(errorMsg) :
954                                               qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
955         }
956 
957         return QLineF();
958     };
959 
960     if (m_data.passmarkAngleType == PassmarkAngleType::Straightforward)
961     {
962         bool ok = false;
963         const qreal length = PassmarkLength(m_data, m_data.passmarkSAPoint.MaxLocalSA(m_data.saWidth), ok);
964 
965         if (not ok)
966         {
967             return {};
968         }
969 
970         QLineF line = QLineF(seamPassmarkSAPoint, m_data.passmarkSAPoint);
971         line.setLength(length);
972         return {line};
973     }
974     else if (m_data.passmarkAngleType == PassmarkAngleType::Bisector)
975     {
976         return PassmarkBisectorBaseLine(seamPassmarkType, m_data, seamPassmarkSAPoint, seamAllowance);
977     }
978     else if (m_data.passmarkAngleType == PassmarkAngleType::Intersection
979              || m_data.passmarkAngleType == PassmarkAngleType::IntersectionOnlyLeft
980              || m_data.passmarkAngleType == PassmarkAngleType::IntersectionOnlyRight)
981     {
982         QVector<QLineF> lines;
983         if ((m_data.passmarkAngleType == PassmarkAngleType::Intersection
984                 || m_data.passmarkAngleType == PassmarkAngleType::IntersectionOnlyLeft)
985                 && (side == PassmarkSide::All || side == PassmarkSide::Left))
986         {
987             // first passmark
988             lines += PassmarkIntersection(QLineF(m_data.nextSAPoint, m_data.passmarkSAPoint),
989                                           m_data.passmarkSAPoint.GetSABefore(m_data.saWidth));
990         }
991 
992         if ((m_data.passmarkAngleType == PassmarkAngleType::Intersection
993                 || m_data.passmarkAngleType == PassmarkAngleType::IntersectionOnlyRight)
994                 && (side == PassmarkSide::All || side == PassmarkSide::Right))
995         {
996             // second passmark
997             lines += PassmarkIntersection(QLineF(m_data.previousSAPoint, m_data.passmarkSAPoint),
998                                           m_data.passmarkSAPoint.GetSAAfter(m_data.saWidth));
999         }
1000 
1001         return lines;
1002     }
1003     else if (m_data.passmarkAngleType == PassmarkAngleType::Intersection2
1004              || m_data.passmarkAngleType == PassmarkAngleType::Intersection2OnlyLeft
1005              || m_data.passmarkAngleType == PassmarkAngleType::Intersection2OnlyRight)
1006     {
1007         QVector<QLineF> lines;
1008         if ((m_data.passmarkAngleType == PassmarkAngleType::Intersection2
1009                 || m_data.passmarkAngleType == PassmarkAngleType::Intersection2OnlyLeft)
1010                 && (side == PassmarkSide::All || side == PassmarkSide::Left))
1011         {
1012             // first passmark
1013             QLineF line(m_data.passmarkSAPoint, m_data.previousSAPoint);
1014             line.setAngle(line.angle()-90);
1015             lines += PassmarkIntersection(line, m_data.passmarkSAPoint.GetSABefore(m_data.saWidth));
1016         }
1017 
1018         if ((m_data.passmarkAngleType == PassmarkAngleType::Intersection2
1019                 || m_data.passmarkAngleType == PassmarkAngleType::Intersection2OnlyRight)
1020                 && (side == PassmarkSide::All || side == PassmarkSide::Right))
1021         {
1022             // second passmark
1023             QLineF line(m_data.passmarkSAPoint, m_data.nextSAPoint);
1024             line.setAngle(line.angle()+90);
1025             lines += PassmarkIntersection(line, m_data.passmarkSAPoint.GetSAAfter(m_data.saWidth));
1026         }
1027 
1028         return lines;
1029     }
1030 
1031     return QVector<QLineF>();
1032 }
1033 
1034 //---------------------------------------------------------------------------------------------------------------------
SAPassmarkPath(const VPiece & piece,const VContainer * data,PassmarkSide side) const1035 QPainterPath VPassmark::SAPassmarkPath(const VPiece &piece, const VContainer *data, PassmarkSide side) const
1036 {
1037     return PassmarkToPath(SAPassmark(piece, data, side));
1038 }
1039 
1040 //---------------------------------------------------------------------------------------------------------------------
BuiltInSAPassmarkPath(const VPiece & piece,const VContainer * data) const1041 QPainterPath VPassmark::BuiltInSAPassmarkPath(const VPiece &piece, const VContainer *data) const
1042 {
1043     return PassmarkToPath(BuiltInSAPassmark(piece, data));
1044 }
1045