1 /************************************************************************
2  **
3  **  @file   vbank.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   11 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 "vbank.h"
30 
31 #include <climits>
32 #include <QLoggingCategory>
33 
34 #include "../vmisc/diagnostic.h"
35 #include "../vmisc/vabstractvalapplication.h"
36 #include "../vmisc/compatibility.h"
37 #include "vlayoutdef.h"
38 #include "../ifc/exception/vexception.h"
39 #include "../vpatterndb/floatItemData/floatitemdef.h"
40 
41 QT_WARNING_PUSH
42 QT_WARNING_DISABLE_CLANG("-Wmissing-prototypes")
43 QT_WARNING_DISABLE_INTEL(1418)
44 
45 Q_LOGGING_CATEGORY(lBank, "layout.bank")
46 
47 QT_WARNING_POP
48 
49 // An annoying char define, from the Windows team in <rpcndr.h>
50 // #define small char
51 // http://stuartjames.info/Journal/c--visual-studio-2012-vs2012--win8--converting-projects-up-some-conflicts-i-found.aspx
52 #if defined (Q_OS_WIN) && defined (Q_CC_MSVC)
53 #pragma push_macro("small")
54 #undef small
55 #endif
56 
57 namespace
58 {
59 //---------------------------------------------------------------------------------------------------------------------
PrepareQuantity(const QVector<VLayoutPiece> & details)60 QVector<VLayoutPiece> PrepareQuantity(const QVector<VLayoutPiece> &details)
61 {
62     QVector<VLayoutPiece> withQuantity;
63     withQuantity.reserve(details.size());
64     for(auto &piece : details)
65     {
66         for (int i = 0; i < piece.GetQuantity(); ++i)
67         {
68             withQuantity.append(piece);
69         }
70     }
71 
72     return withQuantity;
73 }
74 
75 //---------------------------------------------------------------------------------------------------------------------
Insert(QMap<uint,QHash<int,qint64>> & container,uint key,int valKey,qint64 valValue)76 void Insert(QMap<uint, QHash<int, qint64>> &container, uint key, int valKey, qint64 valValue)
77 {
78     QHash<int, qint64> containerSequence = container.value(key);
79     containerSequence.insert(valKey, valValue);
80     container.insert(key, containerSequence);
81 }
82 
83 //---------------------------------------------------------------------------------------------------------------------
84 template <typename T>
CountDetails(const T & container)85 int CountDetails(const T &container)
86 {
87     int count = 0;
88     auto i = container.constBegin();
89     while (i != container.constEnd())
90     {
91         count += i.value().count();
92         ++i;
93     }
94 
95     return count;
96 }
97 
98 //---------------------------------------------------------------------------------------------------------------------
NotArrangedDetail(QMap<uint,QHash<int,qint64>> & container,QMap<uint,QHash<int,qint64>> & unsorted,int i)99 bool NotArrangedDetail(QMap<uint, QHash<int, qint64>> &container, QMap<uint, QHash<int, qint64>> &unsorted,
100                        int i)
101 {
102     QMutableMapIterator<uint, QHash<int, qint64>> iterator(container);
103     while (iterator.hasNext())
104     {
105         iterator.next();
106         auto containerGroup = container.value(iterator.key());
107         if (containerGroup.contains(i))
108         {
109             Insert(unsorted, iterator.key(), i, containerGroup.value(i));
110             containerGroup.remove(i);
111 
112             if (not containerGroup.isEmpty())
113             {
114                 container.insert(iterator.key(), containerGroup);
115             }
116             else
117             {
118                 container.remove(iterator.key());
119             }
120             return true;
121         }
122     }
123     return false;
124 }
125 
126 //---------------------------------------------------------------------------------------------------------------------
NotArrangedDetail(QMap<uint,QMultiMap<qint64,int>> & container,QMap<uint,QHash<int,qint64>> & unsorted,int i)127 bool NotArrangedDetail(QMap<uint, QMultiMap<qint64, int>> &container, QMap<uint, QHash<int, qint64>> &unsorted,
128                        int i)
129 {
130     QMutableMapIterator<uint, QMultiMap<qint64, int>> iterator(container);
131     while (iterator.hasNext())
132     {
133         iterator.next();
134         auto containerGroup = container.value(iterator.key());
135         auto detailIterator = std::find(containerGroup.begin(), containerGroup.end(), i);
136         if (detailIterator != containerGroup.end())
137         {
138             Insert(unsorted, iterator.key(), i, detailIterator.key());
139             containerGroup.erase(detailIterator);
140 
141             if (not containerGroup.isEmpty())
142             {
143                 container.insert(iterator.key(), containerGroup);
144             }
145             else
146             {
147                 container.remove(iterator.key());
148             }
149             return true;
150         }
151     }
152     return false;
153 }
154 
155 //---------------------------------------------------------------------------------------------------------------------
TakeFirstForPriority(const QMap<uint,QHash<int,qint64>> & container,uint priority)156 int TakeFirstForPriority(const QMap<uint, QHash<int, qint64>> &container, uint priority)
157 {
158     const QHash<int, qint64> priorityGroup = container.value(priority);
159     if (not priorityGroup.isEmpty())
160     {
161         return priorityGroup.constBegin().key();
162     }
163 
164     return -1;
165 }
166 }
167 
168 //---------------------------------------------------------------------------------------------------------------------
VBank()169 VBank::VBank()
170 {}
171 
172 //---------------------------------------------------------------------------------------------------------------------
GetLayoutWidth() const173 qreal VBank::GetLayoutWidth() const
174 {
175     return layoutWidth;
176 }
177 
178 //---------------------------------------------------------------------------------------------------------------------
SetLayoutWidth(qreal value)179 void VBank::SetLayoutWidth(qreal value)
180 {
181     layoutWidth = value;
182     Reset();
183 }
184 
185 //---------------------------------------------------------------------------------------------------------------------
GetManualPriority() const186 bool VBank::GetManualPriority() const
187 {
188     return m_manualPriority;
189 }
190 
191 //---------------------------------------------------------------------------------------------------------------------
SetManualPriority(bool value)192 void VBank::SetManualPriority(bool value)
193 {
194     m_manualPriority = value;
195 }
196 
197 //---------------------------------------------------------------------------------------------------------------------
IsNestQuantity() const198 bool VBank::IsNestQuantity() const
199 {
200     return m_nestQuantity;
201 }
202 
203 //---------------------------------------------------------------------------------------------------------------------
SetNestQuantity(bool value)204 void VBank::SetNestQuantity(bool value)
205 {
206     m_nestQuantity = value;
207 }
208 
209 //---------------------------------------------------------------------------------------------------------------------
SetDetails(const QVector<VLayoutPiece> & details)210 void VBank::SetDetails(const QVector<VLayoutPiece> &details)
211 {
212     this->details = details;
213     Reset();
214 }
215 
216 //---------------------------------------------------------------------------------------------------------------------
GetNext()217 int VBank::GetNext()
218 {
219     if (not prepare)
220     {
221         return -1;
222     }
223 
224     if (LeftToArrange() == 0)
225     {
226         if (CountDetails(unsorted) == 0)
227         {
228             return -1;
229         }
230         else
231         {
232             PrepareGroup();
233         }
234     }
235 
236     auto GetNextInGroup = [this](uint group)
237     {
238         switch (caseType)
239         {
240             case Cases::CaseThreeGroup:
241                 return GetNextThreeGroups(group);
242             case Cases::CaseTwoGroup:
243                 return GetNextTwoGroups(group);
244             case Cases::CaseDesc:
245                 return GetNextDescGroup(group);
246             default:
247                 return -1;
248         }
249     };
250 
251     for (auto &group: groups)
252     {
253         int next = -1;
254         if (group != 0) // Group 0 must go last
255         {
256             next = GetNextInGroup(group);
257         }
258 
259         if (next != -1)
260         {
261             return next;
262         }
263     }
264 
265     if (groups.contains(0U))
266     {
267         return GetNextInGroup(0);
268     }
269 
270     return -1;
271 }
272 
273 //---------------------------------------------------------------------------------------------------------------------
GetDetail(int i) const274 VLayoutPiece VBank::GetDetail(int i) const
275 {
276     if (i >= 0 && i < details.size())
277     {
278         return details.at(i);
279     }
280     else
281     {
282         return VLayoutPiece();
283     }
284 }
285 
286 //---------------------------------------------------------------------------------------------------------------------
Arranged(int i)287 void VBank::Arranged(int i)
288 {
289     if (ArrangedDetail(big, i))
290     {
291         return;
292     }
293 
294     if (ArrangedDetail(middle, i))
295     {
296         return;
297     }
298 
299     if (ArrangedDetail(small, i))
300     {
301         return;
302     }
303 
304     ArrangedDetail(desc, i);
305 }
306 
307 //---------------------------------------------------------------------------------------------------------------------
NotArranged(int i)308 void VBank::NotArranged(int i)
309 {
310     if (NotArrangedDetail(big, unsorted, i))
311     {
312         return;
313     }
314 
315     if (NotArrangedDetail(middle, unsorted, i))
316     {
317         return;
318     }
319 
320     if (NotArrangedDetail(small, unsorted, i))
321     {
322         return;
323     }
324 
325     NotArrangedDetail(desc, unsorted, i);
326 }
327 
328 //---------------------------------------------------------------------------------------------------------------------
PrepareUnsorted()329 bool VBank::PrepareUnsorted()
330 {
331     QSet<uint> uniqueGroup;
332 
333     for (int i=0; i < details.size(); ++i)
334     {
335         const qint64 square = details.at(i).Square();
336         if (square <= 0)
337         {
338             qCCritical(lBank) << VAbstractValApplication::warningMessageSignature +
339                                      tr("Preparing data for layout error: Detail '%1' square <= 0")
340                                          .arg(details.at(i).GetName());
341             prepare = false;
342             return prepare;
343         }
344         const uint group = m_manualPriority ? details.at(i).GetPriority() : 0;
345         uniqueGroup.insert(group);
346         Insert(unsorted, group, i, square);
347     }
348 
349     groups = ConvertToVector(uniqueGroup);
350     std::sort(groups.begin(), groups.end());
351 
352     PrepareGroup();
353 
354     prepare = true;
355     return prepare;
356 }
357 
358 //---------------------------------------------------------------------------------------------------------------------
PrepareDetails()359 bool VBank::PrepareDetails()
360 {
361     if (layoutWidth <= 0)
362     {
363         qCCritical(lBank) << VAbstractValApplication::warningMessageSignature +
364                                  tr("Preparing data for layout error: Layout paper sheet <= 0");
365         prepare = false;
366         return prepare;
367     }
368 
369     if (details.isEmpty())
370     {
371         qCCritical(lBank) << VAbstractValApplication::warningMessageSignature +
372                                  tr("Preparing data for layout error: List of details is empty");
373         prepare = false;
374         return prepare;
375     }
376 
377     if (m_nestQuantity)
378     {
379         details = PrepareQuantity(details);
380     }
381 
382     diagonal = 0;
383 
384     for (int i=0; i < details.size(); ++i)
385     {
386         details[i].SetLayoutWidth(layoutWidth);
387         details[i].SetLayoutAllowancePoints();
388 
389         if (not details.at(i).IsLayoutAllowanceValid())
390         {
391             const QString errorMsg = QObject::tr("Piece '%1' has invalid layout allowance. Please, check seam allowance"
392                                                  " to check how seam allowance behave.").arg(details.at(i).GetName());
393             VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) :
394                                               qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
395         }
396 
397         const qreal d = details.at(i).Diagonal();
398         if (d > diagonal)
399         {
400             diagonal = d;
401         }
402     }
403 
404     prepare = true;
405     return prepare;
406 }
407 
408 //---------------------------------------------------------------------------------------------------------------------
Reset()409 void VBank::Reset()
410 {
411     prepare = false;
412     unsorted.clear();
413     big.clear();
414     middle.clear();
415     small.clear();
416     desc.clear();
417     groups.clear();
418     arranged.clear();
419     diagonal = 0;
420 }
421 
422 //---------------------------------------------------------------------------------------------------------------------
SetCaseType(Cases caseType)423 void VBank::SetCaseType(Cases caseType)
424 {
425     this->caseType = caseType;
426 }
427 
428 //---------------------------------------------------------------------------------------------------------------------
AllDetailsCount() const429 int VBank::AllDetailsCount() const
430 {
431     return CountDetails(unsorted) + CountDetails(big) + CountDetails(middle) + CountDetails(small) + CountDetails(desc);
432 }
433 
434 //---------------------------------------------------------------------------------------------------------------------
LeftToArrange() const435 int VBank::LeftToArrange() const
436 {
437     return CountDetails(big) + CountDetails(middle) + CountDetails(small) + CountDetails(desc);
438 }
439 
440 //---------------------------------------------------------------------------------------------------------------------
FailedToArrange() const441 int VBank::FailedToArrange() const
442 {
443     return CountDetails(unsorted);
444 }
445 
446 //---------------------------------------------------------------------------------------------------------------------
GetBiggestDiagonal() const447 qreal VBank::GetBiggestDiagonal() const
448 {
449     return diagonal;
450 }
451 
452 //---------------------------------------------------------------------------------------------------------------------
PrepareGroup()453 void VBank::PrepareGroup()
454 {
455     QMutableMapIterator<uint, QHash<int, qint64>> i(unsorted);
456     while (i.hasNext())
457     {
458         i.next();
459         switch (caseType)
460         {
461             case Cases::CaseThreeGroup:
462                 PrepareThreeGroups(i.key());
463                 break;
464             case Cases::CaseTwoGroup:
465                 PrepareTwoGroups(i.key());
466                 break;
467             case Cases::CaseDesc:
468                 PrepareDescGroup(i.key());
469                 break;
470             default:
471                 break;
472         }
473     }
474 }
475 
476 //---------------------------------------------------------------------------------------------------------------------
PrepareThreeGroups(uint priority)477 void VBank::PrepareThreeGroups(uint priority)
478 {
479     qint64 sMax = LLONG_MIN;
480     qint64 sMin = LLONG_MAX;
481 
482     SqMaxMin(sMax, sMin, priority);
483 
484     const qint64 s1 = sMax - (sMax - sMin)/3;
485     const qint64 s2 = sMin + (sMax - sMin)/3;
486 
487     const QHash<int, qint64> usortedGroup = unsorted.value(priority);
488     QHash<int, qint64>::const_iterator i = usortedGroup.constBegin();
489     while (i != usortedGroup.constEnd())
490     {
491         if (i.value() > s1)
492         {
493             Insert(big, priority, i.key(), i.value());
494         }
495         else if (s1 >= i.value() && i.value() > s2)
496         {
497             Insert(middle, priority, i.key(), i.value());
498         }
499         else
500         {
501             Insert(small, priority, i.key(), i.value());
502         }
503         ++i;
504     }
505     unsorted.remove(priority);
506 }
507 
508 //---------------------------------------------------------------------------------------------------------------------
PrepareTwoGroups(uint priority)509 void VBank::PrepareTwoGroups(uint priority)
510 {
511     qint64 sMax = LLONG_MIN;
512     qint64 sMin = LLONG_MAX;
513 
514     SqMaxMin(sMax, sMin, priority);
515 
516     const qint64 s = (sMax + sMin)/2;
517     const QHash<int, qint64> usortedGroup = unsorted.value(priority);
518     QHash<int, qint64>::const_iterator i = usortedGroup.constBegin();
519     while (i != usortedGroup.constEnd())
520     {
521         if (i.value() >= s)
522         {
523             Insert(big, priority, i.key(), i.value());
524         }
525         else
526         {
527             Insert(small, priority, i.key(), i.value());
528         }
529         ++i;
530     }
531     unsorted.remove(priority);
532 }
533 
534 //---------------------------------------------------------------------------------------------------------------------
PrepareDescGroup(uint priority)535 void VBank::PrepareDescGroup(uint priority)
536 {
537     QMultiMap<qint64, int> descGroup;
538     const QHash<int, qint64> usortedGroup = unsorted.value(priority);
539     QHash<int, qint64>::const_iterator i = usortedGroup.constBegin();
540     while (i != usortedGroup.constEnd())
541     {
542         descGroup.insert(i.value(), i.key());
543         ++i;
544     }
545     desc.insert(priority, descGroup);
546     unsorted.remove(priority);
547 }
548 
549 //---------------------------------------------------------------------------------------------------------------------
GetNextThreeGroups(uint priority) const550 int VBank::GetNextThreeGroups(uint priority) const
551 {
552     int next = TakeFirstForPriority(big, priority);
553     if (next != -1)
554     {
555         return next;
556     }
557 
558     next = TakeFirstForPriority(middle, priority);
559     if (next != -1)
560     {
561         return next;
562     }
563 
564     next = TakeFirstForPriority(small, priority);
565     return next;
566 }
567 
568 //---------------------------------------------------------------------------------------------------------------------
GetNextTwoGroups(uint priority) const569 int VBank::GetNextTwoGroups(uint priority) const
570 {
571     int next = TakeFirstForPriority(big, priority);
572     if (next != -1)
573     {
574         return next;
575     }
576 
577     next = TakeFirstForPriority(small, priority);
578     return next;
579 }
580 
581 //---------------------------------------------------------------------------------------------------------------------
GetNextDescGroup(uint priority) const582 int VBank::GetNextDescGroup(uint priority) const
583 {
584     auto descGroup = desc.value(priority);
585     QMapIterator<qint64, int> i(descGroup);
586     i.toBack();
587     if (i.hasPrevious())
588     {
589         return i.previous().value();
590     }
591 
592     return -1;
593 }
594 
595 //---------------------------------------------------------------------------------------------------------------------
SqMaxMin(qint64 & sMax,qint64 & sMin,uint priority) const596 void VBank::SqMaxMin(qint64 &sMax, qint64 &sMin, uint priority) const
597 {
598     sMax = LLONG_MIN;
599     sMin = LLONG_MAX;
600 
601     const QHash<int, qint64> usortedGroup = unsorted.value(priority);
602     QHash<int, qint64>::const_iterator i = usortedGroup.constBegin();
603     while (i != usortedGroup.constEnd())
604     {
605         if (i.value() < sMin)
606         {
607             sMin = i.value();
608         }
609 
610         if (i.value() > sMax)
611         {
612             sMax = i.value();
613         }
614         ++i;
615     }
616 }
617 
618 //---------------------------------------------------------------------------------------------------------------------
ArrangedDetail(QMap<uint,QHash<int,qint64>> & container,int i)619 bool VBank::ArrangedDetail(QMap<uint, QHash<int, qint64> > &container, int i)
620 {
621     QMutableMapIterator<uint, QHash<int, qint64>> iterator(container);
622     while (iterator.hasNext())
623     {
624         iterator.next();
625         auto containerGroup = container.value(iterator.key());
626         if (containerGroup.contains(i))
627         {
628             arranged.append(details.at(i).GetId());
629             containerGroup.remove(i);
630             if (not containerGroup.isEmpty())
631             {
632                 container.insert(iterator.key(), containerGroup);
633             }
634             else
635             {
636                 container.remove(iterator.key());
637             }
638             return true;
639         }
640     }
641     return false;
642 }
643 
644 //---------------------------------------------------------------------------------------------------------------------
ArrangedDetail(QMap<uint,QMultiMap<qint64,int>> & container,int i)645 bool VBank::ArrangedDetail(QMap<uint, QMultiMap<qint64, int>> &container, int i)
646 {
647     QMutableMapIterator<uint, QMultiMap<qint64, int>> iterator(container);
648     while (iterator.hasNext())
649     {
650         iterator.next();
651         auto containerGroup = container.value(iterator.key());
652         auto detailIterator = std::find(containerGroup.begin(), containerGroup.end(), i);
653         if (detailIterator != containerGroup.end())
654         {
655             arranged.append(details.at(detailIterator.value()).GetId());
656             containerGroup.erase(detailIterator);
657 
658             if (not containerGroup.isEmpty())
659             {
660                 container.insert(iterator.key(), containerGroup);
661             }
662             else
663             {
664                 container.remove(iterator.key());
665             }
666             return true;
667         }
668     }
669     return false;
670 }
671 
672 //---------------------------------------------------------------------------------------------------------------------
IsRotationNeeded() const673 bool VBank::IsRotationNeeded() const
674 {
675     for(auto &piece : details)
676     {
677         if (not piece.IsGrainlineEnabled())
678         {
679             return true;
680         }
681     }
682     return false;
683 }
684 
685 
686 #if defined (Q_OS_WIN) && defined (Q_CC_MSVC)
687 #pragma pop_macro("small")
688 #endif
689