Seamly2D
Code documentation
vposition.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  ** @file vposition.cpp
3  ** @author Douglas S Caskey
4  ** @date Dec 11, 2022
5  **
6  ** @copyright
7  ** Copyright (C) 2017 - 2022 Seamly, LLC
8  ** https://github.com/fashionfreedom/seamly2d
9  **
10  ** @brief
11  ** Seamly2D is free software: you can redistribute it and/or modify
12  ** it under the terms of the GNU General Public License as published by
13  ** the Free Software Foundation, either version 3 of the License, or
14  ** (at your option) any later version.
15  **
16  ** Seamly2D is distributed in the hope that it will be useful,
17  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  ** GNU General Public License for more details.
20  **
21  ** You should have received a copy of the GNU General Public License
22  ** along with Seamly2D. if not, see <http://www.gnu.org/licenses/>.
23  **************************************************************************/
24 
25 /************************************************************************
26  **
27  ** @file vposition.cpp
28  ** @author Roman Telezhynskyi <dismine(at)gmail.com>
29  ** @date 20 1, 2015
30  **
31  ** @brief
32  ** @copyright
33  ** This source code is part of the Valentina project, a pattern making
34  ** program, whose allow create and modeling patterns of clothing.
35  ** Copyright (C) 2013-2015 Valentina project
36  ** <https://bitbucket.org/dismine/valentina> All Rights Reserved.
37  **
38  ** Valentina is free software: you can redistribute it and/or modify
39  ** it under the terms of the GNU General Public License as published by
40  ** the Free Software Foundation, either version 3 of the License, or
41  ** (at your option) any later version.
42  **
43  ** Valentina is distributed in the hope that it will be useful,
44  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
45  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46  ** GNU General Public License for more details.
47  **
48  ** You should have received a copy of the GNU General Public License
49  ** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
50  **
51  *************************************************************************/
52 
53 #include "vposition.h"
54 
55 #include <QDir>
56 #include <QImage>
57 #include <QLineF>
58 #include <QPainter>
59 #include <QPainterPath>
60 #include <QPen>
61 #include <QPicture>
62 #include <QPointF>
63 #include <QPolygonF>
64 #include <QRect>
65 #include <QRectF>
66 #include <QSizeF>
67 #include <QStaticStringData>
68 #include <QString>
69 #include <QStringData>
70 #include <QStringDataPtr>
71 #include <Qt>
72 
73 #include "../vmisc/def.h"
74 #include "../vmisc/vmath.h"
75 
76 //---------------------------------------------------------------------------------------------------------------------
77 VPosition::VPosition(const VContour &gContour, int j, const VLayoutPiece &piece, int i, std::atomic_bool *stop,
78  bool rotate, int rotationIncrease, bool saveLength)
79  : QRunnable(),
80  bestResult(VBestSquare(gContour.GetSize(), saveLength)),
81  gContour(gContour),
82  piece(piece),
83  i(i),
84  j(j),
85  paperIndex(0),
86  frame(0),
87  piecesCount(0),
88  pieces(),
89  stop(stop),
90  rotate(rotate),
91  rotationIncrease(rotationIncrease),
92  angle_between(0)
93 {
94  if ((rotationIncrease >= 1 && rotationIncrease <= 180 && 360 % rotationIncrease == 0) == false)
95  {
96  this->rotationIncrease = 180;
97  }
98 }
99 
100 //---------------------------------------------------------------------------------------------------------------------
102 {
103  if (stop->load())
104  {
105  return;
106  }
107 
108  // We should use copy of the piece.
109  VLayoutPiece workpiece = piece;
110 
111  int dEdge = i;// For mirror piece edge will be different
112  if (CheckCombineEdges(workpiece, j, dEdge))
113  {
114  #ifdef LAYOUT_DEBUG
115  # ifdef SHOW_CANDIDATE_BEST
117  # endif
118  #endif
119 
120  SaveCandidate(bestResult, workpiece, j, dEdge, BestFrom::Combine);
121  }
122  frame = frame + 3;
123 
124  if (rotate)
125  {
127  }
128  else
129  {
130  if (gContour.GetContour().isEmpty())
131  {
133  }
134  }
135 }
136 
137 //---------------------------------------------------------------------------------------------------------------------
138 // cppcheck-suppress unusedFunction
140 {
141  return paperIndex;
142 }
143 
144 //---------------------------------------------------------------------------------------------------------------------
145 void VPosition::setPaperIndex(const quint32 &value)
146 {
147  paperIndex = value;
148 }
149 
150 //---------------------------------------------------------------------------------------------------------------------
151 // cppcheck-suppress unusedFunction
152 quint32 VPosition::getFrame() const
153 {
154  return frame;
155 }
156 
157 //---------------------------------------------------------------------------------------------------------------------
158 void VPosition::setFrame(const quint32 &value)
159 {
160  frame = value;
161 }
162 
163 //---------------------------------------------------------------------------------------------------------------------
164 // cppcheck-suppress unusedFunction
166 {
167  return piecesCount;
168 }
169 
170 //---------------------------------------------------------------------------------------------------------------------
171 void VPosition::setPieceCount(const quint32 &value)
172 {
173  piecesCount = value;
174 }
175 
176 //---------------------------------------------------------------------------------------------------------------------
178 {
179  this->pieces = pieces;
180 }
181 
182 //---------------------------------------------------------------------------------------------------------------------
184 {
185  return bestResult;
186 }
187 
188 //---------------------------------------------------------------------------------------------------------------------
189 void VPosition::DrawDebug(const VContour &contour, const VLayoutPiece &piece, int frame, quint32 paperIndex,
190  int piecesCount, const QVector<VLayoutPiece> &pieces)
191 {
192  const int biasWidth = Bias(contour.GetWidth(), QIMAGE_MAX);
193  const int biasHeight = Bias(contour.GetHeight(), QIMAGE_MAX);
194 
195  QPicture picture;
196  QPainter paint;
197  paint.begin(&picture);
198 
199  paint.setPen(QPen(Qt::black, 6, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
200  QPainterPath path;
201  if (contour.GetContour().isEmpty())
202  {
203  path = DrawContour(contour.CutEmptySheetEdge());
204  path.translate(biasWidth/2, biasHeight/2);
205  paint.drawPath(path);
206  }
207  else
208  {
209  path = DrawContour(contour.GetContour());
210  path.translate(biasWidth/2, biasHeight/2);
211  paint.drawPath(path);
212  }
213 
214 #ifdef SHOW_CANDIDATE
215  paint.setPen(QPen(Qt::darkGreen, 6, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
217  path.translate(biasWidth/2, biasHeight/2);
218  paint.drawPath(path);
219 #else
220  Q_UNUSED(piece)
221  Q_UNUSED(pieces)
222 #endif
223 
224 #ifdef ARRANGED_PIECES
225  paint.setPen(QPen(Qt::blue, 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
226  path = drawPieces(pieces);
227  path.translate(biasWidth/2, biasHeight/2);
228  paint.drawPath(path);
229 #else
230  Q_UNUSED(pieces)
231 #endif
232 
233  // Calculate bounding rect before draw sheet rect
234  const QRect pictureRect = picture.boundingRect();
235 
236  // Sheet
237 #ifdef SHOW_SHEET
238  paint.setPen(QPen(Qt::darkRed, 15, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
239  paint.drawRect(QRectF(biasWidth/2, biasHeight/2, contour.GetWidth(), contour.GetHeight()));
240 #endif
241 
242  paint.end();
243 
244  // Dump frame to image
245  // Note. If program was build with Address Sanitizer possible crashes. Address Sanitizer doesn't support big
246  // allocations.
247  QImage frameImage(pictureRect.width()+biasWidth, pictureRect.height()+biasHeight, QImage::Format_RGB32);
248 
249  if (frameImage.isNull())
250  {
251  return;
252  }
253 
254  frameImage.fill(Qt::white);
255 
256  QPainter paintFrameImage;
257  paintFrameImage.begin(&frameImage);
258  paintFrameImage.drawPicture(0, 0, picture);
259  paintFrameImage.end();
260 
261  const QString fileName = QDir::homePath()+QStringLiteral("/LayoutDebug/")+QString("%1_%2_%3.png").arg(paperIndex)
262  .arg(piecesCount).arg(frame);
263  frameImage.save (fileName);
264 }
265 
266 //---------------------------------------------------------------------------------------------------------------------
267 int VPosition::Bias(int length, int maxLength)
268 {
269  if (length < maxLength && length*2 < maxLength)
270  {
271  return length;
272  }
273  else
274  {
275  return maxLength-length;
276  }
277 }
278 
279 //---------------------------------------------------------------------------------------------------------------------
280 void VPosition::SaveCandidate(VBestSquare &bestResult, const VLayoutPiece &piece, int globalI, int detJ,
281  BestFrom type)
282 {
283  QVector<QPointF> newGContour = gContour.UniteWithContour(piece, globalI, detJ, type);
284  newGContour.append(newGContour.first());
285  const QSizeF size = QPolygonF(newGContour).boundingRect().size();
286  bestResult.NewResult(size, globalI, detJ, piece.getTransform(), piece.isMirror(), type);
287 }
288 
289 //---------------------------------------------------------------------------------------------------------------------
290 bool VPosition::CheckCombineEdges(VLayoutPiece &piece, int j, int &dEdge)
291 {
292  const QLineF globalEdge = gContour.GlobalEdge(j);
293  bool flagMirror = false;
294  bool flagSquare = false;
295 
296  CombineEdges(piece, globalEdge, dEdge);
297 
298 #ifdef LAYOUT_DEBUG
299 # ifdef SHOW_COMBINE
301 # endif
302 #endif
303 
306  {
307  if (not gContour.GetContour().isEmpty())
308  {
309  type = Crossing(piece);
310  }
311  else
312  {
314  }
315  }
316 
317  switch (type)
318  {
320  return false;
322  piece.Mirror(globalEdge);
323  flagMirror = true;
324  break;
326  flagSquare = true;
327  break;
328  default:
329  break;
330  }
331 
332  if (flagMirror && not piece.IsForbidFlipping())
333  {
334  #ifdef LAYOUT_DEBUG
335  #ifdef SHOW_MIRROR
337  #endif
338  #endif
339 
340  if (gContour.GetContour().isEmpty())
341  {
342  dEdge = piece.pieceEdgeByPoint(globalEdge.p2());
343  }
344  else
345  {
346  dEdge = piece.LayoutEdgeByPoint(globalEdge.p2());
347  }
348 
349  if (dEdge <= 0)
350  {
351  return false;
352  }
353 
356  {
357  type = Crossing(piece);
358  }
359 
360  switch (type)
361  {
363  return false;
365  flagSquare = false;
366  break;
368  flagSquare = true;
369  break;
370  default:
371  break;
372  }
373  }
374  return flagSquare;
375 }
376 
377 //---------------------------------------------------------------------------------------------------------------------
378 bool VPosition::CheckRotationEdges(VLayoutPiece &piece, int j, int dEdge, int angle) const
379 {
380  const QLineF globalEdge = gContour.GlobalEdge(j);
381  bool flagSquare = false;
382 
383  RotateEdges(piece, globalEdge, dEdge, angle);
384 
385 #ifdef LAYOUT_DEBUG
386  #ifdef SHOW_ROTATION
388  #endif
389 #endif
390 
393  {
394  type = Crossing(piece);
395  }
396 
397  switch (type)
398  {
400  return false;
402  flagSquare = false;
403  break;
405  flagSquare = true;
406  break;
407  default:
408  break;
409  }
410  return flagSquare;
411 }
412 
413 //---------------------------------------------------------------------------------------------------------------------
415 {
416  const QRectF gRect = gContour.BoundingRect();
417  if (not gRect.intersects(piece.LayoutBoundingRect()) && not gRect.contains(piece.pieceBoundingRect()))
418  {
419  // This we can determine efficiently.
421  }
422 
423  const QPainterPath gPath = gContour.ContourPath();
424  if (not gPath.intersects(piece.LayoutAllowancePath()) && not gPath.contains(piece.createMainPath()))
425  {
427  }
428  else
429  {
431  }
432 }
433 
434 //---------------------------------------------------------------------------------------------------------------------
435 bool VPosition::SheetContains(const QRectF &rect) const
436 {
437  const QRectF bRect(0, 0, gContour.GetWidth(), gContour.GetHeight());
438  return bRect.contains(rect);
439 }
440 
441 //---------------------------------------------------------------------------------------------------------------------
442 void VPosition::CombineEdges(VLayoutPiece &piece, const QLineF &globalEdge, const int &dEdge)
443 {
444  QLineF pieceEdge;
445  if (gContour.GetContour().isEmpty())
446  {
447  pieceEdge = piece.pieceEdge(dEdge);
448  }
449  else
450  {
451  pieceEdge = piece.LayoutEdge(dEdge);
452  }
453 
454  // Find distance between two edges for two begin vertex.
455  const qreal dx = globalEdge.x2() - pieceEdge.x2();
456  const qreal dy = globalEdge.y2() - pieceEdge.y2();
457 
458  pieceEdge.translate(dx, dy); // Use values for translate piece edge.
459 
460  angle_between = globalEdge.angleTo(pieceEdge); // Seek angle between two edges.
461 
462  // Now we move piece to position near to global contour edge.
463  piece.Translate(dx, dy);
464  if (not qFuzzyIsNull(angle_between) || not qFuzzyCompare(angle_between, 360))
465  {
466  piece.Rotate(pieceEdge.p2(), -angle_between);
467  }
468 }
469 
470 //---------------------------------------------------------------------------------------------------------------------
471 void VPosition::RotateEdges(VLayoutPiece &piece, const QLineF &globalEdge, int dEdge, int angle) const
472 {
473  QLineF pieceEdge;
474  if (gContour.GetContour().isEmpty())
475  {
476  pieceEdge = piece.pieceEdge(dEdge);
477  }
478  else
479  {
480  pieceEdge = piece.LayoutEdge(dEdge);
481  }
482 
483  // Find distance between two edges for two begin vertex.
484  const qreal dx = globalEdge.x2() - pieceEdge.x2();
485  const qreal dy = globalEdge.y2() - pieceEdge.y2();
486 
487  pieceEdge.translate(dx, dy); // Use values for translate piece edge.
488 
489  // Now we move piece to position near to global contour edge.
490  piece.Translate(dx, dy);
491  piece.Rotate(globalEdge.p2(), angle);
492 }
493 
494 //---------------------------------------------------------------------------------------------------------------------
495 void VPosition::Rotate(int increase)
496 {
497  int startAngle = 0;
499  {
500  startAngle = increase;
501  }
502  for (int angle = startAngle; angle < 360; angle = angle+increase)
503  {
504  if (stop->load())
505  {
506  return;
507  }
508 
509  // We should use copy of the piece.
510  VLayoutPiece workpiece = piece;
511 
512  if (CheckRotationEdges(workpiece, j, i, angle))
513  {
514  #ifdef LAYOUT_DEBUG
515  # ifdef SHOW_CANDIDATE_BEST
516  ++frame;
518  # endif
519  #endif
520 
522  }
523  ++frame;
524  }
525 }
526 
527 //---------------------------------------------------------------------------------------------------------------------
528 QPainterPath VPosition::ShowDirection(const QLineF &edge)
529 {
530  const int arrowLength = 14;
531  QPainterPath path;
532  if (edge.length()/arrowLength < 5)
533  {
534  return path;
535  }
536 
537  QLineF arrow = edge;
538  arrow.setLength(edge.length()/2.0);
539 
540  //Reverse line because we want start arrow from this point
541  arrow = QLineF(arrow.p2(), arrow.p1());
542  const qreal angle = arrow.angle();//we each time change line angle, better save original angle value
543  arrow.setLength(arrowLength);//arrow length in pixels
544 
545  arrow.setAngle(angle-35);
546  path.moveTo(arrow.p1());
547  path.lineTo(arrow.p2());
548 
549  arrow.setAngle(angle+35);
550  path.moveTo(arrow.p1());
551  path.lineTo(arrow.p2());
552  return path;
553 }
554 
555 //---------------------------------------------------------------------------------------------------------------------
556 QPainterPath VPosition::DrawContour(const QVector<QPointF> &points)
557 {
558  QPainterPath path;
559  path.setFillRule(Qt::WindingFill);
560  if (points.count() >= 2)
561  {
562  for (qint32 i = 0; i < points.count()-1; ++i)
563  {
564  path.moveTo(points.at(i));
565  path.lineTo(points.at(i+1));
566  }
567  path.lineTo(points.at(0));
568 
569 #ifdef SHOW_DIRECTION
570  for (qint32 i = 0; i < points.count()-1; ++i)
571  {
572  path.addPath(ShowDirection(QLineF(points.at(i), points.at(i+1))));
573  }
574 #endif
575 
576 #ifdef SHOW_VERTICES
577  for (qint32 i = 0; i < points.count(); ++i)
578  {
579  path.addRect(points.at(i).x()-3, points.at(i).y()-3, 6, 6);
580  }
581 #endif
582  }
583  return path;
584 }
585 
586 //---------------------------------------------------------------------------------------------------------------------
587 QPainterPath VPosition::drawPieces(const QVector<VLayoutPiece> &pieces)
588 {
589  QPainterPath path;
590  path.setFillRule(Qt::WindingFill);
591  if (pieces.count() > 0)
592  {
593  for (int i = 0; i < pieces.size(); ++i)
594  {
595  path.addPath(pieces.at(i).createMainPath());
596  }
597  }
598  return path;
599 }
bool IsForbidFlipping() const
void NewResult(const QSizeF &candidate, int i, int j, const QTransform &transform, bool mirror, BestFrom type)
Definition: vbestsquare.cpp:69
int GetHeight() const
Definition: vcontour.cpp:128
QPainterPath ContourPath() const
Definition: vcontour.cpp:349
QRectF BoundingRect() const
Definition: vcontour.cpp:337
int GetWidth() const
Definition: vcontour.cpp:140
QVector< QPointF > GetContour() const
Definition: vcontour.cpp:110
QLineF GlobalEdge(int i) const
Definition: vcontour.cpp:252
QVector< QPointF > CutEmptySheetEdge() const
Definition: vcontour.cpp:317
QVector< QPointF > UniteWithContour(const VLayoutPiece &detail, int globalI, int detJ, BestFrom type) const
Definition: vcontour.cpp:158
void Mirror(const QLineF &edge)
QTransform getTransform() const
QPainterPath createMainPath() const
QRectF LayoutBoundingRect() const
void Translate(qreal dx, qreal dy)
int pieceEdgeByPoint(const QPointF &p1) const
QRectF pieceBoundingRect() const
QLineF pieceEdge(int i) const
QVector< QPointF > getLayoutAllowancePoints() const
QLineF LayoutEdge(int i) const
int LayoutEdgeByPoint(const QPointF &p1) const
bool isMirror() const
QPainterPath LayoutAllowancePath() const
void Rotate(const QPointF &originPoint, qreal degrees)
QVector< VLayoutPiece > pieces
Definition: vposition.h:102
VBestSquare bestResult
Definition: vposition.h:94
qreal angle_between
angle_between keep angle between global edge and piece edge. Need for optimization rotation.
Definition: vposition.h:109
CrossingType Crossing(const VLayoutPiece &piece) const
Definition: vposition.cpp:414
static int Bias(int length, int maxLength)
Definition: vposition.cpp:267
quint32 frame
Definition: vposition.h:100
quint32 paperIndex
Definition: vposition.h:99
VPosition(const VContour &gContour, int j, const VLayoutPiece &piece, int i, std::atomic_bool *stop, bool rotate, int rotationIncrease, bool saveLength)
Definition: vposition.cpp:77
static QPainterPath ShowDirection(const QLineF &edge)
Definition: vposition.cpp:528
void Rotate(int increase)
Definition: vposition.cpp:495
bool CheckRotationEdges(VLayoutPiece &piece, int j, int dEdge, int angle) const
Definition: vposition.cpp:378
VBestSquare getBestResult() const
Definition: vposition.cpp:183
quint32 getPaperIndex() const
Definition: vposition.cpp:139
void CombineEdges(VLayoutPiece &piece, const QLineF &globalEdge, const int &dEdge)
Definition: vposition.cpp:442
static void DrawDebug(const VContour &contour, const VLayoutPiece &piece, int frame, quint32 paperIndex, int piecesCount, const QVector< VLayoutPiece > &pieces=QVector< VLayoutPiece >())
Definition: vposition.cpp:189
bool rotate
Definition: vposition.h:104
const VLayoutPiece piece
Definition: vposition.h:96
virtual void run() Q_DECL_OVERRIDE
Definition: vposition.cpp:101
static QPainterPath drawPieces(const QVector< VLayoutPiece > &pieces)
Definition: vposition.cpp:587
void setPieces(const QVector< VLayoutPiece > &pieces)
Definition: vposition.cpp:177
static QPainterPath DrawContour(const QVector< QPointF > &points)
Definition: vposition.cpp:556
void RotateEdges(VLayoutPiece &piece, const QLineF &globalEdge, int dEdge, int angle) const
Definition: vposition.cpp:471
std::atomic_bool * stop
Definition: vposition.h:103
void setFrame(const quint32 &value)
Definition: vposition.cpp:158
const VContour gContour
Definition: vposition.h:95
bool CheckCombineEdges(VLayoutPiece &piece, int j, int &dEdge)
Definition: vposition.cpp:290
bool SheetContains(const QRectF &rect) const
Definition: vposition.cpp:435
void setPaperIndex(const quint32 &value)
Definition: vposition.cpp:145
quint32 getFrame() const
Definition: vposition.cpp:152
quint32 piecesCount
Definition: vposition.h:101
quint32 getPieceCount() const
Definition: vposition.cpp:165
void setPieceCount(const quint32 &value)
Definition: vposition.cpp:171
int rotationIncrease
Definition: vposition.h:105
void SaveCandidate(VBestSquare &bestResult, const VLayoutPiece &piece, int globalI, int detJ, BestFrom type)
Definition: vposition.cpp:280
static Q_REQUIRED_RESULT bool VFuzzyComparePossibleNulls(double p1, double p2)
Definition: def.h:490
#define QIMAGE_MAX
Definition: def.h:298
BestFrom
Definition: vlayoutdef.h:68