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 ¬chBase, 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