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 9e85568..1496cf1 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; - constexpr auto count_orders = 1 + sizeof...(Comparators); 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 * std::size(subjects)); for (auto& s : subjects) { auto& r = s.get_rect(); @@ -91,48 +94,25 @@ 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), ...); - } + 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( - [ith_order](auto callback) { - for (std::size_t i = 0; i < count_orders; ++i) { - callback(ith_order(i)); - } + // 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 = [=](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 } ); }