Skip to content
Merged
Show file tree
Hide file tree
Changes from 43 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
7 changes: 5 additions & 2 deletions example/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,16 @@ int main() {
/*
The example will compile just fine if you use any of these instead:

my_rect rectangles[];

std::vector<rect_type> rectangles;
std::list<rect_type> rectangles;

std::list<my_rect> rectangles;

1. The container just needs to be forward-iterable with .begin() and .end().
2. The element type just needs to have get_rect() member functions defined (const and non-const).
1. The container needs to be forward-iterable with std::begin() and std::end(),
and have its size queryable via std::size().
2. The element type just needs to have get_rect() member functions defined (const and non-const).
*/

rectangles.emplace_back(rect_xywh(0, 0, 20, 40));
Expand Down
21 changes: 11 additions & 10 deletions src/rectpack2D/best_bin_finder.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <variant>
#include <cassert>
#include <algorithm>
#include "rect_structs.h"

namespace rectpack2D {
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 (auto it = ordering.first; it != ordering.second; ++it) {
const auto& rect = dereference(*it).get_rect();

if (root.insert(rect.get_wh())) {
total_inserted_area += rect.area();
Expand Down Expand Up @@ -216,7 +217,7 @@ namespace rectpack2D {
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;
std::optional<OrderType> best_order;

int best_total_inserted = -1;
auto best_bin = max_bin;
Expand All @@ -229,7 +230,7 @@ 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 ([&](const OrderType& current_order) {
const auto packing = best_packing_for_ordering(
root,
current_order,
Expand All @@ -242,29 +243,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 == nullptr) {
if (!best_order.has_value()) {
if (*total_inserted > best_total_inserted) {
best_order = std::addressof(current_order);
best_order = current_order;
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 = current_order;
best_bin = *result_bin;
}
}
});

{
assert(best_order != nullptr);
assert(best_order.has_value());

root.reset(best_bin);

for (auto& rr : *best_order) {
auto& rect = dereference(rr).get_rect();
for (auto span = best_order.value(); span.first != span.second; ++span.first) {
auto& rect = dereference(*span.first).get_rect();

if (const auto ret = root.insert(rect.get_wh())) {
rect = *ret;
Expand Down
55 changes: 34 additions & 21 deletions src/rectpack2D/finders_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <vector>
#include <array>
#include <variant>
#include <memory>
#include <algorithm>

#include "insert_and_split.h"
Expand Down Expand Up @@ -51,10 +52,12 @@ namespace rectpack2D {
Subjects& subjects,
const finder_input<F, G>& input
) {
using order_type = std::remove_reference_t<decltype(subjects)>;
// A slightly hacky way of getting the iterator type of any container (including C arrays).
Comment thread
geneotech marked this conversation as resolved.
Outdated
using iterator_type = decltype(std::begin(subjects));
using order_type = std::pair<iterator_type, iterator_type>;

Comment thread
lyorig marked this conversation as resolved.
return find_best_packing_impl<empty_spaces_type, order_type>(
[&subjects](auto callback) { callback(subjects); },
[&subjects](auto callback) { callback(order_type(std::begin(subjects), std::end(subjects))); },
input
);
}
Expand All @@ -77,43 +80,53 @@ namespace rectpack2D {
Comparators... comparators
) {
using rect_type = output_rect_t<empty_spaces_type>;
using order_type = std::vector<rect_type*>;
using order_type = std::pair<rect_type**, rect_type**>;

constexpr auto count_orders = 1 + sizeof...(Comparators);
thread_local std::array<order_type, count_orders> orders;
std::size_t count_valid_subjects = 0;

{
/* order[0] will always exist since this overload requires at least one comparator */
auto& initial_pointers = orders[0];
initial_pointers.clear();
// Allocate space assuming no rectangle has an area of zero.
// The actual size is adjusted later.
auto orders = std::make_unique<rect_type*[]>(count_orders * std::size(subjects));
Comment thread
geneotech marked this conversation as resolved.

for (auto& s : subjects) {
auto& r = s.get_rect();
for (auto& s : subjects) {
auto& r = s.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[count_valid_subjects++] = std::addressof(r);
}

std::size_t f = 0;
// Cut off any potentially unused rectangle pointers at the end.
const auto orders_end = orders.get() + (count_orders * count_valid_subjects);

auto& orders_ref = orders;
for (auto it = orders.get() + count_valid_subjects; it != orders_end; it += count_valid_subjects) {
std::copy(orders.get(), orders.get() + count_valid_subjects, it);
}

auto make_order = [&f, &orders_ref](auto& predicate) {
std::sort(orders_ref[f].begin(), orders_ref[f].end(), predicate);
std::size_t f = 0;

auto make_order = [&f, &orders, &count_valid_subjects](auto& predicate) {
std::sort(
orders.get() + (f * count_valid_subjects),
orders.get() + ((f + 1) * count_valid_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); } },
[count_valid_subjects, &orders, orders_end](auto callback) {
for (auto it = orders.get(); it != orders_end; it += count_valid_subjects) {
callback(std::make_pair(it, it + count_valid_subjects));
Comment thread
geneotech marked this conversation as resolved.
Outdated
}
},
input
);
}
Expand Down