Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
647a417
initial
lyorig Sep 23, 2025
d8c0950
remove .cache/
lyorig Sep 23, 2025
a9b99f1
re-constify rect reference in all_inserted
lyorig Sep 23, 2025
3d3ba3b
properly resize vector every time
lyorig Sep 23, 2025
2471d26
find_best_packing: put order vector on stack instead of TLS
lyorig Sep 23, 2025
725b464
replace std::span with templated iterators
lyorig Oct 5, 2025
6f36d22
remove unused type alias
lyorig Oct 5, 2025
d2185d1
fix whitespace
geneotech Oct 5, 2025
354ebdd
merge changes
lyorig Oct 5, 2025
b9e7a86
fix whitespace
geneotech Oct 5, 2025
bce82f1
pointers instead of iterators + correctly set vector size
lyorig Oct 5, 2025
550d3f8
remove unused typedef
lyorig Oct 5, 2025
9097f0a
fix: resize before copy to ensure proper end() pointer
lyorig Oct 5, 2025
23e0f6d
restore example.cpp
lyorig Oct 5, 2025
84f7bf3
find_best_packing: return early if there are no subjects
lyorig Oct 5, 2025
9b4df30
example: clarify raw arrays can be used
lyorig Oct 6, 2025
1694965
find_best_packing_dont_sort: fix edge case of empty subjects
lyorig Oct 6, 2025
adecac7
example: clarify necessary STL container functions
lyorig Oct 6, 2025
614cc9a
remove extraneous scope
lyorig Oct 6, 2025
cd701ed
fix whitespace
lyorig Oct 6, 2025
1b577de
example: restore rectangle sizes
lyorig Oct 6, 2025
91cc9b8
relocate empty subject list check to find_best_packing_impl
lyorig Oct 6, 2025
9e8b4ee
backtrack certain changes; use unique_ptr for storing rects
lyorig Oct 7, 2025
cd7c85e
example: document std::size() subject requirement
lyorig Oct 8, 2025
ac109a2
use std::pair<iterator, iterator> as a makeshift std::span
lyorig Oct 8, 2025
d60fa74
compile fix: missing <memory>
lyorig Oct 8, 2025
44bc398
find_best_packing_impl: use std::optional for best order iterator
lyorig Oct 8, 2025
60f5245
find_best_packing_impl: move both iterators into std::optional
lyorig Oct 11, 2025
4b56111
best_packing_for_ordering(_impl): switch back to generic O ordering
lyorig Oct 11, 2025
1ddc9bb
example: fix whitespace
lyorig Oct 11, 2025
e4a09cc
find_best_packing: rename count_subjects to count_valid_subjects
lyorig Oct 11, 2025
8d61511
find_best_packing_impl: re-add scope braces
lyorig Oct 11, 2025
22689a3
find_best_packing_impl: remove unnecessary comment
lyorig Oct 11, 2025
adcac4f
find_best_packing_impl: alias iterator pair as OrderType
lyorig Oct 11, 2025
5933cb9
preserve order_type
lyorig Oct 11, 2025
7a00a21
example: fix whitespace
lyorig Oct 11, 2025
cbb9708
best_bin_finder: fix whitespace
lyorig Oct 11, 2025
d355b20
finders_interface: fix whitespace
lyorig Oct 11, 2025
c96e235
finders_interface: fix whitespace
lyorig Oct 11, 2025
9d43cef
Revert "finders_interface: fix whitespace"
lyorig Oct 11, 2025
9f99e9b
find_best_packing_impl: const-ify OrderType& in for_each_order lambda
lyorig Oct 11, 2025
56b1612
best_bin_finder: de-format
lyorig Oct 12, 2025
b9a283f
Apply suggestion from @geneotech
geneotech Oct 12, 2025
12658a8
Apply suggestion from @geneotech
geneotech Oct 12, 2025
9ec723d
Apply suggestion from @geneotech
geneotech Oct 12, 2025
a10f726
comments
Oct 12, 2025
742aa2c
comments
Oct 12, 2025
68082c2
adjust scope
Oct 12, 2025
da4c297
emulate span to simplify loops
Oct 12, 2025
d0e716a
more readable loop
Oct 12, 2025
f50cbb0
simplify loops with ith_order lambda
Oct 12, 2025
8257624
adjust
Oct 12, 2025
af53bd6
adjust
Oct 12, 2025
7dcd6ab
const
Oct 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/cmake-build-debug/
.DS_Store
.idea
.cache/
*.iml
example/clang_commands.sh
example/main
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ target_include_directories(
rectpack2D
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/
)
)
40 changes: 23 additions & 17 deletions src/rectpack2D/best_bin_finder.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ namespace rectpack2D {
HEIGHT
};

template <class empty_spaces_type, class O>
template <class empty_spaces_type, class OrderingIterator>
std::variant<total_area_type, rect_wh> best_packing_for_ordering_impl(
empty_spaces_type& root,
O ordering,
OrderingIterator ordering_begin,
OrderingIterator ordering_end,
const rect_wh starting_bin,
int discard_step,
const bin_dimension tried_dimension
Expand Down Expand Up @@ -88,8 +89,8 @@ namespace rectpack2D {
int total_inserted_area = 0;

const bool all_inserted = [&]() {
for (const auto& r : ordering) {
const auto& rect = dereference(r).get_rect();
for (OrderingIterator it = ordering_begin; it != ordering_end; ++it) {
const auto& rect = dereference(*it).get_rect();

if (root.insert(rect.get_wh())) {
total_inserted_area += rect.area();
Expand Down Expand Up @@ -158,10 +159,11 @@ namespace rectpack2D {
}
}

template <class empty_spaces_type, class O>
template <class empty_spaces_type, class OrderingIterator>
std::variant<total_area_type, rect_wh> best_packing_for_ordering(
empty_spaces_type& root,
O&& ordering,
OrderingIterator ordering_begin,
OrderingIterator ordering_end,
const rect_wh starting_bin,
const int discard_step
) {
Expand All @@ -171,7 +173,8 @@ namespace rectpack2D {
) {
return best_packing_for_ordering_impl(
root,
std::forward<O>(ordering),
ordering_begin,
ordering_end,
starting_bin,
discard_step,
tried_dimension
Expand Down Expand Up @@ -209,14 +212,14 @@ namespace rectpack2D {

template <
class empty_spaces_type,
class OrderType,
class OrderingIterator,
class F,
class I
>
rect_wh find_best_packing_impl(F for_each_order, const I input) {
const auto max_bin = rect_wh(input.max_bin_side, input.max_bin_side);

Comment thread
geneotech marked this conversation as resolved.
OrderType* best_order = nullptr;
OrderingIterator best_order_begin = OrderingIterator{}, best_order_end;

int best_total_inserted = -1;
auto best_bin = max_bin;
Expand All @@ -229,10 +232,11 @@ namespace rectpack2D {
thread_local empty_spaces_type root = rect_wh();
root.flipping_mode = input.flipping_mode;

for_each_order ([&](OrderType& current_order) {
Comment thread
geneotech marked this conversation as resolved.
for_each_order ([&](OrderingIterator ordering_begin, OrderingIterator ordering_end) {
const auto packing = best_packing_for_ordering(
root,
current_order,
ordering_begin,
ordering_end,
max_bin,
input.discard_step
);
Expand All @@ -242,29 +246,31 @@ 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 == nullptr) {
if (best_order_begin == OrderingIterator{}) {
if (*total_inserted > best_total_inserted) {
best_order = std::addressof(current_order);
best_order_begin = ordering_begin;
best_order_end = ordering_end;
best_total_inserted = *total_inserted;
}
}
}
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 = std::addressof(current_order);
best_order_begin = ordering_begin;
best_order_end = ordering_end;
best_bin = *result_bin;
}
}
});

{
assert(best_order != nullptr);
assert(best_order_begin != OrderingIterator{});
Comment thread
geneotech marked this conversation as resolved.
Outdated

root.reset(best_bin);

for (auto& rr : *best_order) {
auto& rect = dereference(rr).get_rect();
for (OrderingIterator it = best_order_begin; it != best_order_end; ++it) {
auto& rect = dereference(*it).get_rect();

if (const auto ret = root.insert(rect.get_wh())) {
rect = *ret;
Expand Down
64 changes: 41 additions & 23 deletions src/rectpack2D/finders_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ namespace rectpack2D {
Subjects& subjects,
const finder_input<F, G>& input
) {
using order_type = std::remove_reference_t<decltype(subjects)>;
using iterator_type = decltype(std::begin(subjects));

Comment thread
lyorig marked this conversation as resolved.
return find_best_packing_impl<empty_spaces_type, order_type>(
[&subjects](auto callback) { callback(subjects); },
return find_best_packing_impl<empty_spaces_type, iterator_type>(
[&subjects](auto callback) { callback(std::begin(subjects), std::end(subjects)); },
input
);
}
Expand All @@ -63,7 +63,7 @@ namespace rectpack2D {
/*
Finds the best packing for the rectangles.
Accepts a list of predicates able to compare two input rectangles.

The function will try to pack the rectangles in all orders generated by the predicates,
and will only write the x, y coordinates of the best packing found among the orders.
*/
Expand All @@ -77,43 +77,61 @@ namespace rectpack2D {
Comparators... comparators
) {
using rect_type = output_rect_t<empty_spaces_type>;
using order_type = std::vector<rect_type*>;

constexpr auto count_orders = 1 + sizeof...(Comparators);
thread_local std::array<order_type, count_orders> orders;
const std::size_t count_subjects = std::count_if(
std::begin(subjects),
std::end(subjects),
[](const auto& sub) {
return sub.get_rect().area() != 0;
}
);

{
/* order[0] will always exist since this overload requires at least one comparator */
auto& initial_pointers = orders[0];
initial_pointers.clear();
std::vector<rect_type*> orders(count_orders * count_subjects);

for (auto& s : subjects) {
auto& r = s.get_rect();
for (std::size_t i = 0; i < count_subjects; ++i) {
auto& r = subjects[i].get_rect();

if (r.area() > 0) {
Comment thread
geneotech marked this conversation as resolved.
initial_pointers.emplace_back(std::addressof(r));
}
if (r.area() == 0) {
continue;
}

for (std::size_t i = 1; i < count_orders; ++i) {
orders[i] = initial_pointers;
}
orders[i] = std::addressof(r);
}

std::size_t f = 0;
std::transform(
std::begin(subjects),
std::end(subjects),
orders.begin(),
[](auto& sub) { return &sub.get_rect(); }
);
Comment thread
geneotech marked this conversation as resolved.
Outdated

for (auto it = orders.begin() + count_subjects; it != orders.end(); it += count_subjects) {
Comment thread
geneotech marked this conversation as resolved.
Outdated
std::copy(orders.begin(), orders.begin() + count_subjects, it);
}

std::size_t f = 0;
auto& orders_ref = orders;

auto make_order = [&f, &orders_ref](auto& predicate) {
std::sort(orders_ref[f].begin(), orders_ref[f].end(), predicate);
auto make_order = [&f, &orders_ref, &count_subjects](auto& predicate) {
std::sort(
orders_ref.begin() + (f * count_subjects),
orders_ref.begin() + ((f + 1) * count_subjects),
predicate
);
++f;
};

make_order(comparator);
(make_order(comparators), ...);

return find_best_packing_impl<empty_spaces_type, order_type>(
[&orders_ref](auto callback){ for (auto& o : orders_ref) { callback(o); } },

return find_best_packing_impl<empty_spaces_type, decltype(std::begin(orders))>(
[count_subjects, &orders_ref](auto callback) {
for (auto it = orders_ref.begin(); it != orders_ref.end(); it += count_subjects) {
callback(it, it + count_subjects);
}
},
input
);
}
Expand Down