1 /************************************************************************
2  **
3  **  @file   vlayoutpaper.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   7 1, 2015
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) 2013-2015 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 "vlayoutpaper.h"
30 
31 #include <QBrush>
32 #include <QCoreApplication>
33 #include <QGraphicsRectItem>
34 #include <QGraphicsScene>
35 #include <QList>
36 #include <QPen>
37 #include <QPointF>
38 #include <QRect>
39 #include <QRectF>
40 #include <QThread>
41 #include <QThreadPool>
42 #include <QVector>
43 #include <Qt>
44 #include <QtAlgorithms>
45 
46 #ifdef LAYOUT_DEBUG
47 #include <QMutex>
48 #endif
49 
50 #include "vbestsquare.h"
51 #include "vcontour.h"
52 #include "vlayoutpiece.h"
53 #include "vlayoutpaper_p.h"
54 #include "vposition.h"
55 #include "../ifc/exception/vexceptionterminatedposition.h"
56 #include "../vmisc/compatibility.h"
57 
58 //---------------------------------------------------------------------------------------------------------------------
VLayoutPaper()59 VLayoutPaper::VLayoutPaper()
60     :d(new VLayoutPaperData)
61 {}
62 
63 //---------------------------------------------------------------------------------------------------------------------
VLayoutPaper(int height,int width,qreal layoutWidth)64 VLayoutPaper::VLayoutPaper(int height, int width, qreal layoutWidth)
65     :d(new VLayoutPaperData(height, width, layoutWidth))
66 {}
67 
68 //---------------------------------------------------------------------------------------------------------------------
VLayoutPaper(const VLayoutPaper & paper)69 VLayoutPaper::VLayoutPaper(const VLayoutPaper &paper)
70     :d (paper.d)
71 {}
72 
73 //---------------------------------------------------------------------------------------------------------------------
operator =(const VLayoutPaper & paper)74 VLayoutPaper &VLayoutPaper::operator=(const VLayoutPaper &paper)
75 {
76     if ( &paper == this )
77     {
78         return *this;
79     }
80     d = paper.d;
81     return *this;
82 }
83 
84 #ifdef Q_COMPILER_RVALUE_REFS
85 //---------------------------------------------------------------------------------------------------------------------
VLayoutPaper(const VLayoutPaper && paper)86 VLayoutPaper::VLayoutPaper(const VLayoutPaper &&paper) Q_DECL_NOTHROW
87     :d (paper.d)
88 {}
89 
90 //---------------------------------------------------------------------------------------------------------------------
operator =(VLayoutPaper && paper)91 VLayoutPaper &VLayoutPaper::operator=(VLayoutPaper &&paper) Q_DECL_NOTHROW
92 {
93     std::swap(d, paper.d);
94     return *this;
95 }
96 #endif
97 
98 //---------------------------------------------------------------------------------------------------------------------
~VLayoutPaper()99 VLayoutPaper::~VLayoutPaper()
100 {}
101 
102 //---------------------------------------------------------------------------------------------------------------------
GetHeight() const103 int VLayoutPaper::GetHeight() const
104 {
105     return d->globalContour.GetHeight();
106 }
107 
108 //---------------------------------------------------------------------------------------------------------------------
SetHeight(int height)109 void VLayoutPaper::SetHeight(int height)
110 {
111     d->globalContour.SetHeight(height);
112 }
113 
114 //---------------------------------------------------------------------------------------------------------------------
GetWidth() const115 int VLayoutPaper::GetWidth() const
116 {
117     return d->globalContour.GetWidth();
118 }
119 
120 //---------------------------------------------------------------------------------------------------------------------
SetWidth(int width)121 void VLayoutPaper::SetWidth(int width)
122 {
123     d->globalContour.SetWidth(width);
124 }
125 
126 //---------------------------------------------------------------------------------------------------------------------
GetLayoutWidth() const127 qreal VLayoutPaper::GetLayoutWidth() const
128 {
129     return d->layoutWidth;
130 }
131 
132 //---------------------------------------------------------------------------------------------------------------------
SetLayoutWidth(qreal width)133 void VLayoutPaper::SetLayoutWidth(qreal width)
134 {
135     if (width >= 0)
136     {
137         d->layoutWidth = width;
138     }
139 }
140 
141 //---------------------------------------------------------------------------------------------------------------------
GetShift() const142 qreal VLayoutPaper::GetShift() const
143 {
144     return d->globalContour.GetShift();
145 }
146 
147 //---------------------------------------------------------------------------------------------------------------------
SetShift(qreal shift)148 void VLayoutPaper::SetShift(qreal shift)
149 {
150     d->globalContour.SetShift(shift);
151 }
152 
153 //---------------------------------------------------------------------------------------------------------------------
GetRotate() const154 bool VLayoutPaper::GetRotate() const
155 {
156     return d->globalRotate;
157 }
158 
159 //---------------------------------------------------------------------------------------------------------------------
SetRotate(bool value)160 void VLayoutPaper::SetRotate(bool value)
161 {
162     d->globalRotate = value;
163     d->localRotate = d->globalRotate;
164 }
165 
166 //---------------------------------------------------------------------------------------------------------------------
GetFollowGrainline() const167 bool VLayoutPaper::GetFollowGrainline() const
168 {
169     return d->followGrainline;
170 }
171 
172 //---------------------------------------------------------------------------------------------------------------------
SetFollowGrainline(bool value)173 void VLayoutPaper::SetFollowGrainline(bool value)
174 {
175     d->followGrainline = value;
176 }
177 
178 //---------------------------------------------------------------------------------------------------------------------
GetRotationNumber() const179 int VLayoutPaper::GetRotationNumber() const
180 {
181     return d->globalRotationNumber;
182 }
183 
184 //---------------------------------------------------------------------------------------------------------------------
SetRotationNumber(int value)185 void VLayoutPaper::SetRotationNumber(int value)
186 {
187     d->globalRotationNumber = value;
188 
189     if (d->globalRotationNumber > 360 || d->globalRotationNumber < 1)
190     {
191         d->globalRotationNumber = 2;
192     }
193 
194     d->localRotationNumber = d->globalRotationNumber;
195 }
196 
197 //---------------------------------------------------------------------------------------------------------------------
IsSaveLength() const198 bool VLayoutPaper::IsSaveLength() const
199 {
200     return d->saveLength;
201 }
202 
203 //---------------------------------------------------------------------------------------------------------------------
SetSaveLength(bool value)204 void VLayoutPaper::SetSaveLength(bool value)
205 {
206     d->saveLength = value;
207 }
208 
209 //---------------------------------------------------------------------------------------------------------------------
SetPaperIndex(quint32 index)210 void VLayoutPaper::SetPaperIndex(quint32 index)
211 {
212     d->paperIndex = index;
213 }
214 
215 //---------------------------------------------------------------------------------------------------------------------
IsOriginPaperPortrait() const216 bool VLayoutPaper::IsOriginPaperPortrait() const
217 {
218     return d->originPaperOrientation;
219 }
220 
221 //---------------------------------------------------------------------------------------------------------------------
SetOriginPaperPortrait(bool portrait)222 void VLayoutPaper::SetOriginPaperPortrait(bool portrait)
223 {
224     d->originPaperOrientation = portrait;
225 }
226 
227 //---------------------------------------------------------------------------------------------------------------------
ArrangeDetail(const VLayoutPiece & detail,std::atomic_bool & stop)228 bool VLayoutPaper::ArrangeDetail(const VLayoutPiece &detail, std::atomic_bool &stop)
229 {
230     if (detail.LayoutEdgesCount() < 3 || detail.DetailEdgesCount() < 3)
231     {
232         return false;//Not enough edges
233     }
234 
235     if ((detail.IsForceFlipping() || detail.IsForbidFlipping()) && not d->globalRotate)
236     { // Compensate forbidden flipping by rotating. 180 degree will be enough.
237         d->localRotate = true;
238         d->localRotationNumber = 2;
239     }
240     else
241     { // Return to global values if was changed
242         d->localRotate = d->globalRotate;
243         d->localRotationNumber = d->globalRotationNumber;
244     }
245 
246 #ifdef LAYOUT_DEBUG
247     QMutex mutex;
248 #endif
249 
250     VPositionData data;
251     data.gContour = d->globalContour;
252     data.detail = detail;
253     data.rotate = d->localRotate;
254     data.rotationNumber = d->localRotationNumber;
255     data.followGrainline = d->followGrainline;
256     data.positionsCache = d->positionsCache;
257     data.isOriginPaperOrientationPortrait = d->originPaperOrientation;
258 #ifdef LAYOUT_DEBUG
259     data.details = d->details;
260     data.mutex = &mutex;
261 #endif
262 
263     const VBestSquare result = VPosition::ArrangeDetail(data, &stop, d->saveLength);
264 #ifdef LAYOUT_DEBUG
265     return SaveResult(result, detail, &mutex);
266 #else
267     return SaveResult(result, detail);
268 #endif
269 }
270 
271 //---------------------------------------------------------------------------------------------------------------------
Count() const272 int VLayoutPaper::Count() const
273 {
274     return d->details.count();
275 }
276 
277 //---------------------------------------------------------------------------------------------------------------------
SaveResult(const VBestSquare & bestResult,const VLayoutPiece & detail,QMutex * mutex)278 bool VLayoutPaper::SaveResult(const VBestSquare &bestResult, const VLayoutPiece &detail
279 #ifdef LAYOUT_DEBUG
280                               , QMutex *mutex
281 #endif
282                               )
283 {
284     if (bestResult.HasValidResult())
285     {
286         VLayoutPiece workDetail = detail;
287         workDetail.SetMatrix(bestResult.Matrix());// Don't forget set matrix
288         workDetail.SetMirror(bestResult.Mirror());
289 
290         if (d->saveLength)
291         {
292             d->globalContour.CeateEmptySheetContour();
293         }
294 
295         const QVector<QPointF> newGContour = d->globalContour.UniteWithContour(workDetail, bestResult.GContourEdge(),
296                                                                                bestResult.DetailEdge(),
297                                                                                bestResult.Type());
298         if (newGContour.isEmpty())
299         {
300             return false;
301         }
302         d->details.append(workDetail);
303         d->globalContour.SetContour(newGContour);
304 
305         VCachedPositions positionChache;
306         QVector<QPointF> layoutPoints = workDetail.GetLayoutAllowancePoints();
307         positionChache.boundingRect = VLayoutPiece::BoundingRect(layoutPoints);
308         positionChache.layoutAllowancePath = VAbstractPiece::PainterPath(layoutPoints);
309         d->positionsCache.append(positionChache);
310 
311 #ifdef LAYOUT_DEBUG
312 #   ifdef SHOW_BEST
313         VPosition::DumpFrame(d->globalContour, workDetail, mutex, d->details);
314 #   endif
315 #endif
316     }
317     else if (bestResult.IsTerminatedByException())
318     {
319         throw VExceptionTerminatedPosition(bestResult.ReasonTerminatedByException());
320     }
321 
322     return bestResult.HasValidResult(); // Do we have the best result?
323 }
324 
325 //---------------------------------------------------------------------------------------------------------------------
GetPaperItem(bool autoCropLength,bool autoCropWidth,bool textAsPaths) const326 QGraphicsRectItem *VLayoutPaper::GetPaperItem(bool autoCropLength, bool autoCropWidth, bool textAsPaths) const
327 {
328     int height = d->globalContour.GetHeight();
329     int width = d->globalContour.GetWidth();
330 
331     if (autoCropLength || autoCropWidth)
332     {
333         QScopedPointer<QGraphicsScene> scene(new QGraphicsScene());
334         QList<QGraphicsItem *> list = GetItemDetails(textAsPaths);
335         for (auto item : list)
336         {
337             scene->addItem(item);
338         }
339 
340         const QRect boundingRect = scene->itemsBoundingRect().toRect();
341 
342         if (autoCropLength)
343         {
344             if (d->globalContour.IsPortrait())
345             {
346                 height = boundingRect.height() + boundingRect.y() + 1;
347             }
348             else
349             {
350                 width = boundingRect.width() + boundingRect.x() + 1;
351             }
352         }
353 
354         if (autoCropWidth)
355         {
356             if (d->globalContour.IsPortrait())
357             {
358                 width = boundingRect.width() + boundingRect.x() + 1;
359             }
360             else
361             {
362                 height = boundingRect.height() + boundingRect.y() + 1;
363             }
364         }
365     }
366 
367     auto *paper = new QGraphicsRectItem(QRectF(0, 0, width, height));
368     paper->setPen(QPen(Qt::black, 1));
369     paper->setBrush(QBrush(Qt::white));
370     return paper;
371 }
372 
373 //---------------------------------------------------------------------------------------------------------------------
GetGlobalContour() const374 QGraphicsPathItem *VLayoutPaper::GetGlobalContour() const
375 {
376     // contour
377     const QVector<QPointF> points = d->globalContour.GetContour();
378 
379     QPainterPath path;
380     if (points.size() > 0)
381     {
382         path.moveTo(points.at(0));
383         for (auto point : points)
384         {
385             path.lineTo(point);
386         }
387     }
388 
389     const qreal radius = 1;
390     for (auto point : points)
391     {
392         path.addEllipse(point.x()-radius, point.y()-radius, radius*2, radius*2);
393     }
394 
395     for (int i=0; i < points.size()-1; ++i)
396     {
397         QLineF line(points.at(i), points.at(i+1));
398         line.setLength(line.length()/2);
399 
400         path.moveTo(line.p2());
401         QLineF side1(line.p2(), line.p1());
402         side1.setAngle(side1.angle()+35);
403         side1.setLength(3);
404         path.lineTo(side1.p2());
405 
406         path.moveTo(line.p2());
407         QLineF side2(line.p2(), line.p1());
408         side2.setAngle(side2.angle()-35);
409         side2.setLength(3);
410         path.lineTo(side2.p2());
411     }
412 
413     QGraphicsPathItem *item = new QGraphicsPathItem(path);
414     QPen pen = item->pen();
415     pen.setWidthF(0.25);
416     item->setPen(pen);
417 
418     return item;
419 }
420 
421 //---------------------------------------------------------------------------------------------------------------------
GetItemDetails(bool textAsPaths) const422 QList<QGraphicsItem *> VLayoutPaper::GetItemDetails(bool textAsPaths) const
423 {
424     QList<QGraphicsItem *> list;
425     for (auto &detail : d->details)
426     {
427         list.append(detail.GetItem(textAsPaths));
428     }
429     return list;
430 }
431 
432 //---------------------------------------------------------------------------------------------------------------------
GetDetails() const433 QVector<VLayoutPiece> VLayoutPaper::GetDetails() const
434 {
435     return d->details;
436 }
437 
438 //---------------------------------------------------------------------------------------------------------------------
SetDetails(const QVector<VLayoutPiece> & details)439 void VLayoutPaper::SetDetails(const QVector<VLayoutPiece> &details)
440 {
441     d->details = details;
442 }
443 
444 //---------------------------------------------------------------------------------------------------------------------
SetDetails(const QList<VLayoutPiece> & details)445 void VLayoutPaper::SetDetails(const QList<VLayoutPiece> &details)
446 {
447     d->details = ConvertToVector(details);
448 }
449 
450 //---------------------------------------------------------------------------------------------------------------------
DetailsBoundingRect() const451 QRectF VLayoutPaper::DetailsBoundingRect() const
452 {
453     QRectF rec;
454     for (auto &detail : d->details)
455     {
456         rec = rec.united(detail.DetailBoundingRect());
457     }
458 
459     return rec;
460 }
461 
462 //---------------------------------------------------------------------------------------------------------------------
Efficiency() const463 qreal VLayoutPaper::Efficiency() const
464 {
465     qreal efficiency = 0;
466     for(auto &detail : d->details)
467     {
468         efficiency += static_cast<qreal>(detail.Square());
469     }
470 
471     const QRectF boundingRect = DetailsBoundingRect();
472 
473     return efficiency / (boundingRect.width() * boundingRect.height()) * 100.0;
474 }
475