Skip to content
21 changes: 12 additions & 9 deletions src/rectpack2D/best_bin_finder.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once
#include <variant>
#include <cassert>
#include <optional>
#include "rect_structs.h"

namespace rectpack2D {
Expand Down Expand Up @@ -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<order_type> 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.
Expand All @@ -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<rect_wh>(&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())) {
Expand Down
67 changes: 23 additions & 44 deletions src/rectpack2D/finders_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ namespace rectpack2D {
using iterator_type = decltype(std::begin(subjects));
using order_type = rectpack2D::span<iterator_type>;

order_type ord(std::begin(subjects), std::end(subjects));

return find_best_packing_impl<empty_spaces_type, order_type>(
[&subjects](auto callback) { callback(order_type(std::begin(subjects), std::end(subjects))); },
input
[=](auto callback) { callback(ord); },
[]() {},
input,
ord
);
}

Expand All @@ -74,12 +78,11 @@ namespace rectpack2D {
using rect_type = output_rect_t<empty_spaces_type>;
using order_type = rectpack2D::span<rect_type**>;

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<rect_type*[]>(count_orders * std::size(subjects));
auto orders = std::make_unique<rect_type*[]>(2 * std::size(subjects));

for (auto& s : subjects) {
auto& r = s.get_rect();
Expand All @@ -91,49 +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<empty_spaces_type, order_type>(
[ith_order](auto callback) {
constexpr auto orders_count = 1 + sizeof...(Comparators);
for (std::size_t i = 0; i < orders_count; ++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 }
);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/example_expected_output.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Resultant bin: 284 875
170 380 20 40
205 439 20 40
85 439 120 40
85 380 85 59
85 0 199 380
Expand Down
Loading