Skip to content

Commit 96f4404

Browse files
lyoriggeneotechPatryk Czachurski
authored
find_best_packing: store orders side-by-side in a single vector (#15)
* initial * remove .cache/ * re-constify rect reference in all_inserted * properly resize vector every time * find_best_packing: put order vector on stack instead of TLS * replace std::span with templated iterators * remove unused type alias * fix whitespace * merge changes * fix whitespace * pointers instead of iterators + correctly set vector size * remove unused typedef * fix: resize before copy to ensure proper end() pointer * restore example.cpp * find_best_packing: return early if there are no subjects * example: clarify raw arrays can be used * find_best_packing_dont_sort: fix edge case of empty subjects * example: clarify necessary STL container functions * remove extraneous scope * fix whitespace * example: restore rectangle sizes * relocate empty subject list check to find_best_packing_impl * backtrack certain changes; use unique_ptr for storing rects * example: document std::size() subject requirement * use std::pair<iterator, iterator> as a makeshift std::span * compile fix: missing <memory> * find_best_packing_impl: use std::optional for best order iterator * find_best_packing_impl: move both iterators into std::optional * best_packing_for_ordering(_impl): switch back to generic O ordering * example: fix whitespace * find_best_packing: rename count_subjects to count_valid_subjects * find_best_packing_impl: re-add scope braces * find_best_packing_impl: remove unnecessary comment * find_best_packing_impl: alias iterator pair as OrderType * preserve order_type * example: fix whitespace * best_bin_finder: fix whitespace * finders_interface: fix whitespace * finders_interface: fix whitespace * Revert "finders_interface: fix whitespace" This reverts commit c96e235. * find_best_packing_impl: const-ify OrderType& in for_each_order lambda * best_bin_finder: de-format * Apply suggestion from @geneotech * Apply suggestion from @geneotech * Apply suggestion from @geneotech * comments * comments * adjust scope * emulate span to simplify loops * more readable loop * simplify loops with ith_order lambda * adjust * adjust * const --------- Co-authored-by: geneotech <patryk.czachurski@gmail.com> Co-authored-by: Patryk Czachurski <patryk.czachurski@softnet.pl>
1 parent 119eb11 commit 96f4404

File tree

4 files changed

+78
-31
lines changed

4 files changed

+78
-31
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/cmake-build-debug/
22
.DS_Store
33
.idea
4+
.cache/
45
*.iml
56
example/clang_commands.sh
67
example/main

example/main.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,15 @@ int main() {
118118
/*
119119
The example will compile just fine if you use any of these instead:
120120
121+
my_rect rectangles[] = { ... };
122+
121123
std::vector<rect_type> rectangles;
122124
std::list<rect_type> rectangles;
123125
124126
std::list<my_rect> rectangles;
125127
126-
1. The container just needs to be forward-iterable with .begin() and .end().
128+
1. The container needs to be forward-iterable with std::begin() and std::end(),
129+
and have its size queryable via std::size().
127130
2. The element type just needs to have get_rect() member functions defined (const and non-const).
128131
*/
129132

src/rectpack2D/best_bin_finder.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
#pragma once
22
#include <variant>
33
#include <cassert>
4+
#include <algorithm>
45
#include "rect_structs.h"
56

67
namespace rectpack2D {
8+
template <class T>
9+
class span {
10+
T first;
11+
T second;
12+
public:
13+
span(T first, T second) : first(first), second(second) {}
14+
15+
T begin() const {
16+
return first;
17+
}
18+
19+
T end() const {
20+
return second;
21+
}
22+
};
23+
724
enum class callback_result {
825
ABORT_PACKING,
926
CONTINUE_PACKING
@@ -216,7 +233,7 @@ namespace rectpack2D {
216233
rect_wh find_best_packing_impl(F for_each_order, const I input) {
217234
const auto max_bin = rect_wh(input.max_bin_side, input.max_bin_side);
218235

219-
OrderType* best_order = nullptr;
236+
std::optional<OrderType> best_order;
220237

221238
int best_total_inserted = -1;
222239
auto best_bin = max_bin;
@@ -229,7 +246,7 @@ namespace rectpack2D {
229246
thread_local empty_spaces_type root = rect_wh();
230247
root.flipping_mode = input.flipping_mode;
231248

232-
for_each_order ([&](OrderType& current_order) {
249+
for_each_order ([&](const OrderType& current_order) {
233250
const auto packing = best_packing_for_ordering(
234251
root,
235252
current_order,
@@ -242,24 +259,24 @@ namespace rectpack2D {
242259
Track which function inserts the most area in total,
243260
just in case that all orders will fail to fit into the largest allowed bin.
244261
*/
245-
if (best_order == nullptr) {
262+
if (!best_order.has_value()) {
246263
if (*total_inserted > best_total_inserted) {
247-
best_order = std::addressof(current_order);
264+
best_order = current_order;
248265
best_total_inserted = *total_inserted;
249266
}
250267
}
251268
}
252269
else if (const auto result_bin = std::get_if<rect_wh>(&packing)) {
253270
/* Save the function if it performed the best. */
254271
if (result_bin->area() <= best_bin.area()) {
255-
best_order = std::addressof(current_order);
272+
best_order = current_order;
256273
best_bin = *result_bin;
257274
}
258275
}
259276
});
260277

261278
{
262-
assert(best_order != nullptr);
279+
assert(best_order.has_value());
263280

264281
root.reset(best_bin);
265282

src/rectpack2D/finders_interface.h

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <vector>
44
#include <array>
55
#include <variant>
6+
#include <memory>
67
#include <algorithm>
78

89
#include "insert_and_split.h"
@@ -51,10 +52,12 @@ namespace rectpack2D {
5152
Subjects& subjects,
5253
const finder_input<F, G>& input
5354
) {
54-
using order_type = std::remove_reference_t<decltype(subjects)>;
55+
// Works with C arrays as well.
56+
using iterator_type = decltype(std::begin(subjects));
57+
using order_type = rectpack2D::span<iterator_type>;
5558

5659
return find_best_packing_impl<empty_spaces_type, order_type>(
57-
[&subjects](auto callback) { callback(subjects); },
60+
[&subjects](auto callback) { callback(order_type(std::begin(subjects), std::end(subjects))); },
5861
input
5962
);
6063
}
@@ -77,43 +80,66 @@ namespace rectpack2D {
7780
Comparators... comparators
7881
) {
7982
using rect_type = output_rect_t<empty_spaces_type>;
80-
using order_type = std::vector<rect_type*>;
83+
using order_type = rectpack2D::span<rect_type**>;
8184

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

85-
{
86-
/* order[0] will always exist since this overload requires at least one comparator */
87-
auto& initial_pointers = orders[0];
88-
initial_pointers.clear();
88+
// Allocate space assuming no rectangle has an area of zero.
89+
// We fill orders with valid rectangles only.
90+
auto orders = std::make_unique<rect_type*[]>(count_orders * std::size(subjects));
8991

90-
for (auto& s : subjects) {
91-
auto& r = s.get_rect();
92+
for (auto& s : subjects) {
93+
auto& r = s.get_rect();
9294

93-
if (r.area() > 0) {
94-
initial_pointers.emplace_back(std::addressof(r));
95-
}
95+
if (r.area() == 0) {
96+
continue;
9697
}
9798

99+
orders[count_valid_subjects++] = std::addressof(r);
100+
}
101+
102+
auto ith_order = [&orders, n = count_valid_subjects](const std::size_t i) {
103+
return order_type(
104+
orders.get() + i * n,
105+
orders.get() + (i + 1) * n
106+
);
107+
};
108+
109+
{
110+
/*
111+
Zero-th order is already filled.
112+
We duplicate it to all other orders.
113+
*/
114+
const auto first_order = ith_order(0);
115+
98116
for (std::size_t i = 1; i < count_orders; ++i) {
99-
orders[i] = initial_pointers;
117+
std::copy(
118+
first_order.begin(),
119+
first_order.end(),
120+
ith_order(i).begin()
121+
);
100122
}
101123
}
102124

103-
std::size_t f = 0;
104-
105-
auto& orders_ref = orders;
125+
{
126+
std::size_t i = 0;
106127

107-
auto make_order = [&f, &orders_ref](auto& predicate) {
108-
std::sort(orders_ref[f].begin(), orders_ref[f].end(), predicate);
109-
++f;
110-
};
128+
auto make_order = [&i, ith_order](auto& predicate) {
129+
const auto o = ith_order(i++);
130+
std::sort(o.begin(), o.end(), predicate);
131+
};
111132

112-
make_order(comparator);
113-
(make_order(comparators), ...);
133+
make_order(comparator);
134+
(make_order(comparators), ...);
135+
}
114136

115137
return find_best_packing_impl<empty_spaces_type, order_type>(
116-
[&orders_ref](auto callback){ for (auto& o : orders_ref) { callback(o); } },
138+
[ith_order](auto callback) {
139+
for (std::size_t i = 0; i < count_orders; ++i) {
140+
callback(ith_order(i));
141+
}
142+
},
117143
input
118144
);
119145
}

0 commit comments

Comments
 (0)