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