From d0a17146b643f4b8a7e5a6d59bdc3b1a4f9bfe3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0=C3=A1cha?= Date: Mon, 3 Nov 2025 18:31:55 +0100 Subject: [PATCH 1/6] initial working(?) draft --- src/rectpack2D/finders_interface.h | 56 ++++++++---------------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/src/rectpack2D/finders_interface.h b/src/rectpack2D/finders_interface.h index 9e85568..1c5ca34 100644 --- a/src/rectpack2D/finders_interface.h +++ b/src/rectpack2D/finders_interface.h @@ -74,12 +74,12 @@ namespace rectpack2D { using rect_type = output_rect_t; using order_type = rectpack2D::span; - constexpr auto count_orders = 1 + sizeof...(Comparators); + std::size_t count_total_subjects = std::size(subjects); std::size_t count_valid_subjects = 0; // Allocate space assuming no rectangle has an area of zero. // We fill orders with valid rectangles only. - auto orders = std::make_unique(count_orders * std::size(subjects)); + auto orders = std::make_unique(2 * count_total_subjects); for (auto& s : subjects) { auto& r = s.get_rect(); @@ -91,46 +91,20 @@ namespace rectpack2D { orders[count_valid_subjects++] = std::addressof(r); } - auto ith_order = [&orders, n = count_valid_subjects](const std::size_t i) { - return order_type( - orders.get() + i * n, - orders.get() + (i + 1) * n - ); - }; - - { - /* - Zero-th order is already filled. - We duplicate it to all other orders. - */ - const auto first_order = ith_order(0); - - for (std::size_t i = 1; i < count_orders; ++i) { - std::copy( - first_order.begin(), - first_order.end(), - ith_order(i).begin() - ); - } - } - - { - std::size_t i = 0; - - auto make_order = [&i, ith_order](auto& predicate) { - const auto o = ith_order(i++); - std::sort(o.begin(), o.end(), predicate); - }; - - make_order(comparator); - (make_order(comparators), ...); - } - return find_best_packing_impl( - [ith_order](auto callback) { - for (std::size_t i = 0; i < count_orders; ++i) { - callback(ith_order(i)); - } + [&, + order_ptr = orders.get(), + orders_separator = orders.get() + count_total_subjects, + orders_end = orders.get() + 2 * count_total_subjects + ](auto callback) { + auto make_order = [&, callback](auto& predicate) { + std::copy(order_ptr, orders_separator, orders_separator); + std::sort(orders_separator, orders_end, predicate); + callback({orders_separator, orders_end}); + }; + + make_order(comparator); + (make_order(comparators), ...); }, input ); From 68687150ddfc67905497887c57cc09be3ff08c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0=C3=A1cha?= Date: Thu, 13 Nov 2025 09:47:22 +0100 Subject: [PATCH 2/6] seemingly in a working state --- src/rectpack2D/best_bin_finder.h | 21 +++++++++++--------- src/rectpack2D/finders_interface.h | 32 +++++++++++++++++------------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/rectpack2D/best_bin_finder.h b/src/rectpack2D/best_bin_finder.h index 3a5655d..3e096f4 100644 --- a/src/rectpack2D/best_bin_finder.h +++ b/src/rectpack2D/best_bin_finder.h @@ -1,7 +1,6 @@ #pragma once #include #include -#include #include "rect_structs.h" namespace rectpack2D { @@ -227,16 +226,18 @@ namespace rectpack2D { class empty_spaces_type, class order_type, class F, + class G, class I > - rect_wh find_best_packing_impl(F for_each_order, const I input) { + rect_wh find_best_packing_impl(F for_each_order, G best_callback, const I input, order_type best_order) { const auto max_bin = rect_wh(input.max_bin_side, input.max_bin_side); - std::optional best_order; - int best_total_inserted = -1; auto best_bin = max_bin; + // Poor man's std::optional, since the best order storage is provided by the caller. + bool best_order_has_value = false; + /* The root node is re-used on the TLS. It is always reset before any packing attempt. @@ -258,27 +259,29 @@ namespace rectpack2D { Track which function inserts the most area in total, just in case that all orders will fail to fit into the largest allowed bin. */ - if (!best_order.has_value()) { + if (!best_order_has_value) { if (*total_inserted > best_total_inserted) { - best_order = current_order; + best_order_has_value = true; best_total_inserted = *total_inserted; + best_callback(); } } } else if (const auto result_bin = std::get_if(&packing)) { /* Save the function if it performed the best. */ if (result_bin->area() <= best_bin.area()) { - best_order = current_order; + best_order_has_value = true; best_bin = *result_bin; + best_callback(); } } }); - assert(best_order.has_value()); + assert(best_order_has_value); root.reset(best_bin); - for (auto& rr : *best_order) { + for (auto& rr : best_order) { auto& rect = dereference(rr).get_rect(); if (const auto ret = root.insert(rect.get_wh())) { diff --git a/src/rectpack2D/finders_interface.h b/src/rectpack2D/finders_interface.h index 1c5ca34..51781f2 100644 --- a/src/rectpack2D/finders_interface.h +++ b/src/rectpack2D/finders_interface.h @@ -48,9 +48,13 @@ namespace rectpack2D { using iterator_type = decltype(std::begin(subjects)); using order_type = rectpack2D::span; + order_type ord(std::begin(subjects), std::end(subjects)); + return find_best_packing_impl( - [&subjects](auto callback) { callback(order_type(std::begin(subjects), std::end(subjects))); }, - input + [=](auto callback) { callback(ord); }, + []() {}, + input, + ord ); } @@ -74,12 +78,11 @@ namespace rectpack2D { using rect_type = output_rect_t; using order_type = rectpack2D::span; - std::size_t count_total_subjects = std::size(subjects); std::size_t count_valid_subjects = 0; // Allocate space assuming no rectangle has an area of zero. // We fill orders with valid rectangles only. - auto orders = std::make_unique(2 * count_total_subjects); + auto orders = std::make_unique(2 * std::size(subjects)); for (auto& s : subjects) { auto& r = s.get_rect(); @@ -91,22 +94,23 @@ namespace rectpack2D { orders[count_valid_subjects++] = std::addressof(r); } + auto orders_begin = orders.get(); + auto orders_separator = orders_begin + count_valid_subjects; + auto orders_end = orders_separator + count_valid_subjects; + return find_best_packing_impl( - [&, - order_ptr = orders.get(), - orders_separator = orders.get() + count_total_subjects, - orders_end = orders.get() + 2 * count_total_subjects - ](auto callback) { - auto make_order = [&, callback](auto& predicate) { - std::copy(order_ptr, orders_separator, orders_separator); - std::sort(orders_separator, orders_end, predicate); - callback({orders_separator, orders_end}); + [=](auto callback) { + auto make_order = [&, callback](auto predicate) { + std::sort(orders_begin, orders_separator, predicate); + callback({orders_begin, orders_separator}); }; make_order(comparator); (make_order(comparators), ...); }, - input + [=]() { std::copy(orders_begin, orders_separator, orders_separator); }, + input, + {orders_separator, orders_end} ); } From 999fe19ba46984e0a6361e352acebc721ee44927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0=C3=A1cha?= Date: Thu, 13 Nov 2025 09:51:11 +0100 Subject: [PATCH 3/6] remove redundant best_callback --- src/rectpack2D/best_bin_finder.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rectpack2D/best_bin_finder.h b/src/rectpack2D/best_bin_finder.h index 3e096f4..4fcfd4d 100644 --- a/src/rectpack2D/best_bin_finder.h +++ b/src/rectpack2D/best_bin_finder.h @@ -263,7 +263,6 @@ namespace rectpack2D { if (*total_inserted > best_total_inserted) { best_order_has_value = true; best_total_inserted = *total_inserted; - best_callback(); } } } From caa5664229bca8c858791494985da7ffaf561bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0=C3=A1cha?= Date: Thu, 13 Nov 2025 09:52:35 +0100 Subject: [PATCH 4/6] find_best_packing: explicit and explained captures --- src/rectpack2D/finders_interface.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rectpack2D/finders_interface.h b/src/rectpack2D/finders_interface.h index 51781f2..07cd3c9 100644 --- a/src/rectpack2D/finders_interface.h +++ b/src/rectpack2D/finders_interface.h @@ -99,7 +99,9 @@ namespace rectpack2D { auto orders_end = orders_separator + count_valid_subjects; return find_best_packing_impl( - [=](auto callback) { + // Predicates can be expensive-to-copy objects such as std::function, + // so capture them by reference just to be sure. + [=, &comparator, &comparators...](auto callback) { auto make_order = [&, callback](auto predicate) { std::sort(orders_begin, orders_separator, predicate); callback({orders_begin, orders_separator}); From 352a101477e8cfe7f9530363268a2b12d7da4077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0=C3=A1cha?= Date: Thu, 13 Nov 2025 10:08:44 +0100 Subject: [PATCH 5/6] find_best_packing: more by-value captures --- src/rectpack2D/finders_interface.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rectpack2D/finders_interface.h b/src/rectpack2D/finders_interface.h index 07cd3c9..1496cf1 100644 --- a/src/rectpack2D/finders_interface.h +++ b/src/rectpack2D/finders_interface.h @@ -102,7 +102,7 @@ namespace rectpack2D { // Predicates can be expensive-to-copy objects such as std::function, // so capture them by reference just to be sure. [=, &comparator, &comparators...](auto callback) { - auto make_order = [&, callback](auto predicate) { + auto make_order = [=](auto predicate) { std::sort(orders_begin, orders_separator, predicate); callback({orders_begin, orders_separator}); }; @@ -112,7 +112,7 @@ namespace rectpack2D { }, [=]() { std::copy(orders_begin, orders_separator, orders_separator); }, input, - {orders_separator, orders_end} + { orders_separator, orders_end } ); } From 36f304171dd5fbe3b51eb24c3e065e9ad93cd3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0=C3=A1cha?= Date: Thu, 13 Nov 2025 10:13:15 +0100 Subject: [PATCH 6/6] re-add best_callback() --- src/rectpack2D/best_bin_finder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rectpack2D/best_bin_finder.h b/src/rectpack2D/best_bin_finder.h index 4fcfd4d..3e096f4 100644 --- a/src/rectpack2D/best_bin_finder.h +++ b/src/rectpack2D/best_bin_finder.h @@ -263,6 +263,7 @@ namespace rectpack2D { if (*total_inserted > best_total_inserted) { best_order_has_value = true; best_total_inserted = *total_inserted; + best_callback(); } } }