25#include "absl/algorithm/container.h"
26#include "absl/base/log_severity.h"
27#include "absl/container/btree_map.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/log/check.h"
31#include "absl/log/log.h"
32#include "absl/log/vlog_is_on.h"
33#include "absl/strings/str_cat.h"
34#include "absl/types/span.h"
45std::vector<Rectangle> FindSpacesThatCannotBeOccupied(
46 absl::Span<const Rectangle> fixed_boxes,
47 absl::Span<const RectangleInRange> non_fixed_boxes,
48 const Rectangle& bounding_box, IntegerValue min_x_size,
49 IntegerValue min_y_size) {
50 std::vector<Rectangle> optional_boxes = {fixed_boxes.begin(),
53 if (bounding_box.x_min > std::numeric_limits<IntegerValue>::min() &&
54 bounding_box.y_min > std::numeric_limits<IntegerValue>::min() &&
55 bounding_box.x_max < std::numeric_limits<IntegerValue>::max() &&
56 bounding_box.y_max < std::numeric_limits<IntegerValue>::max()) {
64 optional_boxes.push_back({.x_min = bounding_box.x_min - 1,
65 .x_max = bounding_box.x_max,
66 .y_min = bounding_box.y_min - 1,
67 .y_max = bounding_box.y_min});
68 optional_boxes.push_back({.x_min = bounding_box.x_max,
69 .x_max = bounding_box.x_max + 1,
70 .y_min = bounding_box.y_min - 1,
71 .y_max = bounding_box.y_max});
72 optional_boxes.push_back({.x_min = bounding_box.x_min,
73 .x_max = bounding_box.x_max + 1,
74 .y_min = bounding_box.y_max,
75 .y_max = bounding_box.y_max + 1});
76 optional_boxes.push_back({.x_min = bounding_box.x_min - 1,
77 .x_max = bounding_box.x_min,
78 .y_min = bounding_box.y_min,
79 .y_max = bounding_box.y_max + 1});
85 const int num_optional_boxes_to_remove = optional_boxes.size();
89 const auto add_box = [&optional_boxes](
Rectangle new_box) {
90 std::vector<Rectangle> to_add = {std::move(new_box)};
91 for (
int i = 0;
i < to_add.size(); ++
i) {
93 bool is_disjoint =
true;
94 for (
const Rectangle& existing_box : optional_boxes) {
95 if (!new_box.IsDisjoint(existing_box)) {
99 to_add.push_back(disjoint_box);
105 optional_boxes.push_back(std::move(new_box));
114 if (non_fixed_boxes.size() < 1000) {
115 std::vector<Rectangle> bounding_boxes;
116 bounding_boxes.reserve(non_fixed_boxes.size());
118 bounding_boxes.push_back(box.bounding_area);
120 std::vector<Rectangle> empty_spaces =
122 for (
const Rectangle& r : empty_spaces) {
128 for (
int i = 1;
i < optional_boxes.size(); ++
i) {
130 for (
int j = 0; j <
i; ++j) {
131 const Rectangle& other_box = optional_boxes[j];
132 const IntegerValue lower_top = std::min(cur_box.y_max, other_box.y_max);
133 const IntegerValue higher_bottom =
134 std::max(other_box.y_min, cur_box.y_min);
135 const IntegerValue rightmost_left_edge =
136 std::max(other_box.x_min, cur_box.x_min);
137 const IntegerValue leftmost_right_edge =
138 std::min(other_box.x_max, cur_box.x_max);
139 if (rightmost_left_edge < leftmost_right_edge) {
140 if (lower_top < higher_bottom &&
141 higher_bottom - lower_top < min_y_size) {
142 add_box({.x_min = rightmost_left_edge,
143 .x_max = leftmost_right_edge,
145 .y_max = higher_bottom});
148 if (higher_bottom < lower_top) {
149 if (leftmost_right_edge < rightmost_left_edge &&
150 rightmost_left_edge - leftmost_right_edge < min_x_size) {
151 add_box({.x_min = leftmost_right_edge,
152 .x_max = rightmost_left_edge,
153 .y_min = higher_bottom,
154 .y_max = lower_top});
159 optional_boxes.erase(optional_boxes.begin(),
160 optional_boxes.begin() + num_optional_boxes_to_remove);
161 return optional_boxes;
167 absl::Span<const RectangleInRange> non_fixed_boxes,
168 std::vector<Rectangle>* fixed_boxes) {
172 bool changed =
false;
175 IntegerValue original_area = 0;
176 std::vector<Rectangle> fixed_boxes_copy;
178 for (
const Rectangle& r : *fixed_boxes) {
179 original_area += r.Area();
183 fixed_boxes_copy = *fixed_boxes;
186 const int original_num_boxes = fixed_boxes->size();
190 std::vector<Rectangle> empty_vec;
195 IntegerValue min_x_size = std::numeric_limits<IntegerValue>::max();
196 IntegerValue min_y_size = std::numeric_limits<IntegerValue>::max();
198 CHECK(!non_fixed_boxes.empty());
199 Rectangle bounding_box = non_fixed_boxes[0].bounding_area;
203 min_x_size = std::min(min_x_size, box.x_size);
204 min_y_size = std::min(min_y_size, box.y_size);
206 DCHECK_GT(min_x_size, 0);
207 DCHECK_GT(min_y_size, 0);
213 while (new_size < fixed_boxes->size()) {
214 Rectangle& rectangle = (*fixed_boxes)[new_size];
215 DCHECK_GT(rectangle.
SizeX(), 0);
216 DCHECK_GT(rectangle.
SizeY(), 0);
233 if (rectangle.
SizeX() <= 0 || rectangle.
SizeY() <= 0) {
235 std::swap(rectangle, (*fixed_boxes)[fixed_boxes->size() - 1]);
236 fixed_boxes->resize(fixed_boxes->size() - 1);
244 std::vector<Rectangle> optional_boxes = FindSpacesThatCannotBeOccupied(
245 *fixed_boxes, non_fixed_boxes, bounding_box, min_x_size, min_y_size);
269 const int num_after_first_pass = fixed_boxes->size();
273 if (changed && VLOG_IS_ON(1)) {
274 IntegerValue area = 0;
275 for (
const Rectangle& r : *fixed_boxes) {
278 VLOG_EVERY_N_SEC(1, 1) <<
"Presolved " << original_num_boxes
279 <<
" fixed rectangles (area=" << original_area
280 <<
") into " << num_after_first_pass <<
" then "
281 << fixed_boxes->size() <<
" (area=" << area <<
")";
283 VLOG_EVERY_N_SEC(2, 2) <<
"Presolved rectangles:\n"
284 <<
RenderDot(bounding_box, fixed_boxes_copy)
287 << (optional_boxes.empty()
289 : absl::StrCat(
"Unused optional rects:\n",
298 IntegerValue x_start;
299 IntegerValue y_start;
302 static Edge GetEdge(
const Rectangle& rectangle,
EdgePosition pos) {
305 return {.x_start = rectangle.x_min,
306 .y_start = rectangle.y_max,
307 .size = rectangle.SizeX()};
309 return {.x_start = rectangle.x_min,
310 .y_start = rectangle.y_min,
311 .size = rectangle.SizeX()};
313 return {.x_start = rectangle.x_min,
314 .y_start = rectangle.y_min,
315 .size = rectangle.SizeY()};
317 return {.x_start = rectangle.x_max,
318 .y_start = rectangle.y_min,
319 .size = rectangle.SizeY()};
323 template <
typename H>
325 return H::combine(std::move(h), e.x_start, e.y_start, e.size);
329 return x_start == other.x_start && y_start == other.y_start &&
333 static bool CompareXThenY(
const Edge& a,
const Edge&
b) {
334 return std::tie(a.x_start, a.y_start, a.size) <
335 std::tie(
b.x_start,
b.y_start,
b.size);
337 static bool CompareYThenX(
const Edge& a,
const Edge&
b) {
338 return std::tie(a.y_start, a.x_start, a.size) <
339 std::tie(
b.y_start,
b.x_start,
b.size);
345 std::vector<Rectangle>* optional_rectangles) {
348 std::vector<std::unique_ptr<Rectangle>> rectangle_storage;
349 enum class OptionalEnum { OPTIONAL, MANDATORY };
351 std::vector<std::pair<const Rectangle*, OptionalEnum>> rectangles_ptr;
352 absl::flat_hash_map<Edge, int> top_edges_to_rectangle;
353 absl::flat_hash_map<Edge, int> bottom_edges_to_rectangle;
354 absl::flat_hash_map<Edge, int> left_edges_to_rectangle;
355 absl::flat_hash_map<Edge, int> right_edges_to_rectangle;
357 bool changed_optional =
false;
358 bool changed_mandatory =
false;
360 auto add_rectangle = [&](
const Rectangle* rectangle_ptr,
362 const int index = rectangles_ptr.size();
363 rectangles_ptr.push_back({rectangle_ptr,
optional});
364 const Rectangle& rectangle = *rectangles_ptr[index].first;
373 for (
const Rectangle& rectangle : *mandatory_rectangles) {
374 add_rectangle(&rectangle, OptionalEnum::MANDATORY);
376 for (
const Rectangle& rectangle : *optional_rectangles) {
377 add_rectangle(&rectangle, OptionalEnum::OPTIONAL);
380 auto remove_rectangle = [&](
const int index) {
381 const Rectangle& rectangle = *rectangles_ptr[index].first;
386 top_edges_to_rectangle.erase(top_edge);
387 bottom_edges_to_rectangle.erase(bottom_edge);
388 left_edges_to_rectangle.erase(left_edge);
389 right_edges_to_rectangle.erase(right_edge);
390 rectangles_ptr[index].first =
nullptr;
393 bool iteration_did_merge =
true;
394 while (iteration_did_merge) {
395 iteration_did_merge =
false;
396 for (
int i = 0;
i < rectangles_ptr.size(); ++
i) {
397 if (!rectangles_ptr[
i].first) {
400 const Rectangle& rectangle = *rectangles_ptr[
i].first;
408 if (
const auto it = right_edges_to_rectangle.find(left_edge);
409 it != right_edges_to_rectangle.end()) {
411 }
else if (
const auto it = left_edges_to_rectangle.find(right_edge);
412 it != left_edges_to_rectangle.end()) {
414 }
else if (
const auto it = bottom_edges_to_rectangle.find(top_edge);
415 it != bottom_edges_to_rectangle.end()) {
417 }
else if (
const auto it = top_edges_to_rectangle.find(bottom_edge);
418 it != top_edges_to_rectangle.end()) {
424 iteration_did_merge =
true;
427 const OptionalEnum new_optional =
428 (rectangles_ptr[
i].second == OptionalEnum::MANDATORY ||
429 rectangles_ptr[index].second == OptionalEnum::MANDATORY)
430 ? OptionalEnum::MANDATORY
431 : OptionalEnum::OPTIONAL;
433 changed_mandatory || (new_optional == OptionalEnum::MANDATORY);
436 (rectangles_ptr[
i].second == OptionalEnum::OPTIONAL ||
437 rectangles_ptr[index].second == OptionalEnum::OPTIONAL);
438 rectangle_storage.push_back(std::make_unique<Rectangle>(rectangle));
439 Rectangle& new_rectangle = *rectangle_storage.back();
442 remove_rectangle(index);
443 add_rectangle(&new_rectangle, new_optional);
447 if (changed_mandatory) {
448 std::vector<Rectangle> new_rectangles;
449 for (
auto [rectangle,
optional] : rectangles_ptr) {
450 if (rectangle &&
optional == OptionalEnum::MANDATORY) {
451 new_rectangles.push_back(*rectangle);
454 *mandatory_rectangles = std::move(new_rectangles);
456 if (changed_optional) {
457 std::vector<Rectangle> new_rectangles;
458 for (
auto [rectangle,
optional] : rectangles_ptr) {
459 if (rectangle &&
optional == OptionalEnum::OPTIONAL) {
460 new_rectangles.push_back(*rectangle);
463 *optional_rectangles = std::move(new_rectangles);
465 return changed_mandatory;
473 std::vector<std::pair<Edge, int>> edges_to_rectangle[4];
474 std::vector<std::tuple<int, EdgePosition, int>> neighbours;
475 neighbours.reserve(2 * rectangles.size());
476 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
478 edges_to_rectangle[edge_position].reserve(rectangles.size());
481 for (
int i = 0;
i < rectangles.size(); ++
i) {
483 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
485 const Edge edge = Edge::GetEdge(rectangle, edge_position);
486 edges_to_rectangle[edge_position].push_back({edge,
i});
489 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
495 ? [](
const std::pair<Edge, int>& a,
496 const std::pair<Edge, int>&
497 b) {
return Edge::CompareXThenY(a.first,
b.first); }
498 : [](
const std::pair<Edge, int>& a,
const std::pair<Edge, int>&
b) {
499 return Edge::CompareYThenX(a.first,
b.first);
501 absl::c_sort(edges_to_rectangle[edge_position], cmp);
504 constexpr struct EdgeData {
507 bool (*cmp)(
const Edge&,
const Edge&);
510 .cmp = &Edge::CompareYThenX},
513 .cmp = &Edge::CompareYThenX},
516 .cmp = &Edge::CompareXThenY},
519 .cmp = &Edge::CompareXThenY}};
521 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
522 const EdgePosition edge_position = edge_data[edge_int].edge;
524 edge_data[edge_int].opposite_edge;
525 auto it = edges_to_rectangle[edge_position].begin();
526 for (
const auto& [edge, index] :
527 edges_to_rectangle[opposite_edge_position]) {
528 while (it != edges_to_rectangle[edge_position].
end() &&
529 edge_data[edge_int].cmp(it->first, edge)) {
532 if (it == edges_to_rectangle[edge_position].
end()) {
537 while (it != edges_to_rectangle[edge_position].
end() &&
538 it->first.y_start == edge.y_start &&
539 it->first.x_start < edge.x_start + edge.size) {
540 neighbours.push_back({index, opposite_edge_position, it->second});
541 neighbours.push_back({it->second, edge_position, index});
545 while (it != edges_to_rectangle[edge_position].
end() &&
546 it->first.x_start == edge.x_start &&
547 it->first.y_start < edge.y_start + edge.size) {
548 neighbours.push_back({index, opposite_edge_position, it->second});
549 neighbours.push_back({it->second, edge_position, index});
564 explicit GraphView(
const Neighbours& neighbours)
565 : neighbours_(neighbours) {}
566 absl::Span<const int> operator[](
int node)
const {
568 for (
int edge = 0; edge < 4; ++edge) {
569 const auto edge_neighbors = neighbours_.GetSortedNeighbors(
571 for (
int neighbor : edge_neighbors) {
572 temp_.push_back(neighbor);
580 mutable std::vector<int> temp_;
583 std::vector<std::vector<int>> components;
585 GraphView(neighbours), &components);
590IntegerValue GetClockwiseStart(
EdgePosition edge,
const Rectangle& rectangle) {
593 return rectangle.y_min;
595 return rectangle.y_max;
597 return rectangle.x_max;
599 return rectangle.x_min;
606 return rectangle.y_max;
608 return rectangle.y_min;
610 return rectangle.x_min;
612 return rectangle.x_max;
629void GetAllSegmentsTouchingVoid(
630 absl::Span<const Rectangle> rectangles,
const Neighbours& neighbours,
631 std::vector<std::pair<Edge, int>>& vertical_edges_on_boundary,
632 std::vector<std::pair<Edge, int>>& horizontal_edges_on_boundary) {
633 for (
int i = 0;
i < rectangles.size(); ++
i) {
635 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
637 const auto box_neighbors = neighbours.GetSortedNeighbors(
i, edge);
638 if (box_neighbors.empty()) {
640 vertical_edges_on_boundary.push_back(
641 {Edge::GetEdge(rectangle, edge),
i});
643 horizontal_edges_on_boundary.push_back(
644 {Edge::GetEdge(rectangle, edge),
i});
648 IntegerValue previous_pos = GetClockwiseStart(edge, rectangle);
649 for (
int n = 0; n <= box_neighbors.size(); ++n) {
650 IntegerValue neighbor_start;
652 if (n == box_neighbors.size()) {
655 neighbor_start = GetClockwiseEnd(edge, rectangle);
657 const int neighbor_idx = box_neighbors[n];
658 neighbor = &rectangles[neighbor_idx];
659 neighbor_start = GetClockwiseStart(edge, *neighbor);
663 if (neighbor_start > previous_pos) {
664 vertical_edges_on_boundary.push_back(
665 {Edge{.x_start = rectangle.x_min,
666 .y_start = previous_pos,
667 .size = neighbor_start - previous_pos},
672 if (neighbor_start < previous_pos) {
673 vertical_edges_on_boundary.push_back(
674 {Edge{.x_start = rectangle.x_max,
675 .y_start = neighbor_start,
676 .size = previous_pos - neighbor_start},
681 if (neighbor_start < previous_pos) {
682 horizontal_edges_on_boundary.push_back(
683 {Edge{.x_start = neighbor_start,
684 .y_start = rectangle.y_min,
685 .size = previous_pos - neighbor_start},
690 if (neighbor_start > previous_pos) {
691 horizontal_edges_on_boundary.push_back(
692 {Edge{.x_start = previous_pos,
693 .y_start = rectangle.y_max,
694 .size = neighbor_start - previous_pos},
699 if (n != box_neighbors.size()) {
700 previous_pos = GetClockwiseEnd(edge, *neighbor);
712 std::pair<IntegerValue, IntegerValue> starting_step_point,
713 std::array<absl::btree_map<std::pair<IntegerValue, IntegerValue>,
714 std::pair<IntegerValue, int>>,
715 4>& segments_to_follow) {
721 segments_to_follow[starting_edge_position].extract(starting_step_point);
722 CHECK(!extracted.empty());
723 const int first_index = extracted.mapped().second;
725 std::pair<IntegerValue, IntegerValue> cur = starting_step_point;
726 int cur_index = first_index;
730 path.step_points.push_back(cur);
732 bool can_go[4] = {
false,
false,
false,
false};
734 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
736 if (segments_to_follow[edge].contains(cur)) {
738 direction_to_take = edge;
742 if (can_go == absl::Span<const bool>{
false,
false,
false,
false}) {
761 if (cur_index == it_x->second.second) {
768 if (cur_index == it_y->second.second) {
775 auto extracted = segments_to_follow[direction_to_take].extract(cur);
776 cur_index = extracted.mapped().second;
777 switch (direction_to_take) {
779 cur.first -= extracted.mapped().first;
784 cur.first += extracted.mapped().first;
788 cur.second += extracted.mapped().first;
793 cur.second -= extracted.mapped().first;
797 path.touching_box_index.push_back(cur_index);
799 path.touching_box_index.push_back(cur_index);
805std::vector<SingleShape>
BoxesToShapes(absl::Span<const Rectangle> rectangles,
807 std::vector<std::pair<Edge, int>> vertical_edges_on_boundary;
808 std::vector<std::pair<Edge, int>> horizontal_edges_on_boundary;
809 GetAllSegmentsTouchingVoid(rectangles, neighbours, vertical_edges_on_boundary,
810 horizontal_edges_on_boundary);
812 std::array<absl::btree_map<std::pair<IntegerValue, IntegerValue>,
813 std::pair<IntegerValue, int>>,
817 for (
const auto& [edge, box_index] : vertical_edges_on_boundary) {
819 edge.size, box_index};
821 edge.x_start, edge.y_start + edge.size}] = {edge.size, box_index};
823 for (
const auto& [edge, box_index] : horizontal_edges_on_boundary) {
825 edge.size, box_index};
827 edge.x_start + edge.size, edge.y_start}] = {edge.size, box_index};
831 std::vector<SingleShape> result(components.size());
832 std::vector<int> box_to_component(rectangles.size());
833 for (
int i = 0;
i < components.size(); ++
i) {
834 for (
const int box_index : components[
i]) {
835 box_to_component[box_index] =
i;
840 const int box_index =
842 const std::pair<IntegerValue, IntegerValue> starting_step_point =
844 const int component_index = box_to_component[box_index];
849 const bool is_hole = !result[component_index].boundary.step_points.empty();
850 ShapePath& path = is_hole ? result[component_index].holes.emplace_back()
851 : result[component_index].boundary;
866 std::pair<IntegerValue, IntegerValue> start;
867 std::pair<IntegerValue, IntegerValue>
end;
872 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
873 return std::tie(a.start.second, a.start.first) <
874 std::tie(
b.start.second,
b.start.first);
879 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
880 return std::tie(a.end.second, a.end.first) <
881 std::tie(
b.end.second,
b.end.first);
886 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
887 return a.start <
b.start;
892 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
893 return a.end <
b.end;
897 template <
typename Sink>
898 friend void AbslStringify(Sink& sink,
const PolygonCut& diagonal) {
899 absl::Format(&sink,
"(%v,%v)-(%v,%v)", diagonal.start.first,
900 diagonal.start.second, diagonal.end.first,
901 diagonal.end.second);
912 std::vector<std::pair<IntegerValue, IntegerValue>> points;
913 std::vector<int> next;
917 const std::pair<IntegerValue, IntegerValue>& curr_segment,
918 const std::pair<IntegerValue, IntegerValue>& next_segment) {
919 if (curr_segment.first == next_segment.first) {
937std::array<std::vector<PolygonCut>, 4> GetPotentialPolygonCuts(
939 std::array<std::vector<PolygonCut>, 4> cuts;
943 for (
int i = 0;
i < shape.points.size();
i++) {
944 const auto& it = &shape.points[shape.next[
i]];
945 const auto& previous = &shape.points[
i];
946 const auto& next_segment = &shape.points[shape.next[shape.next[
i]]];
947 const EdgePosition previous_dir = GetSegmentDirection(*previous, *it);
948 const EdgePosition next_dir = GetSegmentDirection(*it, *next_segment);
955 .end = {std::numeric_limits<IntegerValue>::max(), it->second},
956 .start_index = shape.next[
i]});
963 {.start = {std::numeric_limits<IntegerValue>::min(), it->second},
965 .end_index = shape.next[
i]});
972 {.start = {it->first, std::numeric_limits<IntegerValue>::min()},
974 .end_index = shape.next[
i]});
981 .end = {it->first, std::numeric_limits<IntegerValue>::max()},
982 .start_index = shape.next[
i]});
997 PolygonCut::CmpByStartY());
999 PolygonCut::CmpByEndY());
1003 PolygonCut::CmpByStartX());
1008 const auto cut_segment_if_necessary =
1009 [&shape](
int segment_idx,
1010 std::pair<IntegerValue, IntegerValue> point_to_cut) {
1011 const auto& cur = shape.points[segment_idx];
1012 const auto& next = shape.points[shape.next[segment_idx]];
1013 if (cur.second == next.second) {
1014 DCHECK_EQ(point_to_cut.second, cur.second);
1016 const IntegerValue edge_start = std::min(cur.first, next.first);
1017 const IntegerValue edge_end = std::max(cur.first, next.first);
1019 if (edge_start < point_to_cut.first &&
1020 point_to_cut.first < edge_end) {
1021 shape.points.push_back(point_to_cut);
1022 const int next_idx = shape.next[segment_idx];
1023 shape.next[segment_idx] = shape.points.size() - 1;
1024 shape.next.push_back(next_idx);
1025 return static_cast<int>(shape.points.size() - 1);
1027 return (shape.points[segment_idx] == point_to_cut)
1029 : shape.next[segment_idx];
1031 DCHECK_EQ(cur.first, next.first);
1032 DCHECK_EQ(point_to_cut.first, cur.first);
1034 const IntegerValue edge_start = std::min(cur.second, next.second);
1035 const IntegerValue edge_end = std::max(cur.second, next.second);
1037 if (edge_start < point_to_cut.second &&
1038 point_to_cut.second < edge_end) {
1039 shape.points.push_back(point_to_cut);
1040 const int next_idx = shape.next[segment_idx];
1041 shape.next[segment_idx] = shape.points.size() - 1;
1042 shape.next.push_back(next_idx);
1043 return static_cast<int>(shape.points.size() - 1);
1045 return (shape.points[segment_idx] == point_to_cut)
1047 : shape.next[segment_idx];
1051 for (
int i = 0;
i < shape.points.size();
i++) {
1052 const auto* cur_point_ptr = &shape.points[shape.next[
i]];
1053 const auto* previous = &shape.points[
i];
1054 DCHECK(cur_point_ptr->first == previous->first ||
1055 cur_point_ptr->second == previous->second)
1056 <<
"found a segment that is neither horizontal nor vertical";
1058 GetSegmentDirection(*previous, *cur_point_ptr);
1061 const auto cut_start = absl::c_lower_bound(
1063 PolygonCut{.start = {std::numeric_limits<IntegerValue>::min(),
1064 cur_point_ptr->second}},
1065 PolygonCut::CmpByStartY());
1066 auto cut_end = absl::c_upper_bound(
1068 PolygonCut{.start = {std::numeric_limits<IntegerValue>::max(),
1070 PolygonCut::CmpByStartY());
1072 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1073 PolygonCut& diagonal = *cut_it;
1074 const IntegerValue diagonal_start_x = diagonal.start.first;
1075 const IntegerValue diagonal_cur_end_x = diagonal.end.first;
1077 DCHECK_LE(cur_point_ptr->second, diagonal.start.second);
1078 DCHECK_LE(diagonal.start.second, previous->second);
1081 if (diagonal_start_x <= previous->first &&
1082 diagonal_cur_end_x > cur_point_ptr->first) {
1083 DCHECK_LT(diagonal_start_x, cur_point_ptr->first);
1084 DCHECK_LE(previous->first, diagonal_cur_end_x);
1086 diagonal.end.first = cur_point_ptr->first;
1088 diagonal.end_index = cut_segment_if_necessary(
i, diagonal.end);
1089 DCHECK(shape.points[diagonal.end_index] == diagonal.end);
1095 cur_point_ptr = &shape.points[shape.next[
i]];
1096 previous = &shape.points[
i];
1097 cut_end = absl::c_upper_bound(
1099 PolygonCut{.start = {std::numeric_limits<IntegerValue>::max(),
1101 PolygonCut::CmpByStartY());
1107 const auto cut_start = absl::c_lower_bound(
1109 PolygonCut{.end = {std::numeric_limits<IntegerValue>::min(),
1111 PolygonCut::CmpByEndY());
1112 auto cut_end = absl::c_upper_bound(
1114 PolygonCut{.end = {std::numeric_limits<IntegerValue>::max(),
1115 cur_point_ptr->second}},
1116 PolygonCut::CmpByEndY());
1117 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1118 PolygonCut& diagonal = *cut_it;
1119 const IntegerValue diagonal_start_x = diagonal.start.first;
1120 const IntegerValue diagonal_cur_end_x = diagonal.end.first;
1122 DCHECK_LE(diagonal.end.second, cur_point_ptr->second);
1123 DCHECK_LE(previous->second, diagonal.end.second);
1126 if (diagonal_start_x < cur_point_ptr->first &&
1127 previous->first <= diagonal_cur_end_x) {
1128 DCHECK_LT(cur_point_ptr->first, diagonal_cur_end_x);
1129 DCHECK_LE(diagonal_start_x, previous->first);
1131 diagonal.start.first = cur_point_ptr->first;
1132 diagonal.start_index = cut_segment_if_necessary(
i, diagonal.start);
1133 DCHECK(shape.points[diagonal.start_index] == diagonal.start);
1134 cur_point_ptr = &shape.points[shape.next[
i]];
1135 previous = &shape.points[
i];
1136 cut_end = absl::c_upper_bound(
1138 PolygonCut{.end = {std::numeric_limits<IntegerValue>::max(),
1139 cur_point_ptr->second}},
1140 PolygonCut::CmpByEndY());
1146 const auto cut_start = absl::c_lower_bound(
1148 PolygonCut{.end = {cur_point_ptr->first,
1149 std::numeric_limits<IntegerValue>::min()}},
1150 PolygonCut::CmpByEndX());
1151 auto cut_end = absl::c_upper_bound(
1153 PolygonCut{.end = {previous->first,
1154 std::numeric_limits<IntegerValue>::max()}},
1155 PolygonCut::CmpByEndX());
1156 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1157 PolygonCut& diagonal = *cut_it;
1158 const IntegerValue diagonal_start_y = diagonal.start.second;
1159 const IntegerValue diagonal_cur_end_y = diagonal.end.second;
1162 DCHECK_LE(cur_point_ptr->first, diagonal.end.first);
1163 DCHECK_LE(diagonal.end.first, previous->first);
1166 if (diagonal_start_y < cur_point_ptr->second &&
1167 cur_point_ptr->second <= diagonal_cur_end_y) {
1168 DCHECK_LE(diagonal_start_y, previous->second);
1169 DCHECK_LT(cur_point_ptr->second, diagonal_cur_end_y);
1171 diagonal.start.second = cur_point_ptr->second;
1172 diagonal.start_index = cut_segment_if_necessary(
i, diagonal.start);
1173 DCHECK(shape.points[diagonal.start_index] == diagonal.start);
1174 cur_point_ptr = &shape.points[shape.next[
i]];
1175 previous = &shape.points[
i];
1176 cut_end = absl::c_upper_bound(
1178 PolygonCut{.end = {previous->first,
1179 std::numeric_limits<IntegerValue>::max()}},
1180 PolygonCut::CmpByEndX());
1186 const auto cut_start = absl::c_lower_bound(
1188 PolygonCut{.start = {previous->first,
1189 std::numeric_limits<IntegerValue>::min()}},
1190 PolygonCut::CmpByStartX());
1191 auto cut_end = absl::c_upper_bound(
1193 PolygonCut{.start = {cur_point_ptr->first,
1194 std::numeric_limits<IntegerValue>::max()}},
1195 PolygonCut::CmpByStartX());
1196 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1197 PolygonCut& diagonal = *cut_it;
1198 const IntegerValue diagonal_start_y = diagonal.start.second;
1199 const IntegerValue diagonal_cur_end_y = diagonal.end.second;
1202 DCHECK_LE(previous->first, diagonal.start.first);
1203 DCHECK_LE(diagonal.start.first, cur_point_ptr->first);
1206 if (diagonal_start_y <= cur_point_ptr->second &&
1207 cur_point_ptr->second < diagonal_cur_end_y) {
1208 DCHECK_LT(diagonal_start_y, previous->second);
1209 DCHECK_LE(cur_point_ptr->second, diagonal_cur_end_y);
1211 diagonal.end.second = cur_point_ptr->second;
1212 diagonal.end_index = cut_segment_if_necessary(
i, diagonal.end);
1213 DCHECK(shape.points[diagonal.end_index] == diagonal.end);
1214 cur_point_ptr = &shape.points[shape.next[
i]];
1215 cut_end = absl::c_upper_bound(
1217 PolygonCut{.start = {cur_point_ptr->first,
1218 std::numeric_limits<IntegerValue>::max()}},
1219 PolygonCut::CmpByStartX());
1220 previous = &shape.points[
i];
1228void CutShapeWithPolygonCuts(FlatShape& shape,
1229 absl::Span<const PolygonCut> cuts) {
1230 std::vector<int> previous(shape.points.size(), -1);
1231 for (
int i = 0;
i < shape.points.size();
i++) {
1232 previous[shape.next[
i]] =
i;
1235 std::vector<std::pair<int, int>> cut_previous_index(cuts.size(), {-1, -1});
1236 for (
int i = 0;
i < cuts.size();
i++) {
1237 DCHECK(cuts[
i].start == shape.points[cuts[
i].start_index]);
1238 DCHECK(cuts[
i].
end == shape.points[cuts[
i].end_index]);
1240 cut_previous_index[
i].first = previous[cuts[
i].start_index];
1241 cut_previous_index[
i].second = previous[cuts[
i].end_index];
1244 for (
const auto& [
i, j] : cut_previous_index) {
1245 const int prev_start_next = shape.next[
i];
1246 const int prev_end_next = shape.next[j];
1247 const std::pair<IntegerValue, IntegerValue> start =
1248 shape.points[prev_start_next];
1249 const std::pair<IntegerValue, IntegerValue>
end =
1250 shape.points[prev_end_next];
1252 shape.points.push_back(start);
1253 shape.next[
i] = shape.points.size() - 1;
1254 shape.next.push_back(prev_end_next);
1256 shape.points.push_back(
end);
1257 shape.next[j] = shape.points.size() - 1;
1258 shape.next.push_back(prev_start_next);
1269 auto is_aligned = [](
const std::pair<IntegerValue, IntegerValue>& p1,
1270 const std::pair<IntegerValue, IntegerValue>& p2,
1271 const std::pair<IntegerValue, IntegerValue>& p3) {
1272 return ((p1.first == p2.first) == (p2.first == p3.first)) &&
1273 ((p1.second == p2.second) == (p2.second == p3.second));
1275 const auto add_segment =
1276 [&is_aligned](
const std::pair<IntegerValue, IntegerValue>& segment,
1277 const int start_index,
1278 std::vector<std::pair<IntegerValue, IntegerValue>>& points,
1279 std::vector<int>& next) {
1280 if (points.size() > 1 + start_index &&
1281 is_aligned(points[points.size() - 1], points[points.size() - 2],
1283 points.back() = segment;
1285 points.push_back(segment);
1286 next.push_back(points.size());
1292 FlatShape flat_shape;
1294 const std::pair<IntegerValue, IntegerValue>& segment =
1296 add_segment(segment, 0, flat_shape.points, flat_shape.next);
1298 flat_shape.next.back() = 0;
1300 const int start = flat_shape.next.size();
1303 const std::pair<IntegerValue, IntegerValue>& segment =
1305 add_segment(segment, start, flat_shape.points, flat_shape.next);
1307 flat_shape.next.back() = start;
1310 std::array<std::vector<PolygonCut>, 4> all_cuts =
1311 GetPotentialPolygonCuts(flat_shape);
1321 std::array<std::vector<PolygonCut>, 2> good_diagonals;
1324 PolygonCut::CmpByStartX())) {
1325 good_diagonals[0].push_back(d);
1330 PolygonCut::CmpByStartY())) {
1331 good_diagonals[1].push_back(d);
1341 std::vector<std::vector<int>> arcs(good_diagonals[0].size());
1342 for (
int i = 0;
i < good_diagonals[0].size(); ++
i) {
1343 for (
int j = 0; j < good_diagonals[1].size(); ++j) {
1344 const PolygonCut& vertical = good_diagonals[0][
i];
1345 const PolygonCut& horizontal = good_diagonals[1][j];
1346 const IntegerValue vertical_x = vertical.start.first;
1347 const IntegerValue horizontal_y = horizontal.start.second;
1348 if (horizontal.start.first <= vertical_x &&
1349 vertical_x <= horizontal.end.first &&
1350 vertical.start.second <= horizontal_y &&
1351 horizontal_y <= vertical.end.second) {
1352 arcs[
i].push_back(good_diagonals[0].size() + j);
1357 const std::vector<bool> minimum_cover =
1360 std::vector<PolygonCut> minimum_cover_horizontal_diagonals;
1361 for (
int i = good_diagonals[0].size();
1362 i < good_diagonals[0].size() + good_diagonals[1].size(); ++
i) {
1363 if (minimum_cover[
i])
continue;
1364 minimum_cover_horizontal_diagonals.push_back(
1365 good_diagonals[1][
i - good_diagonals[0].size()]);
1371 CutShapeWithPolygonCuts(flat_shape, minimum_cover_horizontal_diagonals);
1375 all_cuts = GetPotentialPolygonCuts(flat_shape);
1387 PolygonCut::CmpByStartX())) {
1388 cuts.push_back(cut);
1392 CutShapeWithPolygonCuts(flat_shape, cuts);
1396 std::vector<Rectangle> result;
1397 std::vector<bool> seen(flat_shape.points.size(),
false);
1398 for (
int i = 0;
i < flat_shape.points.size(); ++
i) {
1399 if (seen[
i])
continue;
1401 .x_min = std::numeric_limits<IntegerValue>::max(),
1402 .x_max = std::numeric_limits<IntegerValue>::min(),
1403 .y_min = std::numeric_limits<IntegerValue>::max(),
1404 .y_max = std::numeric_limits<IntegerValue>::min(),
1409 rectangle.
GrowToInclude({.x_min = flat_shape.points[cur].first,
1410 .x_max = flat_shape.points[cur].first,
1411 .y_min = flat_shape.points[cur].second,
1412 .y_max = flat_shape.points[cur].second});
1413 cur = flat_shape.next[cur];
1414 DCHECK_LT(cur, flat_shape.next.size());
1422 std::vector<Rectangle>* mandatory_rectangles,
1423 std::vector<Rectangle>* optional_rectangles) {
1424 if (mandatory_rectangles->empty())
return false;
1425 std::vector<Rectangle> result = *mandatory_rectangles;
1426 std::vector<Rectangle> new_optional_rectangles = *optional_rectangles;
1430 if (mandatory_rectangles->size() < 1000) {
1431 Rectangle mandatory_bounding_box = (*mandatory_rectangles)[0];
1432 for (
const Rectangle& box : *mandatory_rectangles) {
1435 const std::vector<Rectangle> mandatory_empty_holes =
1437 const std::vector<std::vector<int>> mandatory_holes_components =
1442 std::vector<Rectangle> holes_in_component;
1443 for (
const std::vector<int>& component : mandatory_holes_components) {
1444 holes_in_component.clear();
1445 holes_in_component.reserve(component.size());
1446 for (
const int index : component) {
1447 holes_in_component.push_back(mandatory_empty_holes[index]);
1451 result.insert(result.end(), holes_in_component.begin(),
1452 holes_in_component.end());
1456 new_optional_rectangles, std::move(holes_in_component));
1461 std::vector<SingleShape> shapes =
BoxesToShapes(result, neighbours);
1463 std::vector<Rectangle> original_result;
1465 original_result = result;
1470 const std::vector<Rectangle> cut_rectangles =
1472 result.insert(result.end(), cut_rectangles.begin(), cut_rectangles.end());
1479 if (result.size() >= mandatory_rectangles->size())
return false;
1480 mandatory_rectangles->swap(result);
1481 optional_rectangles->swap(new_optional_rectangles);
1486 absl::Span<const RectangleInRange> non_fixed_boxes,
1487 absl::Span<const Rectangle> fixed_boxes,
int max_num_components) {
1488 if (max_num_components <= 1)
return {};
1490 IntegerValue min_x_size = std::numeric_limits<IntegerValue>::max();
1491 IntegerValue min_y_size = std::numeric_limits<IntegerValue>::max();
1493 CHECK(!non_fixed_boxes.empty());
1494 Rectangle bounding_box = non_fixed_boxes[0].bounding_area;
1498 min_x_size = std::min(min_x_size, box.x_size);
1499 min_y_size = std::min(min_y_size, box.y_size);
1501 DCHECK_GT(min_x_size, 0);
1502 DCHECK_GT(min_y_size, 0);
1504 std::vector<Rectangle> optional_boxes = FindSpacesThatCannotBeOccupied(
1505 fixed_boxes, non_fixed_boxes, bounding_box, min_x_size, min_y_size);
1506 std::vector<Rectangle> unoccupiable_space = {fixed_boxes.begin(),
1508 unoccupiable_space.insert(unoccupiable_space.end(), optional_boxes.begin(),
1509 optional_boxes.end());
1511 std::vector<Rectangle> occupiable_space =
1514 std::vector<Rectangle> empty;
1516 std::vector<std::vector<int>> space_components =
1519 if (space_components.size() == 1 ||
1520 space_components.size() > max_num_components) {
1527 absl::flat_hash_set<int> component_set;
1528 for (
const std::vector<int>& component : space_components) {
1529 Rectangle bin_bounding_box = occupiable_space[component[0]];
1530 for (
int i = 1;
i < component.size(); ++
i) {
1533 std::optional<Rectangle> reachable_area_bounding_box;
1535 for (
int idx : component) {
1536 bin.
bin_area.push_back(occupiable_space[idx]);
1538 for (
int i = 0;
i < non_fixed_boxes.size();
i++) {
1539 if (!non_fixed_boxes[
i].bounding_area.IsDisjoint(bin_bounding_box)) {
1540 if (reachable_area_bounding_box.has_value()) {
1541 reachable_area_bounding_box->GrowToInclude(
1542 non_fixed_boxes[
i].bounding_area);
1544 reachable_area_bounding_box = non_fixed_boxes[
i].bounding_area;
1550 result.
bins.pop_back();
1557 VLOG_EVERY_N_SEC(1, 1) <<
"Detected a bin packing problem with "
1558 << result.
bins.size()
1559 <<
" bins. Original problem sizes: "
1560 << non_fixed_boxes.size() <<
" non-fixed boxes, "
1561 << fixed_boxes.size() <<
" fixed boxes.";
int NumRectangles() const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool ReduceNumberOfBoxesExactMandatory(std::vector< Rectangle > *mandatory_rectangles, std::vector< Rectangle > *optional_rectangles)
std::vector< std::vector< int > > SplitInConnectedComponents(const Neighbours &neighbours)
bool RegionIncludesOther(absl::Span< const Rectangle > region, absl::Span< const Rectangle > other)
The two regions must be defined by non-overlapping rectangles.
bool operator==(const BoolArgumentProto &lhs, const BoolArgumentProto &rhs)
std::vector< Rectangle > FindEmptySpaces(const Rectangle &bounding_box, std::vector< Rectangle > ocupied_rectangles)
std::vector< SingleShape > BoxesToShapes(absl::Span< const Rectangle > rectangles, const Neighbours &neighbours)
Disjoint2dPackingResult DetectDisjointRegionIn2dPacking(absl::Span< const RectangleInRange > non_fixed_boxes, absl::Span< const Rectangle > fixed_boxes, int max_num_components)
std::vector< Rectangle > PavedRegionDifference(std::vector< Rectangle > original_region, absl::Span< const Rectangle > area_to_remove)
H AbslHashValue(H h, const IntVar &i)
– ABSL HASHING SUPPORT --------------------------------------------------—
bool ReduceNumberofBoxesGreedy(std::vector< Rectangle > *mandatory_rectangles, std::vector< Rectangle > *optional_rectangles)
std::vector< Rectangle > CutShapeIntoRectangles(SingleShape shape)
void AbslStringify(Sink &sink, EdgePosition e)
std::vector< std::pair< int, int > > FindPartialRectangleIntersections(absl::Span< const Rectangle > rectangles)
Neighbours BuildNeighboursGraph(absl::Span< const Rectangle > rectangles)
bool PresolveFixed2dRectangles(absl::Span< const RectangleInRange > non_fixed_boxes, std::vector< Rectangle > *fixed_boxes)
std::string RenderDot(std::optional< Rectangle > bb, absl::Span< const Rectangle > solution, std::string_view extra_dot_payload)
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< bool > BipartiteMinimumVertexCover(const std::vector< std::vector< int > > &left_to_right_arcs, int num_right)
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
void FindStronglyConnectedComponents(NodeIndex num_nodes, const Graph &graph, SccOutput *components)
Simple wrapper function for most usage.
std::vector< Rectangle > bin_area
std::vector< Rectangle > fixed_boxes
Fixed boxes that the non-fixed boxes in this bin cannot overlap with.
std::vector< int > non_fixed_box_indexes
Non-fixed boxes on the original problem to copy to this new constraint.
void GrowToInclude(const Rectangle &other)
absl::InlinedVector< Rectangle, 4 > RegionDifference(const Rectangle &other) const
IntegerValue SizeY() const
IntegerValue SizeX() const
std::vector< std::pair< IntegerValue, IntegerValue > > step_points
The two vectors should have exactly the same size.
std::vector< int > touching_box_index
std::vector< ShapePath > holes