117 {
118 using Placer = PlacementStrategyLike<TPlacer>;
119 using ItemList = std::list<ItemRef>;
120
121 const double bin_area =
sl::area(bin);
123
125 const double INITIAL_FILL_AREA = bin_area*INITIAL_FILL_PROPORTION;
126
128 store_.reserve(last-first);
129
130
132
133 std::copy(first, last, std::back_inserter(
store_));
134
136 return i1.area() > i2.area();
137 });
138
139 size_t glob_vertex_count = 0;
141 [&glob_vertex_count](
const Item& item) {
142 glob_vertex_count += item.vertexCount();
143 });
144
145 std::vector<Placer> placers;
146
148
149
150 auto addBin = [this, &placers, &bin, &pconfig]()
151 {
152 placers.emplace_back(bin);
154 placers.back().configure(pconfig);
155 };
156
157
158 using TPair = std::tuple<ItemRef, ItemRef>;
159 using TTriplet = std::tuple<ItemRef, ItemRef, ItemRef>;
160
161
162
163 auto check_pair = [](const std::vector<TPair>& wrong_pairs,
165 {
166 return std::any_of(wrong_pairs.begin(), wrong_pairs.end(),
167 [&i1, &i2](const TPair& pair)
168 {
169 Item& pi1 = std::get<0>(pair), &pi2 = std::get<1>(pair);
170 Item& ri1 = i1, &ri2 = i2;
171 return (&pi1 == &ri1 && &pi2 == &ri2) ||
172 (&pi1 == &ri2 && &pi2 == &ri1);
173 });
174 };
175
176
177 auto check_triplet = [](
178 const std::vector<TTriplet>& wrong_triplets,
182 {
183 return std::any_of(wrong_triplets.begin(),
184 wrong_triplets.end(),
185 [&i1, &i2, &i3](const TTriplet& tripl)
186 {
187 Item& pi1 = std::get<0>(tripl);
188 Item& pi2 = std::get<1>(tripl);
189 Item& pi3 = std::get<2>(tripl);
190 Item& ri1 = i1, &ri2 = i2, &ri3 = i3;
191 return (&pi1 == &ri1 && &pi2 == &ri2 && &pi3 == &ri3) ||
192 (&pi1 == &ri1 && &pi2 == &ri3 && &pi3 == &ri2) ||
193 (&pi1 == &ri2 && &pi2 == &ri1 && &pi3 == &ri3) ||
194 (&pi1 == &ri3 && &pi2 == &ri2 && &pi3 == &ri1);
195 });
196 };
197
198 using ItemListIt = typename ItemList::iterator;
199
200 auto largestPiece = [](ItemListIt it, ItemList& not_packed) {
201 return it == not_packed.begin()? std::next(it) : not_packed.
begin();
202 };
203
204 auto secondLargestPiece = [&largestPiece](ItemListIt it,
205 ItemList& not_packed) {
206 auto ret = std::next(largestPiece(it, not_packed));
207 return ret == it? std::next(ret) : ret;
208 };
209
210 auto smallestPiece = [](ItemListIt it, ItemList& not_packed) {
211 auto last = std::prev(not_packed.end());
212 return it == last? std::prev(it) : last;
213 };
214
215 auto secondSmallestPiece = [&smallestPiece](ItemListIt it,
216 ItemList& not_packed) {
217 auto ret = std::prev(smallestPiece(it, not_packed));
218 return ret == it? std::prev(ret) : ret;
219 };
220
221 auto tryOneByOne =
222 [&bin_area]
223 (Placer& placer, ItemList& not_packed,
224 double waste,
225 double& free_area,
226 double& filled_area)
227 {
228 double item_area = 0;
229 bool ret = false;
230 auto it = not_packed.begin();
231
232 auto pack = [&placer, ¬_packed](ItemListIt it) {
233 return placer.pack(*it,
rem(it, not_packed));
234 };
235
236 while(it != not_packed.end() && !ret &&
237 free_area - (item_area = it->get().area()) <= waste)
238 {
239 if(item_area <= free_area && pack(it) ) {
240 free_area -= item_area;
241 filled_area = bin_area - free_area;
242 ret = true;
243 } else
244 it++;
245 }
246
247 if(ret) not_packed.erase(it);
248
249 return ret;
250 };
251
252 auto tryGroupsOfTwo =
253 [&bin_area, &check_pair, &largestPiece, &smallestPiece,
254 try_reverse]
255 (Placer& placer, ItemList& not_packed,
256 double waste,
257 double& free_area,
258 double& filled_area)
259 {
260 double item_area = 0;
261 const auto endit = not_packed.end();
262
263 if(not_packed.size() < 2)
264 return false;
265
266 double largest_area = not_packed.front().get().area();
267 auto itmp = not_packed.begin(); itmp++;
268 double second_largest = itmp->get().area();
269 if( free_area - second_largest - largest_area > waste)
270 return false;
271
272
273
274 bool ret = false;
275 auto it = not_packed.begin();
276 auto it2 = it;
277
278 std::vector<TPair> wrong_pairs;
279 using std::placeholders::_1;
280
281 auto trypack = [&placer, ¬_packed](ItemListIt it) {
282 return placer.trypack(*it,
rem(it, not_packed));
283 };
284
285 while(it != endit && !ret &&
286 free_area - (item_area = it->get().area()) -
287 largestPiece(it, not_packed)->get().area() <= waste)
288 {
289 if(item_area + smallestPiece(it, not_packed)->get().area() >
290 free_area ) { it++; continue; }
291
292 auto pr = trypack(it);
293
294
295 it2 = not_packed.begin();
296 double item2_area = 0;
297 while(it2 != endit && pr && !ret && free_area -
298 (item2_area = it2->get().area()) - item_area <= waste)
299 {
300 double area_sum = item_area + item2_area;
301
302 if(it == it2 || area_sum > free_area ||
303 check_pair(wrong_pairs, *it, *it2)) {
304 it2++; continue;
305 }
306
307 placer.accept(pr);
308 auto pr2 = trypack(it2);
309 if(!pr2) {
310 placer.unpackLast();
311 if(try_reverse) {
312 pr2 = trypack(it2);
313 if(pr2) {
314 placer.accept(pr2);
315 auto pr12 = trypack(it);
316 if(pr12) {
317 placer.accept(pr12);
318 ret = true;
319 } else {
320 placer.unpackLast();
321 }
322 }
323 }
324 } else {
325 placer.accept(pr2); ret = true;
326 }
327
328 if(ret)
329 {
330 free_area -= area_sum;
331 filled_area = bin_area - free_area;
332 } else {
333 wrong_pairs.emplace_back(*it, *it2);
334 it2++;
335 }
336 }
337
338 if(!ret) it++;
339 }
340
341 if(ret) { not_packed.erase(it); not_packed.erase(it2); }
342
343 return ret;
344 };
345
346 auto tryGroupsOfThree =
347 [&bin_area,
348 &smallestPiece, &largestPiece,
349 &secondSmallestPiece, &secondLargestPiece,
350 &check_pair, &check_triplet, try_reverse]
351 (Placer& placer, ItemList& not_packed,
352 double waste,
353 double& free_area,
354 double& filled_area)
355 {
356 auto np_size = not_packed.size();
357 if(np_size < 3) return false;
358
359 auto it = not_packed.begin();
360 const auto endit = not_packed.end();
361 auto it2 = it, it3 = it;
362
363
364
365 std::vector<TPair> wrong_pairs;
366 std::vector<TTriplet> wrong_triplets;
367
368 auto cap = np_size*np_size / 2 ;
369 wrong_pairs.reserve(cap);
370 wrong_triplets.reserve(cap);
371
372
373 bool ret = false;
374
375 auto area = [](
const ItemListIt& it) {
376 return it->get().area();
377 };
378
379 auto trypack = [&placer, ¬_packed](ItemListIt it) {
380 return placer.trypack(*it,
rem(it, not_packed));
381 };
382
383 auto pack = [&placer, ¬_packed](ItemListIt it) {
384 return placer.pack(*it,
rem(it, not_packed));
385 };
386
387 while (it != endit && !ret) {
388
389
390
391
392 Item& largest = *largestPiece(it, not_packed);
393 Item& second_largest = *secondLargestPiece(it, not_packed);
394
395 double area_of_two_largest =
396 largest.area() + second_largest.area();
397
398
399
400 if(free_area -
area(it) - area_of_two_largest > waste)
401 break;
402
403
404 Item& smallest = *smallestPiece(it, not_packed);
405 Item& second_smallest = *secondSmallestPiece(it, not_packed);
406
407
408
409 double area_of_two_smallest =
410 smallest.area() + second_smallest.area();
411
412 if(
area(it) + area_of_two_smallest > free_area) {
413 it++; continue;
414 }
415
416 auto pr = trypack(it);
417
418
419 if(!pr) { it++; continue; }
420
421 it2 = not_packed.begin();
422 double rem2_area = free_area - largest.area();
423 double a2_sum = 0;
424
425 while(it2 != endit && !ret &&
426 rem2_area - (a2_sum =
area(it) +
area(it2)) <= waste) {
427
428
429 if(a2_sum !=
area(it) +
area(it2))
throw -1;
430
431 if(it == it2 || check_pair(wrong_pairs, *it, *it2)) {
432 it2++; continue;
433 }
434
435 if(a2_sum + smallest.area() > free_area) {
436 it2++; continue;
437 }
438
439 bool can_pack2 = false;
440
441 placer.accept(pr);
442 auto pr2 = trypack(it2);
443 auto pr12 = pr;
444 if(!pr2) {
445 placer.unpackLast();
446 if(try_reverse) {
447 pr2 = trypack(it2);
448 if(pr2) {
449 placer.accept(pr2);
450 pr12 = trypack(it);
451 if(pr12) can_pack2 = true;
452 placer.unpackLast();
453 }
454 }
455 } else {
456 placer.unpackLast();
457 can_pack2 = true;
458 }
459
460 if(!can_pack2) {
461 wrong_pairs.emplace_back(*it, *it2);
462 it2++;
463 continue;
464 }
465
466
467
468
469
470 it3 = not_packed.begin();
471
472 double a3_sum = 0;
473
474 while(it3 != endit && !ret &&
475 free_area - (a3_sum = a2_sum +
area(it3)) <= waste) {
476
477
478 if(it3 == it || it3 == it2 ||
479 check_triplet(wrong_triplets, *it, *it2, *it3))
480 { it3++; continue; }
481
482 if(a3_sum > free_area) { it3++; continue; }
483
484 placer.accept(pr12); placer.accept(pr2);
485 bool can_pack3 = pack(it3);
486
487 if(!can_pack3) {
488 placer.unpackLast();
489 placer.unpackLast();
490 }
491
492 if(!can_pack3 && try_reverse) {
493
494 std::array<size_t, 3> indices = {0, 1, 2};
495 std::array<typename ItemList::iterator, 3>
496 candidates = {it, it2, it3};
497
498 auto tryPack = [&placer, &candidates, &pack](
499 const decltype(indices)& idx)
500 {
501 std::array<bool, 3> packed = {false};
502
503 for(auto id : idx) packed.at(id) =
504 pack(candidates[id]);
505
507 std::all_of(packed.begin(),
508 packed.end(),
509 [](bool b) { return b; });
510
511 if(!check)
for(
bool b : packed)
if(
b)
512 placer.unpackLast();
513
515 };
516
517 while (!can_pack3 && std::next_permutation(
518 indices.begin(),
519 indices.end())){
520 can_pack3 = tryPack(indices);
521 };
522 }
523
524 if(can_pack3) {
525
526 free_area -= a3_sum;
527 filled_area = bin_area - free_area;
528 ret = true;
529 } else {
530 wrong_triplets.emplace_back(*it, *it2, *it3);
531 it3++;
532 }
533
534 }
535
536 if(!ret) it2++;
537
538 }
539
540 if(!ret) it++;
541
542 }
543
544 if(ret) {
545 not_packed.erase(it);
546 not_packed.erase(it2);
547 not_packed.erase(it3);
548 }
549
550 return ret;
551 };
552
553 this->
template remove_unpackable_items<Placer>(
store_, bin, pconfig);
554
555 int acounter = int(
store_.size());
556 std::atomic_flag flg = ATOMIC_FLAG_INIT;
557 SpinLock slock(flg);
558
559 auto makeProgress = [this, &acounter, &slock]
560 (Placer& placer, size_t idx, int packednum)
561 {
562
564
565
566 slock.lock();
567 acounter -= packednum;
569 slock.unlock();
570 };
571
572 double items_area = 0;
574
575
576 auto bincount_guess = unsigned(std::ceil(items_area / bin_area));
577
578
583
584 if(do_parallel)
dout() <<
"Parallel execution..." <<
"\n";
585
589
590
591 auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs,
592 stopcond,
593 &tryOneByOne,
594 &tryGroupsOfTwo,
595 &tryGroupsOfThree,
596 &makeProgress]
597 (Placer& placer, ItemList& not_packed, size_t idx)
598 {
599 double filled_area = placer.filledArea();
600 double free_area = bin_area - filled_area;
601 double waste = .0;
602 bool lasttry = false;
603
604 while(!not_packed.empty() && !stopcond()) {
605
606 {
607 auto it = not_packed.begin();
608
609 while(it != not_packed.end() && !stopcond() &&
610 filled_area < INITIAL_FILL_AREA)
611 {
612 if(placer.pack(*it,
rem(it, not_packed))) {
613 filled_area += it->get().area();
614 free_area = bin_area - filled_area;
615 it = not_packed.erase(it);
616 makeProgress(placer, idx, 1);
617 } else it++;
618 }
619 }
620
621
622 while(tryOneByOne(placer, not_packed, waste, free_area,
623 filled_area)) {
624 waste = 0; lasttry = false;
625 makeProgress(placer, idx, 1);
626 }
627
628
629 while(do_pairs &&
630 tryGroupsOfTwo(placer, not_packed, waste, free_area,
631 filled_area)) {
632 waste = 0; lasttry = false;
633 makeProgress(placer, idx, 2);
634 }
635
636
637 while(do_triplets &&
638 tryGroupsOfThree(placer, not_packed, waste, free_area,
639 filled_area)) {
640 waste = 0; lasttry = false;
641 makeProgress(placer, idx, 3);
642 }
643
644 waste += w;
645 if(!lasttry && waste > free_area) lasttry = true;
646 else if(lasttry) break;
647 }
648 };
649
650 size_t idx = 0;
651 ItemList remaining;
652
653 if(do_parallel) {
654 std::vector<ItemList> not_packeds(bincount_guess);
655
656
657 for(
unsigned b = 0;
b < bincount_guess;
b++) {
658 addBin();
659 ItemList& not_packed = not_packeds[
b];
660 for(
unsigned idx = b; idx <
store_.size(); idx+=bincount_guess) {
661 not_packed.emplace_back(
store_[idx]);
662 }
663 }
664
665
666 auto job = [&placers, ¬_packeds, &packjob](unsigned idx) {
667 Placer& placer = placers[idx];
668 ItemList& not_packed = not_packeds[idx];
669 return packjob(placer, not_packed, idx);
670 };
671
672
673 std::vector<std::future<void>> rets(bincount_guess);
674
675 for(
unsigned b = 0;
b < bincount_guess;
b++) {
676 rets[
b] = std::async(std::launch::async, job, b);
677 }
678
679 for(unsigned fi = 0; fi < rets.size(); ++fi) {
680 rets[fi].wait();
681
682
683 remaining.merge( not_packeds[fi], [](
Item& i1,
Item& i2) {
684 return i1.area() > i2.area();
685 });
686
687 }
688
689 idx = placers.size();
690
691
692 if(remaining.size() <= placers.size())
693 for(size_t j = 0; j < idx && !remaining.empty(); j++) {
694 packjob(placers[j], remaining, j);
695 }
696
697 } else {
699 }
700
701 while(!remaining.empty()) {
702 addBin();
703 packjob(placers[idx], remaining, idx); idx++;
704 }
705
706 int binid = 0;
708 for(
Item& itm : bin) itm.binId(binid);
709 binid++;
710 }
711 }
double initial_fill_proportion
Definition djd_heuristic.hpp:71
bool try_pairs
try_pairs Whether to try pairs of items to pack. It will add a quadratic component to the complexity.
Definition djd_heuristic.hpp:53
double waste_increment
How much is the acceptable waste incremented at each iteration.
Definition djd_heuristic.hpp:76
bool try_reverse_order
Definition djd_heuristic.hpp:47
_Item< RawShape > Item
Definition selection_boilerplate.hpp:13
static const unsigned MAX_ITEMS_SEQUENTIALLY
Definition djd_heuristic.hpp:101
PackGroup packed_bins_
Definition selection_boilerplate.hpp:56
bool try_triplets
Whether to try groups of 3 items to pack. This could be very slow for large number of items (>100) as...
Definition djd_heuristic.hpp:60
static const unsigned MAX_VERTICES_SEQUENTIALLY
Definition djd_heuristic.hpp:102
std::reference_wrapper< Item > ItemRef
Definition djd_heuristic.hpp:36
bool force_parallel
Always use parallel processing if the items don't fit into one bin.
Definition djd_heuristic.hpp:90
bool allow_parallel
Allow parallel jobs for filling multiple bins.
Definition djd_heuristic.hpp:84
Container store_
Definition djd_heuristic.hpp:98
StopCondition stopcond_
Definition selection_boilerplate.hpp:58
ProgressFunction progress_
Definition selection_boilerplate.hpp:57
if(!(yy_init))
Definition lexer.c:1190
S::iterator begin(S &sh, const PathTag &)
Definition geometry_traits.hpp:614
Unit area(const Cntr &poly, const PathTag &)
Definition geometry_traits.hpp:971
std::function< bool(void)> StopCondition
Definition nester.hpp:678
DOut dout()
Definition common.hpp:63
ConstItemRange< typename Container::const_iterator > rem(typename Container::const_iterator it, const Container &cont)
Definition nester.hpp:534
bool check(const DataBase &input, bool check_fontfile=true, bool use_surface=false)
Assert check of inputs data.
Definition EmbossJob.cpp:348