Seamly2D
Code documentation
vabstractpiece.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2017 Seamly, LLC *
4  * *
5  * https://github.com/fashionfreedom/seamly2d *
6  * *
7  ***************************************************************************
8  **
9  ** Seamly2D is free software: you can redistribute it and/or modify
10  ** it under the terms of the GNU General Public License as published by
11  ** the Free Software Foundation, either version 3 of the License, or
12  ** (at your option) any later version.
13  **
14  ** Seamly2D is distributed in the hope that it will be useful,
15  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  ** GNU General Public License for more details.
18  **
19  ** You should have received a copy of the GNU General Public License
20  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
21  **
22  **************************************************************************
23 
24  ************************************************************************
25  **
26  ** @file
27  ** @author Roman Telezhynskyi <dismine(at)gmail.com>
28  ** @date 3 11, 2016
29  **
30  ** @brief
31  ** @copyright
32  ** This source code is part of the Valentine project, a pattern making
33  ** program, whose allow create and modeling patterns of clothing.
34  ** Copyright (C) 2016 Seamly2D project
35  ** <https://github.com/fashionfreedom/seamly2d> All Rights Reserved.
36  **
37  ** Seamly2D is free software: you can redistribute it and/or modify
38  ** it under the terms of the GNU General Public License as published by
39  ** the Free Software Foundation, either version 3 of the License, or
40  ** (at your option) any later version.
41  **
42  ** Seamly2D is distributed in the hope that it will be useful,
43  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
44  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45  ** GNU General Public License for more details.
46  **
47  ** You should have received a copy of the GNU General Public License
48  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
49  **
50  *************************************************************************/
51 
52 #include "vabstractpiece.h"
53 #include "vabstractpiece_p.h"
54 #include "../vmisc/vabstractapplication.h"
55 #include "../vgeometry/vpointf.h"
56 
57 #include <QLineF>
58 #include <QSet>
59 #include <QVector>
60 #include <QPainterPath>
61 
62 const qreal maxL = 2.4;
63 
64 #ifdef Q_COMPILER_RVALUE_REFS
66 { Swap(piece); return *this; }
67 #endif
68 
69 void VAbstractPiece::Swap(VAbstractPiece &piece) Q_DECL_NOTHROW
70 { std::swap(d, piece.d); }
71 
72 //---------------------------------------------------------------------------------------------------------------------
74  : d(new VAbstractPieceData)
75 {}
76 
77 //---------------------------------------------------------------------------------------------------------------------
79  :d (piece.d)
80 {}
81 
82 //---------------------------------------------------------------------------------------------------------------------
84 {
85  if ( &piece == this )
86  {
87  return *this;
88  }
89  d = piece.d;
90  return *this;
91 }
92 
93 //---------------------------------------------------------------------------------------------------------------------
95 {}
96 
97 //---------------------------------------------------------------------------------------------------------------------
98 QString VAbstractPiece::GetName() const
99 {
100  return d->m_name;
101 }
102 
103 //---------------------------------------------------------------------------------------------------------------------
104 void VAbstractPiece::SetName(const QString &value)
105 {
106  d->m_name = value;
107 }
108 
109 
110 //---------------------------------------------------------------------------------------------------------------------
112 {
113  return d->m_color;
114 }
115 
116 //---------------------------------------------------------------------------------------------------------------------
117 void VAbstractPiece::setColor(const QString &value)
118 {
119  d->m_color = value;
120 }
121 
122 //---------------------------------------------------------------------------------------------------------------------
123 QString VAbstractPiece::getFill() const
124 {
125  return d->m_fill;
126 }
127 
128 //---------------------------------------------------------------------------------------------------------------------
129 void VAbstractPiece::setFill(const QString &value)
130 {
131  d->m_fill = value;
132 }
133 
134 //---------------------------------------------------------------------------------------------------------------------
136 {
137  return d->m_pieceLock;
138 }
139 
140 //---------------------------------------------------------------------------------------------------------------------
141 void VAbstractPiece::setLock(bool value)
142 {
143  d->m_pieceLock = value;
144 }
145 
146 //---------------------------------------------------------------------------------------------------------------------
148 {
149  return d->m_forbidFlipping;
150 }
151 
152 //---------------------------------------------------------------------------------------------------------------------
154 {
155  d->m_forbidFlipping = value;
156 }
157 
158 //---------------------------------------------------------------------------------------------------------------------
160 {
161  return d->m_seamAllowance;
162 }
163 
164 //---------------------------------------------------------------------------------------------------------------------
166 {
167  d->m_seamAllowance = value;
168 }
169 
170 //---------------------------------------------------------------------------------------------------------------------
172 {
173  return d->m_seamAllowanceBuiltIn;
174 }
175 
176 //---------------------------------------------------------------------------------------------------------------------
178 {
179  d->m_seamAllowanceBuiltIn = value;
180 }
181 
182 //---------------------------------------------------------------------------------------------------------------------
184 {
185  return d->m_hideMainPath;
186 }
187 
188 //---------------------------------------------------------------------------------------------------------------------
190 {
191  d->m_hideMainPath = value;
192 }
193 
194 //---------------------------------------------------------------------------------------------------------------------
196 {
197  return d->m_width;
198 }
199 
200 //---------------------------------------------------------------------------------------------------------------------
201 void VAbstractPiece::SetSAWidth(qreal value)
202 {
203  value >= 0 ? d->m_width = value : d->m_width = 0;
204 }
205 
206 //---------------------------------------------------------------------------------------------------------------------
208 {
209  if (width < 0)
210  {
211  qDebug()<<"Width < 0.";
212  return QVector<QPointF>();
213  }
214 
216  if ( p.size() < 3 )
217  {
218  qDebug()<<"Not enough points for building the equidistant.";
219  return QVector<QPointF>();
220  }
221 
222  if (p.last().toPoint() != p.first().toPoint())
223  {
224  p.append(p.at(0));// Should be always closed
225  }
226 
227  QVector<QPointF> ekvPoints;
228  for (qint32 i = 0; i < p.size(); ++i )
229  {
230  if ( i == 0)
231  {//first point
232  ekvPoints << EkvPoint(p.at(p.size()-2), p.at(p.size()-1),
233  p.at(1), p.at(0), width);
234  continue;
235  }
236 
237  if (i == p.size()-1)
238  {//last point
239  if (not ekvPoints.isEmpty())
240  {
241  ekvPoints.append(ekvPoints.at(0));
242  }
243  continue;
244  }
245  //points in the middle of polyline
246  ekvPoints << EkvPoint(p.at(i-1), p.at(i),
247  p.at(i+1), p.at(i), width);
248  }
249 
250  const bool removeFirstAndLast = false;
251  ekvPoints = CheckLoops(CorrectEquidistantPoints(ekvPoints, removeFirstAndLast));//Result path can contain loops
252  return ekvPoints;
253 }
254 
255 //---------------------------------------------------------------------------------------------------------------------
257 {
258  // Calculation a polygon area through the sum of the areas of trapezoids
259  qreal s, res = 0;
260  const int n = points.size();
261 
262  if(n > 2)
263  {
264  for (int i = 0; i < n; ++i)
265  {
266  if (i == 0)
267  {
268  //if i == 0, then y[i-1] replace on y[n-1]
269  s = points.at(i).x()*(points.at(n-1).y() - points.at(i+1).y());
270  res += s;
271  }
272  else
273  {
274  if (i == n-1)
275  {
276  // if i == n-1, then y[i+1] replace on y[0]
277  s = points.at(i).x()*(points.at(i-1).y() - points.at(0).y());
278  res += s;
279  }
280  else
281  {
282  s = points.at(i).x()*(points.at(i-1).y() - points.at(i+1).y());
283  res += s;
284  }
285  }
286  }
287  }
288  return res;
289 }
290 
291 /*
292  * @brief Checks for direction of a vector of points.
293  * @param points QVector of QPointF.
294  * @return true for clockwise direction.
295  * return false for counterclock-wise direction.
296  */
298 {
299  if(points.count() < 3)
300  {
301  return false;
302  }
303  else if (sumTrapezoids(points) < 0)
304  {
305  return true;
306  }
307  return false;
308 }
309 //---------------------------------------------------------------------------------------------------------------------
310 /**
311  * @brief CheckLoops seek and delete loops in equidistant.
312  * @param points vector of points of equidistant.
313  * @return vector of points of equidistant.
314  */
316 {
317  int count = points.size();
318  /*If we got less than 4 points no need seek loops.*/
319  if (count < 4)
320  {
321  return points;
322  }
323 
324  const bool pathClosed = (points.first() == points.last());
325 
326  QVector<QPointF> ekvPoints;
327 
328  qint32 i, j, jNext = 0;
329  for (i = 0; i < count; ++i)
330  {
331  /*Last three points no need check.*/
332  /*Triangle has not contain loops*/
333  if (i > count-3)
334  {
335  ekvPoints.append(points.at(i));
336  continue;
337  }
338 
339  enum LoopIntersectType { NoIntersection, BoundedIntersection, ParallelIntersection };
340 
341  QPointF crosPoint;
342  LoopIntersectType status = NoIntersection;
343  const QLineF line1(points.at(i), points.at(i+1));
344  // Because a path can contains several loops we will seek the last and only then remove the loop(s)
345  // That's why we parse from the end
346  for (j = count-1; j >= i+2; --j)
347  {
348  j == count-1 ? jNext = 0 : jNext = j+1;
349  QLineF line2(points.at(j), points.at(jNext));
350 
351  if(qFuzzyIsNull(line2.length()))
352  {//If a path is closed the edge (count-1;0) length will be 0
353  continue;
354  }
355 
356  QVector<qint32> uniqueVertices;
357 
358  auto AddUniqueIndex = [&uniqueVertices](qint32 i)
359  {
360  if (not uniqueVertices.contains(i))
361  {
362  uniqueVertices.append(i);
363  }
364  };
365 
366  AddUniqueIndex(i);
367  AddUniqueIndex(i+1);
368  AddUniqueIndex(j);
369 
370  // For closed path last point is equal to first. Using index of the first.
371  pathClosed && jNext == count-1 ? AddUniqueIndex(0) : AddUniqueIndex(jNext);
372 
373  const QLineF::IntersectType intersect = line1.intersects(line2, &crosPoint);
374  if (intersect == QLineF::NoIntersection)
375  { // According to the documentation QLineF::NoIntersection indicates that the lines do not intersect;
376  // i.e. they are parallel. But parallel also mean they can be on the same line.
377  // Method IsPointOnLineviaPDP will check it.
378  if (VGObject::IsPointOnLineviaPDP(points.at(j), points.at(i), points.at(i+1))
379  // Lines are not neighbors
380  && uniqueVertices.size() == 4)
381  {
382  // Left to catch case where segments are on the same line, but do not have real intersections.
383  QLineF tmpLine1 = line1;
384  QLineF tmpLine2 = line2;
385 
386  tmpLine1.setAngle(tmpLine1.angle()+90);
387 
388  QPointF tmpCrosPoint;
389  const QLineF::IntersectType tmpIntrs1 = tmpLine1.intersects(tmpLine2, &tmpCrosPoint);
390 
391  tmpLine1 = line1;
392  tmpLine2.setAngle(tmpLine2.angle()+90);
393 
394  const QLineF::IntersectType tmpIntrs2 = tmpLine1.intersects(tmpLine2, &tmpCrosPoint);
395 
396  if (tmpIntrs1 == QLineF::BoundedIntersection || tmpIntrs2 == QLineF::BoundedIntersection)
397  { // Now we really sure that lines are on the same lines and have real intersections.
398  QPointF cPoint;
399  const bool caseFlag = ParallelCrossPoint(line1, line2, cPoint);
400  if (not caseFlag || CheckIntersection(points, i, i+1, j, jNext, cPoint))
401  {
402  status = ParallelIntersection;
403  break;
404  }
405  }
406  }
407  }
408  else if (intersect == QLineF::BoundedIntersection)
409  {
410  if (uniqueVertices.size() == 4)
411  { // Break, but not if lines are neighbors
412  if ((line1.p1() != crosPoint
413  && line1.p2() != crosPoint
414  && line2.p1() != crosPoint
415  && line2.p2() != crosPoint) || CheckIntersection(points, i, i+1, j, jNext, crosPoint))
416  {
417  status = BoundedIntersection;
418  break;
419  }
420  }
421  }
422  status = NoIntersection;
423  }
424 
425  switch (status)
426  {
427  case ParallelIntersection:
428  /*We have found a loop.*/
429  ekvPoints.append(points.at(i));
430  ekvPoints.append(points.at(jNext));
431  jNext > j ? i = jNext : i = j; // Skip a loop
432  break;
433  case BoundedIntersection:
434  ekvPoints.append(points.at(i));
435  ekvPoints.append(crosPoint);
436  i = j;
437  break;
438  case NoIntersection:
439  /*We have not found loop.*/
440  ekvPoints.append(points.at(i));
441  break;
442  default:
443  break;
444  }
445  }
446  return ekvPoints;
447 }
448 
449 //---------------------------------------------------------------------------------------------------------------------
450 Q_DECL_CONSTEXPR qreal VAbstractPiece::PointPosition(const QPointF &p, const QLineF &line)
451 {
452  return (line.p2().x() - line.p1().x()) * (p.y() - line.p1().y()) -
453  (line.p2().y() - line.p1().y()) * (p.x() - line.p1().x());
454 }
455 
456 //---------------------------------------------------------------------------------------------------------------------
457 qreal VAbstractPiece::MaxLocalSA(const VSAPoint &p, qreal width)
458 {
459  qreal w1 = p.GetSAAfter();
460  if (w1 < 0)
461  {
462  w1 = width;
463  }
464 
465  qreal w2 = p.GetSABefore();
466  if (w2 < 0)
467  {
468  w2 = width;
469  }
470 
471  return qMax(w1, w2);
472 }
473 
474 //---------------------------------------------------------------------------------------------------------------------
475 /**
476  * @brief EkvPoint return seam allowance points in place of intersection of two edges. Last points of two edges
477  * should be equal.
478  * @param width global seam allowance width.
479  * @return seam allowance points.
480  */
482  const VSAPoint &p1Line2, const VSAPoint &p2Line2, qreal width)
483 {
484  if (width < 0)
485  { // width can't be < 0
486  return QVector<QPointF>();
487  }
488 
489  QVector<QPointF> points;
490  if (p2Line1 != p2Line2)
491  {
492  qDebug()<<"Last points of two lines must be equal.";
493  return QVector<QPointF>(); // Wrong edges
494  }
495 
496  const QLineF bigLine1 = createParallelLine(p1Line1, p2Line1, width );
497  const QLineF bigLine2 = createParallelLine(p2Line2, p1Line2, width );
498  QPointF CrosPoint;
499  const QLineF::IntersectType type = bigLine1.intersects( bigLine2, &CrosPoint );
500  switch (type)
501  {// There are at least three big cases
502  case (QLineF::BoundedIntersection):
503  // The easiest, real intersection
504  points.append(CrosPoint);
505  return points;
506  case (QLineF::UnboundedIntersection):
507  { // Most common case
508  const qreal localWidth = MaxLocalSA(p2Line1, width);
509  QLineF line( p2Line1, CrosPoint );
510 
511  // Checking two subcases
512  const QLineF b1 = BisectorLine(p1Line1, p2Line1, p1Line2);
513  const QLineF b2 = BisectorLine(bigLine1.p1(), CrosPoint, bigLine2.p2());
514 
515  const qreal angle = AngleBetweenBisectors(b1, b2);
516 
517  // Comparison bisector angles helps to find direction
518  if (angle < 90
519  || VFuzzyComparePossibleNulls(angle, 90.0))// Go in a same direction
520  {//Regular equdistant case
521 QT_WARNING_PUSH
522 QT_WARNING_DISABLE_GCC("-Wswitch-default")
523  switch (p2Line1.GetAngleType())
524  {
526  return AngleByLength(p2Line1, bigLine1.p1(), CrosPoint, bigLine2.p2(), localWidth);
528  return AngleByIntersection(p1Line1, p2Line1, p1Line2, bigLine1.p1(), CrosPoint, bigLine2.p2(),
529  localWidth);
531  return AngleByFirstSymmetry(p1Line1, p2Line1, bigLine1.p1(), CrosPoint, bigLine2.p2(),
532  localWidth);
534  return AngleBySecondSymmetry(p2Line1, p1Line2, bigLine1.p1(), CrosPoint,bigLine2.p2(),
535  localWidth);
537  return AngleByFirstRightAngle(p1Line1, p2Line1, bigLine1.p1(), CrosPoint, bigLine2.p2(),
538  localWidth);
540  return AngleBySecondRightAngle(p2Line1, p1Line2, bigLine1.p1(), CrosPoint, bigLine2.p2(),
541  localWidth);
542  }
544  }
545  else
546  { // Different directions
547  QLineF bisector(p2Line1, p1Line1);
548  bisector.setAngle(b1.angle());
549 
550  const qreal result1 = PointPosition(bisector.p2(), QLineF(p1Line1, p2Line1));
551  const qreal result2 = PointPosition(bisector.p2(), QLineF(p2Line2, p1Line2));
552 
553  if ((result1 < 0 || qFuzzyIsNull(result1)) && (result2 < 0 || qFuzzyIsNull(result2)))
554  {// Dart case. A bisector watch outside. In some cases a point still valid, but ignore if going
555  // outside of an equdistant.
556 
557  const QLineF bigEdge = createParallelLine(p1Line1, p1Line2, localWidth );
558  QPointF px;
559  const QLineF::IntersectType type = bigEdge.intersects(line, &px);
560  if (type != QLineF::BoundedIntersection)
561  {
562  if (line.length() < QLineF(p2Line1, px).length())
563  {
564  points.append(CrosPoint);
565  return points;
566  }
567  }
568  }
569  else
570  { // New subcase. This is not a dart. An angle is acute and bisector watch inside.
571  const qreal result1 = PointPosition(CrosPoint, QLineF(p1Line1, p2Line1));
572  const qreal result2 = PointPosition(CrosPoint, QLineF(p2Line2, p1Line2));
573 
574  if ((result1 < 0 || qFuzzyIsNull(result1)) && (result2 < 0 || qFuzzyIsNull(result2)))
575  {// The cross point is still outside of a piece
576  if (line.length() >= localWidth)
577  {
578  points.append(CrosPoint);
579  return points;
580  }
581  else
582  {// but not enough far, fix it
583  line.setLength(localWidth);
584  points.append(line.p2());
585  return points;
586  }
587  }
588  else
589  {// Wrong cross point, probably inside of a piece. Manually creating correct seam allowance
590  const QLineF bigEdge = createParallelLine(bigLine1.p2(), bigLine2.p1(), localWidth );
591  points.append(bigEdge.p1());
592  points.append(bigEdge.p2());
593  return points;
594  }
595  }
596  }
597  break;
598  }
599  case (QLineF::NoIntersection):
600  /*If we have correct lines this means lines lie on a line.*/
601  points.append(bigLine1.p2());
602  return points;
603  default:
604  break;
605  }
606  return points;
607 }
608 
609 //---------------------------------------------------------------------------------------------------------------------
610 QVector<QPointF> VAbstractPiece::AngleByLength(const QPointF &p2, const QPointF &sp1, const QPointF &sp2,
611  const QPointF &sp3, qreal width)
612 {
613  QVector<QPointF> points;
614 
615  QLineF line(p2, sp2);
616  const qreal length = line.length();
617  if (length > width*maxL)
618  { // Cutting too long a cut angle
619  line.setLength(width);
620  QLineF cutLine(line.p2(), sp2); // Cut line is a perpendicular
621  cutLine.setLength(length); // Decided take this length
622 
623  // We do not check intersection type because intersection must alwayse exist
624  QPointF px;
625  cutLine.setAngle(cutLine.angle()+90);
626  QLineF::IntersectType type = QLineF(sp1, sp2).intersects(cutLine, &px);
627  if (type == QLineF::NoIntersection)
628  {
629  qDebug()<<"Couldn't find intersection with cut line.";
630  }
631  points.append(px);
632 
633  cutLine.setAngle(cutLine.angle()-180);
634  type = QLineF(sp2, sp3).intersects(cutLine, &px);
635  if (type == QLineF::NoIntersection)
636  {
637  qDebug()<<"Couldn't find intersection with cut line.";
638  }
639  points.append(px);
640  }
641  else
642  { // The point just fine
643  points.append(sp2);
644  }
645  return points;
646 }
647 
648 //---------------------------------------------------------------------------------------------------------------------
649 QVector<QPointF> VAbstractPiece::AngleByIntersection(const QPointF &p1, const QPointF &p2, const QPointF &p3,
650  const QPointF &sp1, const QPointF &sp2, const QPointF &sp3,
651  qreal width)
652 {
653  QVector<QPointF> points;
654 
655  QLineF edge2(p2, p3);
656  QLineF sEdge1(sp1, sp2);
657 
658  QPointF px;
659  QLineF::IntersectType type = edge2.intersects(sEdge1, &px);
660  if (type == QLineF::NoIntersection)
661  {
662  return AngleByLength(p2, sp1, sp2, sp3, width);
663  }
664 
665  if (QLineF(p2, px).length() > width*maxL)
666  {
667  return AngleByLength(p2, sp1, sp2, sp3, width);
668  }
669  points.append(px);
670 
671  QLineF edge1(p1, p2);
672  QLineF sEdge2(sp2, sp3);
673 
674  type = edge1.intersects(sEdge2, &px);
675  if (type == QLineF::NoIntersection)
676  {
677  return AngleByLength(p2, sp1, sp2, sp3, width);
678  }
679 
680  if (QLineF(p2, px).length() > width*maxL)
681  {
682  return AngleByLength(p2, sp1, sp2, sp3, width);
683  }
684  points.append(px);
685 
686  return points;
687 }
688 
689 //---------------------------------------------------------------------------------------------------------------------
690 QVector<QPointF> VAbstractPiece::AngleByFirstSymmetry(const QPointF &p1, const QPointF &p2,
691  const QPointF &sp1, const QPointF &sp2, const QPointF &sp3,
692  qreal width)
693 {
694  QVector<QPointF> points;
695 
696  QLineF sEdge2(sp2, sp3);
697  QPointF fp1 = VPointF::FlipPF(sEdge2, p1);
698  QPointF fp2 = VPointF::FlipPF(sEdge2, p2);
699  QLineF fEdge(fp1, fp2);
700 
701  QPointF px;
702  QLineF sEdge1(sp1, sp2);
703  QLineF::IntersectType type = fEdge.intersects(sEdge1, &px);
704  if (type == QLineF::NoIntersection)
705  {
706  return AngleByLength(p2, sp1, sp2, sp3, width);
707  }
708 
709  if (QLineF(p2, px).length() > width*maxL)
710  {
711  return AngleByLength(p2, sp1, sp2, sp3, width);
712  }
713  points.append(px);
714 
715  type = fEdge.intersects(sEdge2, &px);
716  if (type == QLineF::NoIntersection)
717  {
718  return AngleByLength(p2, sp1, sp2, sp3, width);
719  }
720 
721  if (QLineF(p2, px).length() > width*maxL)
722  {
723  return AngleByLength(p2, sp1, sp2, sp3, width);
724  }
725  points.append(px);
726 
727  return points;
728 }
729 
730 //---------------------------------------------------------------------------------------------------------------------
731 QVector<QPointF> VAbstractPiece::AngleBySecondSymmetry(const QPointF &p2, const QPointF &p3,
732  const QPointF &sp1, const QPointF &sp2, const QPointF &sp3,
733  qreal width)
734 {
735  QVector<QPointF> points;
736 
737  QLineF sEdge1(sp1, sp2);
738  QPointF fp2 = VPointF::FlipPF(sEdge1, p2);
739  QPointF fp3 = VPointF::FlipPF(sEdge1, p3);
740  QLineF fEdge(fp2, fp3);
741 
742  QPointF px;
743  QLineF::IntersectType type = fEdge.intersects(sEdge1, &px);
744  if (type == QLineF::NoIntersection)
745  {
746  return AngleByLength(p2, sp1, sp2, sp3, width);
747  }
748 
749  if (QLineF(p2, px).length() > width*maxL)
750  {
751  return AngleByLength(p2, sp1, sp2, sp3, width);
752  }
753  points.append(px);
754 
755  QLineF sEdge2(sp2, sp3);
756  type = fEdge.intersects(sEdge2, &px);
757  if (type == QLineF::NoIntersection)
758  {
759  return AngleByLength(p2, sp1, sp2, sp3, width);
760  }
761 
762  if (QLineF(p2, px).length() > width*maxL)
763  {
764  return AngleByLength(p2, sp1, sp2, sp3, width);
765  }
766  points.append(px);
767 
768  return points;
769 }
770 
771 //---------------------------------------------------------------------------------------------------------------------
772 QVector<QPointF> VAbstractPiece::AngleByFirstRightAngle(const QPointF &p1, const QPointF &p2,
773  const QPointF &sp1, const QPointF &sp2, const QPointF &sp3,
774  qreal width)
775 {
776  QVector<QPointF> points;
777 
778  QLineF edge1(p2, p1);
779  edge1.setAngle(edge1.angle()-90);
780 
781  QPointF px;
782  QLineF::IntersectType type = edge1.intersects(QLineF(sp1, sp2), &px);
783  if (type == QLineF::NoIntersection)
784  {
785  return AngleByLength(p2, sp1, sp2, sp3, width);
786  }
787 
788  if (QLineF(p2, px).length() > width*maxL)
789  {
790  return AngleByLength(p2, sp1, sp2, sp3, width);
791  }
792  points.append(px);
793 
794  type = edge1.intersects(QLineF(sp2, sp3), &px);
795  if (type == QLineF::NoIntersection)
796  {
797  return AngleByLength(p2, sp1, sp2, sp3, width);
798  }
799 
800  if (QLineF(p2, px).length() > width*maxL)
801  {
802  return AngleByLength(p2, sp1, sp2, sp3, width);
803  }
804  points.append(px);
805 
806  return points;
807 }
808 
809 //---------------------------------------------------------------------------------------------------------------------
810 QVector<QPointF> VAbstractPiece::AngleBySecondRightAngle(const QPointF &p2, const QPointF &p3,
811  const QPointF &sp1, const QPointF &sp2, const QPointF &sp3,
812  qreal width)
813 {
814  QVector<QPointF> points;
815 
816  QLineF edge2(p2, p3);
817  edge2.setAngle(edge2.angle()+90);
818 
819  QPointF px;
820  QLineF::IntersectType type = edge2.intersects(QLineF(sp1, sp2), &px);
821  if (type == QLineF::NoIntersection)
822  {
823  return AngleByLength(p2, sp1, sp2, sp3, width);
824  }
825 
826  if (QLineF(p2, px).length() > width*maxL)
827  {
828  return AngleByLength(p2, sp1, sp2, sp3, width);
829  }
830  points.append(px);
831 
832  type = edge2.intersects(QLineF(sp2, sp3), &px);
833  if (type == QLineF::NoIntersection)
834  {
835  return AngleByLength(p2, sp1, sp2, sp3, width);
836  }
837 
838  if (QLineF(p2, px).length() > width*maxL)
839  {
840  return AngleByLength(p2, sp1, sp2, sp3, width);
841  }
842  points.append(px);
843 
844  return points;
845 }
846 
847 //---------------------------------------------------------------------------------------------------------------------
848 QLineF VAbstractPiece::createParallelLine(const VSAPoint &p1, const VSAPoint &p2, qreal width)
849 {
850  qreal w1 = p1.GetSAAfter();
851  if (w1 < 0)
852  {
853  w1 = width;
854  }
855 
856  qreal w2 = p2.GetSABefore();
857  if (w2 < 0)
858  {
859  w2 = width;
860  }
861 
862  const QLineF parallel = QLineF(SingleParallelPoint(p1, p2, 90, w1),
863  SingleParallelPoint(p2, p1, -90, w2));
864  return parallel;
865 }
866 
867 //---------------------------------------------------------------------------------------------------------------------
868 QLineF VAbstractPiece::createParallelLine(const QPointF &p1, const QPointF &p2, qreal width)
869 {
870  const QLineF parallel = QLineF(SingleParallelPoint(p1, p2, 90, width),
871  SingleParallelPoint(p2, p1, -90, width));
872  return parallel;
873 }
874 
875 //---------------------------------------------------------------------------------------------------------------------
876 QPointF VAbstractPiece::SingleParallelPoint(const QPointF &p1, const QPointF &p2, qreal angle, qreal width)
877 {
878  QLineF pLine(p1, p2);
879  pLine.setAngle( pLine.angle() + angle );
880  pLine.setLength( width );
881  return pLine.p2();
882 }
883 
884 //---------------------------------------------------------------------------------------------------------------------
885 QLineF VAbstractPiece::BisectorLine(const QPointF &p1, const QPointF &p2, const QPointF &p3)
886 {
887  QLineF line1(p2, p1);
888  QLineF line2(p2, p3);
889  QLineF bLine;
890 
891  const qreal angle1 = line1.angleTo(line2);
892  const qreal angle2 = line2.angleTo(line1);
893 
894  if (angle1 <= angle2)
895  {
896  bLine = line1;
897  bLine.setAngle(bLine.angle() + angle1/2.0);
898  }
899  else
900  {
901  bLine = line2;
902  bLine.setAngle(bLine.angle() + angle2/2.0);
903  }
904 
905  return bLine;
906 }
907 
908 //---------------------------------------------------------------------------------------------------------------------
909 qreal VAbstractPiece::AngleBetweenBisectors(const QLineF &b1, const QLineF &b2)
910 {
911  const QLineF newB2 = b2.translated(-(b2.p1().x() - b1.p1().x()), -(b2.p1().y() - b1.p1().y()));
912 
913  qreal angle1 = newB2.angleTo(b1);
914  if (VFuzzyComparePossibleNulls(angle1, 360))
915  {
916  angle1 = 0;
917  }
918 
919  qreal angle2 = b1.angleTo(newB2);
920  if (VFuzzyComparePossibleNulls(angle2, 360))
921  {
922  angle2 = 0;
923  }
924 
925  if (angle1 <= angle2)
926  {
927  return angle1;
928  }
929  else
930  {
931  return angle2;
932  }
933 }
934 
935 //---------------------------------------------------------------------------------------------------------------------
936 bool VAbstractPiece::CheckIntersection(const QVector<QPointF> &points, int i, int iNext, int j, int jNext,
937  const QPointF &crossPoint)
938 {
939  QVector<QPointF> sub1 = SubPath(points, iNext, j);
940  sub1.append(crossPoint);
941  sub1 = CheckLoops(CorrectEquidistantPoints(sub1, false));
942  const qreal sub1Sum = sumTrapezoids(sub1);
943 
944  QVector<QPointF> sub2 = SubPath(points, jNext, i);
945  sub2.append(crossPoint);
946  sub2 = CheckLoops(CorrectEquidistantPoints(sub2, false));
947  const qreal sub2Sum = sumTrapezoids(sub2);
948 
949  if (sub1Sum < 0 && sub2Sum < 0)
950  {
951  if (Crossing(sub1, sub2))
952  {
953  return true;
954  }
955  }
956  else
957  {
958  if (not Crossing(sub1, sub2))
959  {
960  return true;
961  }
962  }
963  return false;
964 }
965 
966 //---------------------------------------------------------------------------------------------------------------------
967 bool VAbstractPiece::ParallelCrossPoint(const QLineF &line1, const QLineF &line2, QPointF &point)
968 {
969  const bool l1p1el2p1 = (line1.p1() == line2.p1());
970  const bool l1p2el2p2 = (line1.p2() == line2.p2());
971  const bool l1p1el2p2 = (line1.p1() == line2.p2());
972  const bool l1p2el2p1 = (line1.p2() == line2.p1());
973 
974  if (l1p2el2p2 || l1p2el2p1)
975  {
976  point = line1.p2();
977  return true;
978  }
979  else if (l1p1el2p1 || l1p1el2p2)
980  {
981  point = line1.p1();
982  return true;
983  }
984  else
985  {
986  point = QPointF();
987  return false;
988  }
989 }
990 
991 //---------------------------------------------------------------------------------------------------------------------
993 {
994  if (sub1.isEmpty() || sub2.isEmpty())
995  {
996  return false;
997  }
998 
999  const QRectF sub1Rect = QPolygonF(sub1).boundingRect();
1000  const QRectF sub2Rect = QPolygonF(sub2).boundingRect();
1001  if (not sub1Rect.intersects(sub2Rect))
1002  {
1003  return false;
1004  }
1005 
1006  QPainterPath sub1Path;
1007  sub1Path.setFillRule(Qt::WindingFill);
1008  sub1Path.moveTo(sub1.at(0));
1009  for (qint32 i = 1; i < sub1.count(); ++i)
1010  {
1011  sub1Path.lineTo(sub1.at(i));
1012  }
1013  sub1Path.lineTo(sub1.at(0));
1014 
1015  QPainterPath sub2Path;
1016  sub2Path.setFillRule(Qt::WindingFill);
1017  sub2Path.moveTo(sub2.at(0));
1018  for (qint32 i = 1; i < sub2.count(); ++i)
1019  {
1020  sub2Path.lineTo(sub2.at(i));
1021  }
1022  sub2Path.lineTo(sub2.at(0));
1023 
1024  if (not sub1Path.intersects(sub2Path))
1025  {
1026  return false;
1027  }
1028  else
1029  {
1030  return true;
1031  }
1032 }
1033 
1034 //---------------------------------------------------------------------------------------------------------------------
1035 QVector<QPointF> VAbstractPiece::SubPath(const QVector<QPointF> &path, int startIndex, int endIndex)
1036 {
1037  if (path.isEmpty()
1038  || startIndex < 0 || startIndex >= path.size()
1039  || endIndex < 0 || endIndex >= path.size()
1040  || startIndex == endIndex)
1041  {
1042  return path;
1043  }
1044 
1045  QVector<QPointF> subPath;
1046  int i = startIndex - 1;
1047  do
1048  {
1049  ++i;
1050  if (i >= path.size())
1051  {
1052  i = 0;
1053  }
1054  subPath.append(path.at(i));
1055  } while (i != endIndex);
1056 
1057  return subPath;
1058 }
1059 
1060 //---------------------------------------------------------------------------------------------------------------------
1061 bool VAbstractPiece::IsEkvPointOnLine(const QPointF &iPoint, const QPointF &prevPoint, const QPointF &nextPoint)
1062 {
1063  return VGObject::IsPointOnLineviaPDP(iPoint, prevPoint, nextPoint);
1064 }
1065 
1066 //---------------------------------------------------------------------------------------------------------------------
1067 bool VAbstractPiece::IsEkvPointOnLine(const VSAPoint &iPoint, const VSAPoint &prevPoint, const VSAPoint &nextPoint)
1068 {
1069  // See bug #671
1070  const qreal tmpWidth = 10;
1071  const QLineF bigLine1 = createParallelLine(prevPoint, iPoint, tmpWidth );
1072  const QLineF bigLine2 = createParallelLine(iPoint, nextPoint, tmpWidth );
1073 
1074  return (VGObject::IsPointOnLineviaPDP(iPoint, prevPoint, nextPoint)
1075  && VGObject::IsPointOnLineviaPDP(bigLine1.p2(), bigLine1.p1(), bigLine2.p2())
1076  && VGObject::IsPointOnLineviaPDP(bigLine2.p1(), bigLine1.p1(), bigLine2.p2())
1077  && qAbs(prevPoint.GetSAAfter(tmpWidth) - nextPoint.GetSABefore(tmpWidth)) < VGObject::accuracyPointOnLine);
1078 }
1079 
1080 //---------------------------------------------------------------------------------------------------------------------
1082 {
1083  return d->m_mx;
1084 }
1085 
1086 //---------------------------------------------------------------------------------------------------------------------
1087 void VAbstractPiece::SetMx(qreal value)
1088 {
1089  d->m_mx = value;
1090 }
1091 
1092 //---------------------------------------------------------------------------------------------------------------------
1094 {
1095  return d->m_my;
1096 }
1097 
1098 //---------------------------------------------------------------------------------------------------------------------
1099 void VAbstractPiece::SetMy(qreal value)
1100 {
1101  d->m_my = value;
1102 }
1103 
1104 //---------------------------------------------------------------------------------------------------------------------
1105 qreal VSAPoint::GetSABefore(qreal width) const
1106 {
1107  if (m_before < 0)
1108  {
1109  return width;
1110  }
1111  return m_before;
1112 }
1113 
1114 //---------------------------------------------------------------------------------------------------------------------
1115 qreal VSAPoint::GetSAAfter(qreal width) const
1116 {
1117  if (m_after < 0)
1118  {
1119  return width;
1120  }
1121  return m_after;
1122 }
static QVector< QPointF > AngleByFirstRightAngle(const QPointF &p1, const QPointF &p2, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width)
bool IsSeamAllowanceBuiltIn() const
bool IsSeamAllowance() const
bool isHideSeamLine() const
static bool isClockwise(const QVector< QPointF > &points)
void setFill(const QString &value)
static bool IsEkvPointOnLine(const QPointF &iPoint, const QPointF &prevPoint, const QPointF &nextPoint)
void SetMx(qreal value)
bool getLock() const
static QVector< QPointF > Equidistant(const QVector< VSAPoint > &points, qreal width)
qreal GetSAWidth() const
void setLock(bool value)
static bool ParallelCrossPoint(const QLineF &line1, const QLineF &line2, QPointF &point)
static QVector< QPointF > AngleBySecondRightAngle(const QPointF &p2, const QPointF &p3, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width)
QString getColor() const
static QLineF createParallelLine(const VSAPoint &p1, const VSAPoint &p2, qreal width)
static bool Crossing(const QVector< QPointF > &sub1, const QVector< QPointF > &sub2)
static Q_DECL_CONSTEXPR qreal PointPosition(const QPointF &p, const QLineF &line)
static qreal sumTrapezoids(const QVector< QPointF > &points)
qreal GetMx() const
static QVector< QPointF > AngleByLength(const QPointF &p2, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width)
QSharedDataPointer< VAbstractPieceData > d
void SetSAWidth(qreal value)
static QVector< QPointF > EkvPoint(const VSAPoint &p1Line1, const VSAPoint &p2Line1, const VSAPoint &p1Line2, const VSAPoint &p2Line2, qreal width)
EkvPoint return seam allowance points in place of intersection of two edges. Last points of two edges...
static QVector< QPointF > AngleBySecondSymmetry(const QPointF &p2, const QPointF &p3, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width)
void SetForbidFlipping(bool value)
static qreal AngleBetweenBisectors(const QLineF &b1, const QLineF &b2)
static QPointF SingleParallelPoint(const QPointF &p1, const QPointF &p2, qreal angle, qreal width)
void setColor(const QString &value)
static qreal MaxLocalSA(const VSAPoint &p, qreal width)
static QVector< QPointF > SubPath(const QVector< QPointF > &path, int startIndex, int endIndex)
void setHideSeamLine(bool value)
void SetName(const QString &value)
void SetSeamAllowanceBuiltIn(bool value)
void Swap(VAbstractPiece &piece) Q_DECL_NOTHROW
void SetSeamAllowance(bool value)
virtual ~VAbstractPiece()
QString GetName() const
qreal GetMy() const
static QVector< QPointF > CheckLoops(const QVector< QPointF > &points)
CheckLoops seek and delete loops in equidistant.
static QVector< QPointF > AngleByIntersection(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width)
QString getFill() const
static QVector< QPointF > AngleByFirstSymmetry(const QPointF &p1, const QPointF &p2, const QPointF &sp1, const QPointF &sp2, const QPointF &sp3, qreal width)
static QVector< T > CorrectEquidistantPoints(const QVector< T > &points, bool removeFirstAndLast=true)
CorrectEquidistantPoints clear equivalent points and remove point on line from equdistant.
bool IsForbidFlipping() const
void SetMy(qreal value)
static QLineF BisectorLine(const QPointF &p1, const QPointF &p2, const QPointF &p3)
static bool CheckIntersection(const QVector< QPointF > &points, int i, int iNext, int j, int jNext, const QPointF &crossPoint)
VAbstractPiece & operator=(const VAbstractPiece &piece)
static bool IsPointOnLineviaPDP(const QPointF &t, const QPointF &p1, const QPointF &p2)
IsPointOnLineviaPDP use the perp dot product (PDP) way.
Definition: vgobject.cpp:516
static const double accuracyPointOnLine
Definition: vgobject.h:126
static QPointF FlipPF(const QLineF &axis, const QPointF &point)
Definition: vpointf.cpp:279
The VSAPoint class seam allowance point.
qreal m_after
Q_DECL_CONSTEXPR PieceNodeAngle GetAngleType() const
Q_DECL_CONSTEXPR qreal GetSABefore() const
qreal m_before
Q_DECL_CONSTEXPR qreal GetSAAfter() const
static Q_REQUIRED_RESULT bool VFuzzyComparePossibleNulls(double p1, double p2)
Definition: def.h:490
const qreal maxL