15#include <lagrange/utils/warnoff.h>
16#include <RectangleBinPack/GuillotineBinPack.h>
17#include <RectangleBinPack/Rect.h>
18#include <lagrange/utils/warnon.h>
21#include <lagrange/Logger.h>
22#include <lagrange/utils/assert.h>
23#include <lagrange/utils/range.h>
24#include <lagrange/utils/safe_cast.h>
35namespace lagrange::packing {
38bool product_will_overflow(T a, T b)
40 static_assert(std::is_integral<T>::value,
"T must be an integral type");
42 if constexpr (std::is_signed<T>::value) {
43 using Limits = std::numeric_limits<T>;
47 return a > Limits::max() / b;
49 return b < Limits::min() / a;
53 return a < Limits::min() / b;
55 return a < Limits::max() / b;
61 if (b == 0)
return false;
62 return a > std::numeric_limits<T>::max() / b;
66class PackingFailure :
public std::runtime_error
69 PackingFailure(
const std::string& what)
70 : std::runtime_error(what)
87template <
typename Scalar>
88std::tuple<Eigen::Matrix<Scalar, Eigen::Dynamic, 2, Eigen::RowMajor>, std::vector<bool>,
Scalar>
90 const Eigen::Matrix<Scalar, Eigen::Dynamic, 2, Eigen::RowMajor>& bbox_mins,
91 const Eigen::Matrix<Scalar, Eigen::Dynamic, 2, Eigen::RowMajor>& bbox_maxs,
95#ifdef RECTANGLE_BIN_PACK_OSS
98 using Int = ::rbp::Int;
104 const auto num_boxes = bbox_mins.rows();
105 if (num_boxes == 0) {
106 return std::make_tuple(
107 Eigen::Matrix<Scalar, Eigen::Dynamic, 2, Eigen::RowMajor>(0, 2),
112 const Scalar max_box_length = (bbox_maxs - bbox_mins).maxCoeff();
113 constexpr Int MAX_AREA = std::numeric_limits<Int>::max();
114 constexpr Int RESOLUTION = 1 << 12;
115 Int max_canvas_size = RESOLUTION;
116 Int min_canvas_size = RESOLUTION;
121 max_box_length >
safe_cast<Scalar>(1e-12) ? max_box_length / max_canvas_size : 1;
123 logger().trace(
"Scale: {}", scale);
126 std::vector<::rbp::RectSize> boxes;
127 boxes.reserve(num_boxes);
128 for (
auto i :
range(num_boxes)) {
129 boxes.emplace_back();
130 auto& box = boxes.back();
131 if (bbox_maxs(i, 0) < bbox_mins(i, 0) || bbox_maxs(i, 1) < bbox_mins(i, 1)) {
133 logger().warn(
"Skipping invalid bounding box (index {})!", i);
137 box.width =
safe_cast<Int>(std::ceil((bbox_maxs(i, 0) - bbox_mins(i, 0)) / scale));
138 box.height =
safe_cast<Int>(std::ceil((bbox_maxs(i, 1) - bbox_mins(i, 1)) / scale));
143 Eigen::Matrix<Scalar, Eigen::Dynamic, 2, Eigen::RowMajor> centers(num_boxes, 2);
144 std::vector<bool> rotated(num_boxes);
146 auto pack = [&](
int L,
bool trial) ->
bool {
148#ifdef RECTANGLE_BIN_PACK_OSS
149 if (!allow_rotation) {
151 "Disabling rotation is not supported with this version of RectangleBinPack!");
153 rbp::GuillotineBinPack packer(L, L);
155 rbp::GuillotineBinPack packer(L, L, allow_rotation);
157 Int int_margin = std::max<Int>(2,
static_cast<Int
>(std::ceil(margin * L)));
158 for (
auto i :
range(num_boxes)) {
159 auto rect = packer.Insert(
160 boxes[i].width + int_margin,
161 boxes[i].height + int_margin,
163 rbp::GuillotineBinPack::FreeRectChoiceHeuristic::RectBestAreaFit,
164 rbp::GuillotineBinPack::GuillotineSplitHeuristic::SplitMinimizeArea);
165 if (rect.width == 0 || rect.height == 0) {
169 const auto& packed_rect = packer.GetUsedRectangles();
170 logger().debug(
"num packed rectangles {}, expecting {}", packed_rect.size(), num_boxes);
171 if (
static_cast<decltype(num_boxes)
>(packed_rect.size()) != num_boxes)
return false;
174 for (
auto i :
range(num_boxes)) {
175 const auto& r = packed_rect[i];
176 rotated[i] = r.width != boxes[i].width + int_margin;
178 centers(i, 0) = (r.x + r.width * 0.5f) * scale;
179 centers(i, 1) = (r.y + r.height * 0.5f) * scale;
185 logger().trace(
"Minimum canvas size: {}", min_canvas_size);
186 logger().trace(
"Maximum canvas size: {}", max_canvas_size);
188 while (!pack(max_canvas_size,
true)) {
189 min_canvas_size = max_canvas_size;
190 max_canvas_size *= 2;
191 if (MAX_AREA / max_canvas_size <= max_canvas_size) {
193 throw PackingFailure(
"Cannot pack even with canvas at max area!");
196 logger().trace(
"Minimum canvas size: {}", min_canvas_size);
197 logger().trace(
"Maximum canvas size: {}", max_canvas_size);
200 while (min_canvas_size < max_canvas_size) {
201 const auto l = (min_canvas_size + max_canvas_size) / 2;
202 if (l == min_canvas_size) {
212 bool r = pack(max_canvas_size,
false);
214 logger().trace(
"Minimum canvas size: {}", min_canvas_size);
215 logger().trace(
"Maximum canvas size: {}", max_canvas_size);
217 return std::make_tuple(centers, rotated,
static_cast<Scalar>(max_canvas_size) * scale);
Definition pack_boxes.h:67
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
#define la_debug_assert(...)
Debug assertion check.
Definition assert.h:194
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:50