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()};
321 LOG(FATAL) <<
"Invalid edge position: " <<
static_cast<int>(pos);
324 template <
typename H>
326 return H::combine(std::move(h), e.x_start, e.y_start, e.size);
330 return x_start == other.x_start && y_start == other.y_start &&
334 static bool CompareXThenY(
const Edge& a,
const Edge&
b) {
335 return std::tie(a.x_start, a.y_start, a.size) <
336 std::tie(
b.x_start,
b.y_start,
b.size);
338 static bool CompareYThenX(
const Edge& a,
const Edge&
b) {
339 return std::tie(a.y_start, a.x_start, a.size) <
340 std::tie(
b.y_start,
b.x_start,
b.size);
346 std::vector<Rectangle>* optional_rectangles) {
349 std::vector<std::unique_ptr<Rectangle>> rectangle_storage;
350 enum class OptionalEnum { OPTIONAL, MANDATORY };
352 std::vector<std::pair<const Rectangle*, OptionalEnum>> rectangles_ptr;
353 absl::flat_hash_map<Edge, int> top_edges_to_rectangle;
354 absl::flat_hash_map<Edge, int> bottom_edges_to_rectangle;
355 absl::flat_hash_map<Edge, int> left_edges_to_rectangle;
356 absl::flat_hash_map<Edge, int> right_edges_to_rectangle;
358 bool changed_optional =
false;
359 bool changed_mandatory =
false;
361 auto add_rectangle = [&](
const Rectangle* rectangle_ptr,
362 OptionalEnum optional) {
363 const int index = rectangles_ptr.size();
364 rectangles_ptr.push_back({rectangle_ptr, optional});
365 const Rectangle& rectangle = *rectangles_ptr[index].first;
374 for (
const Rectangle& rectangle : *mandatory_rectangles) {
375 add_rectangle(&rectangle, OptionalEnum::MANDATORY);
377 for (
const Rectangle& rectangle : *optional_rectangles) {
378 add_rectangle(&rectangle, OptionalEnum::OPTIONAL);
381 auto remove_rectangle = [&](
const int index) {
382 const Rectangle& rectangle = *rectangles_ptr[index].first;
387 top_edges_to_rectangle.erase(top_edge);
388 bottom_edges_to_rectangle.erase(bottom_edge);
389 left_edges_to_rectangle.erase(left_edge);
390 right_edges_to_rectangle.erase(right_edge);
391 rectangles_ptr[index].first =
nullptr;
394 bool iteration_did_merge =
true;
395 while (iteration_did_merge) {
396 iteration_did_merge =
false;
397 for (
int i = 0;
i < rectangles_ptr.size(); ++
i) {
398 if (!rectangles_ptr[
i].first) {
401 const Rectangle& rectangle = *rectangles_ptr[
i].first;
409 if (
const auto it = right_edges_to_rectangle.find(left_edge);
410 it != right_edges_to_rectangle.end()) {
412 }
else if (
const auto it = left_edges_to_rectangle.find(right_edge);
413 it != left_edges_to_rectangle.end()) {
415 }
else if (
const auto it = bottom_edges_to_rectangle.find(top_edge);
416 it != bottom_edges_to_rectangle.end()) {
418 }
else if (
const auto it = top_edges_to_rectangle.find(bottom_edge);
419 it != top_edges_to_rectangle.end()) {
425 iteration_did_merge =
true;
428 const OptionalEnum new_optional =
429 (rectangles_ptr[
i].second == OptionalEnum::MANDATORY ||
430 rectangles_ptr[index].second == OptionalEnum::MANDATORY)
431 ? OptionalEnum::MANDATORY
432 : OptionalEnum::OPTIONAL;
434 changed_mandatory || (new_optional == OptionalEnum::MANDATORY);
437 (rectangles_ptr[
i].second == OptionalEnum::OPTIONAL ||
438 rectangles_ptr[index].second == OptionalEnum::OPTIONAL);
439 rectangle_storage.push_back(std::make_unique<Rectangle>(rectangle));
440 Rectangle& new_rectangle = *rectangle_storage.back();
443 remove_rectangle(index);
444 add_rectangle(&new_rectangle, new_optional);
448 if (changed_mandatory) {
449 std::vector<Rectangle> new_rectangles;
450 for (
auto [rectangle, optional] : rectangles_ptr) {
451 if (rectangle && optional == OptionalEnum::MANDATORY) {
452 new_rectangles.push_back(*rectangle);
455 *mandatory_rectangles = std::move(new_rectangles);
457 if (changed_optional) {
458 std::vector<Rectangle> new_rectangles;
459 for (
auto [rectangle, optional] : rectangles_ptr) {
460 if (rectangle && optional == OptionalEnum::OPTIONAL) {
461 new_rectangles.push_back(*rectangle);
464 *optional_rectangles = std::move(new_rectangles);
466 return changed_mandatory;
474 std::vector<std::pair<Edge, int>> edges_to_rectangle[4];
475 std::vector<std::tuple<int, EdgePosition, int>> neighbours;
476 neighbours.reserve(2 * rectangles.size());
477 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
479 edges_to_rectangle[edge_position].reserve(rectangles.size());
482 for (
int i = 0;
i < rectangles.size(); ++
i) {
484 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
486 const Edge edge = Edge::GetEdge(rectangle, edge_position);
487 edges_to_rectangle[edge_position].push_back({edge,
i});
490 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
496 ? [](
const std::pair<Edge, int>& a,
497 const std::pair<Edge, int>&
498 b) {
return Edge::CompareXThenY(a.first,
b.first); }
499 : [](
const std::pair<Edge, int>& a,
const std::pair<Edge, int>&
b) {
500 return Edge::CompareYThenX(a.first,
b.first);
502 absl::c_sort(edges_to_rectangle[edge_position], cmp);
505 constexpr struct EdgeData {
508 bool (*cmp)(
const Edge&,
const Edge&);
511 .cmp = &Edge::CompareYThenX},
514 .cmp = &Edge::CompareYThenX},
517 .cmp = &Edge::CompareXThenY},
520 .cmp = &Edge::CompareXThenY}};
522 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
523 const EdgePosition edge_position = edge_data[edge_int].edge;
525 edge_data[edge_int].opposite_edge;
526 auto it = edges_to_rectangle[edge_position].begin();
527 for (
const auto& [edge, index] :
528 edges_to_rectangle[opposite_edge_position]) {
529 while (it != edges_to_rectangle[edge_position].
end() &&
530 edge_data[edge_int].cmp(it->first, edge)) {
533 if (it == edges_to_rectangle[edge_position].
end()) {
538 while (it != edges_to_rectangle[edge_position].
end() &&
539 it->first.y_start == edge.y_start &&
540 it->first.x_start < edge.x_start + edge.size) {
541 neighbours.push_back({index, opposite_edge_position, it->second});
542 neighbours.push_back({it->second, edge_position, index});
546 while (it != edges_to_rectangle[edge_position].
end() &&
547 it->first.x_start == edge.x_start &&
548 it->first.y_start < edge.y_start + edge.size) {
549 neighbours.push_back({index, opposite_edge_position, it->second});
550 neighbours.push_back({it->second, edge_position, index});
565 explicit GraphView(
const Neighbours& neighbours)
566 : neighbours_(neighbours) {}
567 absl::Span<const int> operator[](
int node)
const {
569 for (
int edge = 0; edge < 4; ++edge) {
570 const auto edge_neighbors = neighbours_.GetSortedNeighbors(
572 for (
int neighbor : edge_neighbors) {
573 temp_.push_back(neighbor);
581 mutable std::vector<int> temp_;
584 std::vector<std::vector<int>> components;
586 GraphView(neighbours), &components);
591IntegerValue GetClockwiseStart(
EdgePosition edge,
const Rectangle& rectangle) {
594 return rectangle.y_min;
596 return rectangle.y_max;
598 return rectangle.x_max;
600 return rectangle.x_min;
602 LOG(FATAL) <<
"Invalid edge position: " <<
static_cast<int>(edge);
608 return rectangle.y_max;
610 return rectangle.y_min;
612 return rectangle.x_min;
614 return rectangle.x_max;
616 LOG(FATAL) <<
"Invalid edge position: " <<
static_cast<int>(edge);
632void GetAllSegmentsTouchingVoid(
633 absl::Span<const Rectangle> rectangles,
const Neighbours& neighbours,
634 std::vector<std::pair<Edge, int>>& vertical_edges_on_boundary,
635 std::vector<std::pair<Edge, int>>& horizontal_edges_on_boundary) {
636 for (
int i = 0;
i < rectangles.size(); ++
i) {
638 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
640 const auto box_neighbors = neighbours.GetSortedNeighbors(
i, edge);
641 if (box_neighbors.empty()) {
643 vertical_edges_on_boundary.push_back(
644 {Edge::GetEdge(rectangle, edge),
i});
646 horizontal_edges_on_boundary.push_back(
647 {Edge::GetEdge(rectangle, edge),
i});
651 IntegerValue previous_pos = GetClockwiseStart(edge, rectangle);
652 for (
int n = 0; n <= box_neighbors.size(); ++n) {
653 IntegerValue neighbor_start;
655 if (n == box_neighbors.size()) {
658 neighbor_start = GetClockwiseEnd(edge, rectangle);
660 const int neighbor_idx = box_neighbors[n];
661 neighbor = &rectangles[neighbor_idx];
662 neighbor_start = GetClockwiseStart(edge, *neighbor);
666 if (neighbor_start > previous_pos) {
667 vertical_edges_on_boundary.push_back(
668 {Edge{.x_start = rectangle.x_min,
669 .y_start = previous_pos,
670 .size = neighbor_start - previous_pos},
675 if (neighbor_start < previous_pos) {
676 vertical_edges_on_boundary.push_back(
677 {Edge{.x_start = rectangle.x_max,
678 .y_start = neighbor_start,
679 .size = previous_pos - neighbor_start},
684 if (neighbor_start < previous_pos) {
685 horizontal_edges_on_boundary.push_back(
686 {Edge{.x_start = neighbor_start,
687 .y_start = rectangle.y_min,
688 .size = previous_pos - neighbor_start},
693 if (neighbor_start > previous_pos) {
694 horizontal_edges_on_boundary.push_back(
695 {Edge{.x_start = previous_pos,
696 .y_start = rectangle.y_max,
697 .size = neighbor_start - previous_pos},
702 if (n != box_neighbors.size()) {
703 previous_pos = GetClockwiseEnd(edge, *neighbor);
715 std::pair<IntegerValue, IntegerValue> starting_step_point,
716 std::array<absl::btree_map<std::pair<IntegerValue, IntegerValue>,
717 std::pair<IntegerValue, int>>,
718 4>& segments_to_follow) {
724 segments_to_follow[starting_edge_position].extract(starting_step_point);
725 CHECK(!extracted.empty());
726 const int first_index = extracted.mapped().second;
728 std::pair<IntegerValue, IntegerValue> cur = starting_step_point;
729 int cur_index = first_index;
733 path.step_points.push_back(cur);
735 bool can_go[4] = {
false,
false,
false,
false};
737 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
739 if (segments_to_follow[edge].contains(cur)) {
741 direction_to_take = edge;
745 if (can_go == absl::Span<const bool>{
false,
false,
false,
false}) {
764 if (cur_index == it_x->second.second) {
771 if (cur_index == it_y->second.second) {
778 auto extracted = segments_to_follow[direction_to_take].extract(cur);
779 cur_index = extracted.mapped().second;
780 switch (direction_to_take) {
782 cur.first -= extracted.mapped().first;
787 cur.first += extracted.mapped().first;
791 cur.second += extracted.mapped().first;
796 cur.second -= extracted.mapped().first;
800 path.touching_box_index.push_back(cur_index);
802 path.touching_box_index.push_back(cur_index);
808std::vector<SingleShape>
BoxesToShapes(absl::Span<const Rectangle> rectangles,
810 std::vector<std::pair<Edge, int>> vertical_edges_on_boundary;
811 std::vector<std::pair<Edge, int>> horizontal_edges_on_boundary;
812 GetAllSegmentsTouchingVoid(rectangles, neighbours, vertical_edges_on_boundary,
813 horizontal_edges_on_boundary);
815 std::array<absl::btree_map<std::pair<IntegerValue, IntegerValue>,
816 std::pair<IntegerValue, int>>,
820 for (
const auto& [edge, box_index] : vertical_edges_on_boundary) {
822 edge.size, box_index};
824 edge.x_start, edge.y_start + edge.size}] = {edge.size, box_index};
826 for (
const auto& [edge, box_index] : horizontal_edges_on_boundary) {
828 edge.size, box_index};
830 edge.x_start + edge.size, edge.y_start}] = {edge.size, box_index};
834 std::vector<SingleShape> result(components.size());
835 std::vector<int> box_to_component(rectangles.size());
836 for (
int i = 0;
i < components.size(); ++
i) {
837 for (
const int box_index : components[
i]) {
838 box_to_component[box_index] =
i;
843 const int box_index =
845 const std::pair<IntegerValue, IntegerValue> starting_step_point =
847 const int component_index = box_to_component[box_index];
852 const bool is_hole = !result[component_index].boundary.step_points.empty();
853 ShapePath& path = is_hole ? result[component_index].holes.emplace_back()
854 : result[component_index].boundary;
869 std::pair<IntegerValue, IntegerValue> start;
870 std::pair<IntegerValue, IntegerValue>
end;
875 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
876 return std::tie(a.start.second, a.start.first) <
877 std::tie(
b.start.second,
b.start.first);
882 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
883 return std::tie(a.end.second, a.end.first) <
884 std::tie(
b.end.second,
b.end.first);
889 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
890 return a.start <
b.start;
895 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
896 return a.end <
b.end;
900 template <
typename Sink>
901 friend void AbslStringify(Sink& sink,
const PolygonCut& diagonal) {
902 absl::Format(&sink,
"(%v,%v)-(%v,%v)", diagonal.start.first,
903 diagonal.start.second, diagonal.end.first,
904 diagonal.end.second);
915 std::vector<std::pair<IntegerValue, IntegerValue>> points;
916 std::vector<int> next;
920 const std::pair<IntegerValue, IntegerValue>& curr_segment,
921 const std::pair<IntegerValue, IntegerValue>& next_segment) {
922 if (curr_segment.first == next_segment.first) {
940std::array<std::vector<PolygonCut>, 4> GetPotentialPolygonCuts(
942 std::array<std::vector<PolygonCut>, 4> cuts;
946 for (
int i = 0;
i < shape.points.size();
i++) {
947 const auto& it = &shape.points[shape.next[
i]];
948 const auto& previous = &shape.points[
i];
949 const auto& next_segment = &shape.points[shape.next[shape.next[
i]]];
950 const EdgePosition previous_dir = GetSegmentDirection(*previous, *it);
951 const EdgePosition next_dir = GetSegmentDirection(*it, *next_segment);
958 .end = {std::numeric_limits<IntegerValue>::max(), it->second},
959 .start_index = shape.next[
i]});
966 {.start = {std::numeric_limits<IntegerValue>::min(), it->second},
968 .end_index = shape.next[
i]});
975 {.start = {it->first, std::numeric_limits<IntegerValue>::min()},
977 .end_index = shape.next[
i]});
984 .end = {it->first, std::numeric_limits<IntegerValue>::max()},
985 .start_index = shape.next[
i]});
1000 PolygonCut::CmpByStartY());
1002 PolygonCut::CmpByEndY());
1006 PolygonCut::CmpByStartX());
1011 const auto cut_segment_if_necessary =
1012 [&shape](
int segment_idx,
1013 std::pair<IntegerValue, IntegerValue> point_to_cut) {
1014 const auto& cur = shape.points[segment_idx];
1015 const auto& next = shape.points[shape.next[segment_idx]];
1016 if (cur.second == next.second) {
1017 DCHECK_EQ(point_to_cut.second, cur.second);
1019 const IntegerValue edge_start = std::min(cur.first, next.first);
1020 const IntegerValue edge_end = std::max(cur.first, next.first);
1022 if (edge_start < point_to_cut.first &&
1023 point_to_cut.first < edge_end) {
1024 shape.points.push_back(point_to_cut);
1025 const int next_idx = shape.next[segment_idx];
1026 shape.next[segment_idx] = shape.points.size() - 1;
1027 shape.next.push_back(next_idx);
1028 return static_cast<int>(shape.points.size() - 1);
1030 return (shape.points[segment_idx] == point_to_cut)
1032 : shape.next[segment_idx];
1034 DCHECK_EQ(cur.first, next.first);
1035 DCHECK_EQ(point_to_cut.first, cur.first);
1037 const IntegerValue edge_start = std::min(cur.second, next.second);
1038 const IntegerValue edge_end = std::max(cur.second, next.second);
1040 if (edge_start < point_to_cut.second &&
1041 point_to_cut.second < edge_end) {
1042 shape.points.push_back(point_to_cut);
1043 const int next_idx = shape.next[segment_idx];
1044 shape.next[segment_idx] = shape.points.size() - 1;
1045 shape.next.push_back(next_idx);
1046 return static_cast<int>(shape.points.size() - 1);
1048 return (shape.points[segment_idx] == point_to_cut)
1050 : shape.next[segment_idx];
1054 for (
int i = 0;
i < shape.points.size();
i++) {
1055 const auto* cur_point_ptr = &shape.points[shape.next[
i]];
1056 const auto* previous = &shape.points[
i];
1057 DCHECK(cur_point_ptr->first == previous->first ||
1058 cur_point_ptr->second == previous->second)
1059 <<
"found a segment that is neither horizontal nor vertical";
1061 GetSegmentDirection(*previous, *cur_point_ptr);
1064 const auto cut_start = absl::c_lower_bound(
1066 PolygonCut{.start = {std::numeric_limits<IntegerValue>::min(),
1067 cur_point_ptr->second}},
1068 PolygonCut::CmpByStartY());
1069 auto cut_end = absl::c_upper_bound(
1071 PolygonCut{.start = {std::numeric_limits<IntegerValue>::max(),
1073 PolygonCut::CmpByStartY());
1075 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1076 PolygonCut& diagonal = *cut_it;
1077 const IntegerValue diagonal_start_x = diagonal.start.first;
1078 const IntegerValue diagonal_cur_end_x = diagonal.end.first;
1080 DCHECK_LE(cur_point_ptr->second, diagonal.start.second);
1081 DCHECK_LE(diagonal.start.second, previous->second);
1084 if (diagonal_start_x <= previous->first &&
1085 diagonal_cur_end_x > cur_point_ptr->first) {
1086 DCHECK_LT(diagonal_start_x, cur_point_ptr->first);
1087 DCHECK_LE(previous->first, diagonal_cur_end_x);
1089 diagonal.end.first = cur_point_ptr->first;
1091 diagonal.end_index = cut_segment_if_necessary(
i, diagonal.end);
1092 DCHECK(shape.points[diagonal.end_index] == diagonal.end);
1098 cur_point_ptr = &shape.points[shape.next[
i]];
1099 previous = &shape.points[
i];
1100 cut_end = absl::c_upper_bound(
1102 PolygonCut{.start = {std::numeric_limits<IntegerValue>::max(),
1104 PolygonCut::CmpByStartY());
1110 const auto cut_start = absl::c_lower_bound(
1112 PolygonCut{.end = {std::numeric_limits<IntegerValue>::min(),
1114 PolygonCut::CmpByEndY());
1115 auto cut_end = absl::c_upper_bound(
1117 PolygonCut{.end = {std::numeric_limits<IntegerValue>::max(),
1118 cur_point_ptr->second}},
1119 PolygonCut::CmpByEndY());
1120 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1121 PolygonCut& diagonal = *cut_it;
1122 const IntegerValue diagonal_start_x = diagonal.start.first;
1123 const IntegerValue diagonal_cur_end_x = diagonal.end.first;
1125 DCHECK_LE(diagonal.end.second, cur_point_ptr->second);
1126 DCHECK_LE(previous->second, diagonal.end.second);
1129 if (diagonal_start_x < cur_point_ptr->first &&
1130 previous->first <= diagonal_cur_end_x) {
1131 DCHECK_LT(cur_point_ptr->first, diagonal_cur_end_x);
1132 DCHECK_LE(diagonal_start_x, previous->first);
1134 diagonal.start.first = cur_point_ptr->first;
1135 diagonal.start_index = cut_segment_if_necessary(
i, diagonal.start);
1136 DCHECK(shape.points[diagonal.start_index] == diagonal.start);
1137 cur_point_ptr = &shape.points[shape.next[
i]];
1138 previous = &shape.points[
i];
1139 cut_end = absl::c_upper_bound(
1141 PolygonCut{.end = {std::numeric_limits<IntegerValue>::max(),
1142 cur_point_ptr->second}},
1143 PolygonCut::CmpByEndY());
1149 const auto cut_start = absl::c_lower_bound(
1151 PolygonCut{.end = {cur_point_ptr->first,
1152 std::numeric_limits<IntegerValue>::min()}},
1153 PolygonCut::CmpByEndX());
1154 auto cut_end = absl::c_upper_bound(
1156 PolygonCut{.end = {previous->first,
1157 std::numeric_limits<IntegerValue>::max()}},
1158 PolygonCut::CmpByEndX());
1159 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1160 PolygonCut& diagonal = *cut_it;
1161 const IntegerValue diagonal_start_y = diagonal.start.second;
1162 const IntegerValue diagonal_cur_end_y = diagonal.end.second;
1165 DCHECK_LE(cur_point_ptr->first, diagonal.end.first);
1166 DCHECK_LE(diagonal.end.first, previous->first);
1169 if (diagonal_start_y < cur_point_ptr->second &&
1170 cur_point_ptr->second <= diagonal_cur_end_y) {
1171 DCHECK_LE(diagonal_start_y, previous->second);
1172 DCHECK_LT(cur_point_ptr->second, diagonal_cur_end_y);
1174 diagonal.start.second = cur_point_ptr->second;
1175 diagonal.start_index = cut_segment_if_necessary(
i, diagonal.start);
1176 DCHECK(shape.points[diagonal.start_index] == diagonal.start);
1177 cur_point_ptr = &shape.points[shape.next[
i]];
1178 previous = &shape.points[
i];
1179 cut_end = absl::c_upper_bound(
1181 PolygonCut{.end = {previous->first,
1182 std::numeric_limits<IntegerValue>::max()}},
1183 PolygonCut::CmpByEndX());
1189 const auto cut_start = absl::c_lower_bound(
1191 PolygonCut{.start = {previous->first,
1192 std::numeric_limits<IntegerValue>::min()}},
1193 PolygonCut::CmpByStartX());
1194 auto cut_end = absl::c_upper_bound(
1196 PolygonCut{.start = {cur_point_ptr->first,
1197 std::numeric_limits<IntegerValue>::max()}},
1198 PolygonCut::CmpByStartX());
1199 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1200 PolygonCut& diagonal = *cut_it;
1201 const IntegerValue diagonal_start_y = diagonal.start.second;
1202 const IntegerValue diagonal_cur_end_y = diagonal.end.second;
1205 DCHECK_LE(previous->first, diagonal.start.first);
1206 DCHECK_LE(diagonal.start.first, cur_point_ptr->first);
1209 if (diagonal_start_y <= cur_point_ptr->second &&
1210 cur_point_ptr->second < diagonal_cur_end_y) {
1211 DCHECK_LT(diagonal_start_y, previous->second);
1212 DCHECK_LE(cur_point_ptr->second, diagonal_cur_end_y);
1214 diagonal.end.second = cur_point_ptr->second;
1215 diagonal.end_index = cut_segment_if_necessary(
i, diagonal.end);
1216 DCHECK(shape.points[diagonal.end_index] == diagonal.end);
1217 cur_point_ptr = &shape.points[shape.next[
i]];
1218 cut_end = absl::c_upper_bound(
1220 PolygonCut{.start = {cur_point_ptr->first,
1221 std::numeric_limits<IntegerValue>::max()}},
1222 PolygonCut::CmpByStartX());
1223 previous = &shape.points[
i];
1231void CutShapeWithPolygonCuts(FlatShape& shape,
1232 absl::Span<const PolygonCut> cuts) {
1233 std::vector<int> previous(shape.points.size(), -1);
1234 for (
int i = 0;
i < shape.points.size();
i++) {
1235 previous[shape.next[
i]] =
i;
1238 std::vector<std::pair<int, int>> cut_previous_index(cuts.size(), {-1, -1});
1239 for (
int i = 0;
i < cuts.size();
i++) {
1240 DCHECK(cuts[
i].start == shape.points[cuts[
i].start_index]);
1241 DCHECK(cuts[
i].
end == shape.points[cuts[
i].end_index]);
1243 cut_previous_index[
i].first = previous[cuts[
i].start_index];
1244 cut_previous_index[
i].second = previous[cuts[
i].end_index];
1247 for (
const auto& [
i, j] : cut_previous_index) {
1248 const int prev_start_next = shape.next[
i];
1249 const int prev_end_next = shape.next[j];
1250 const std::pair<IntegerValue, IntegerValue> start =
1251 shape.points[prev_start_next];
1252 const std::pair<IntegerValue, IntegerValue>
end =
1253 shape.points[prev_end_next];
1255 shape.points.push_back(start);
1256 shape.next[
i] = shape.points.size() - 1;
1257 shape.next.push_back(prev_end_next);
1259 shape.points.push_back(
end);
1260 shape.next[j] = shape.points.size() - 1;
1261 shape.next.push_back(prev_start_next);
1272 auto is_aligned = [](
const std::pair<IntegerValue, IntegerValue>& p1,
1273 const std::pair<IntegerValue, IntegerValue>& p2,
1274 const std::pair<IntegerValue, IntegerValue>& p3) {
1275 return ((p1.first == p2.first) == (p2.first == p3.first)) &&
1276 ((p1.second == p2.second) == (p2.second == p3.second));
1278 const auto add_segment =
1279 [&is_aligned](
const std::pair<IntegerValue, IntegerValue>& segment,
1280 const int start_index,
1281 std::vector<std::pair<IntegerValue, IntegerValue>>& points,
1282 std::vector<int>& next) {
1283 if (points.size() > 1 + start_index &&
1284 is_aligned(points[points.size() - 1], points[points.size() - 2],
1286 points.back() = segment;
1288 points.push_back(segment);
1289 next.push_back(points.size());
1295 FlatShape flat_shape;
1297 const std::pair<IntegerValue, IntegerValue>& segment =
1299 add_segment(segment, 0, flat_shape.points, flat_shape.next);
1301 flat_shape.next.back() = 0;
1303 const int start = flat_shape.next.size();
1306 const std::pair<IntegerValue, IntegerValue>& segment =
1308 add_segment(segment, start, flat_shape.points, flat_shape.next);
1310 flat_shape.next.back() = start;
1313 std::array<std::vector<PolygonCut>, 4> all_cuts =
1314 GetPotentialPolygonCuts(flat_shape);
1324 std::array<std::vector<PolygonCut>, 2> good_diagonals;
1327 PolygonCut::CmpByStartX())) {
1328 good_diagonals[0].push_back(d);
1333 PolygonCut::CmpByStartY())) {
1334 good_diagonals[1].push_back(d);
1344 std::vector<std::vector<int>> arcs(good_diagonals[0].size());
1345 for (
int i = 0;
i < good_diagonals[0].size(); ++
i) {
1346 for (
int j = 0; j < good_diagonals[1].size(); ++j) {
1347 const PolygonCut& vertical = good_diagonals[0][
i];
1348 const PolygonCut& horizontal = good_diagonals[1][j];
1349 const IntegerValue vertical_x = vertical.start.first;
1350 const IntegerValue horizontal_y = horizontal.start.second;
1351 if (horizontal.start.first <= vertical_x &&
1352 vertical_x <= horizontal.end.first &&
1353 vertical.start.second <= horizontal_y &&
1354 horizontal_y <= vertical.end.second) {
1355 arcs[
i].push_back(good_diagonals[0].size() + j);
1360 const std::vector<bool> minimum_cover =
1363 std::vector<PolygonCut> minimum_cover_horizontal_diagonals;
1364 for (
int i = good_diagonals[0].size();
1365 i < good_diagonals[0].size() + good_diagonals[1].size(); ++
i) {
1366 if (minimum_cover[
i])
continue;
1367 minimum_cover_horizontal_diagonals.push_back(
1368 good_diagonals[1][
i - good_diagonals[0].size()]);
1374 CutShapeWithPolygonCuts(flat_shape, minimum_cover_horizontal_diagonals);
1378 all_cuts = GetPotentialPolygonCuts(flat_shape);
1390 PolygonCut::CmpByStartX())) {
1391 cuts.push_back(cut);
1395 CutShapeWithPolygonCuts(flat_shape, cuts);
1399 std::vector<Rectangle> result;
1400 std::vector<bool> seen(flat_shape.points.size(),
false);
1401 for (
int i = 0;
i < flat_shape.points.size(); ++
i) {
1402 if (seen[
i])
continue;
1404 .x_min = std::numeric_limits<IntegerValue>::max(),
1405 .x_max = std::numeric_limits<IntegerValue>::min(),
1406 .y_min = std::numeric_limits<IntegerValue>::max(),
1407 .y_max = std::numeric_limits<IntegerValue>::min(),
1412 rectangle.
GrowToInclude({.x_min = flat_shape.points[cur].first,
1413 .x_max = flat_shape.points[cur].first,
1414 .y_min = flat_shape.points[cur].second,
1415 .y_max = flat_shape.points[cur].second});
1416 cur = flat_shape.next[cur];
1417 DCHECK_LT(cur, flat_shape.next.size());
1425 std::vector<Rectangle>* mandatory_rectangles,
1426 std::vector<Rectangle>* optional_rectangles) {
1427 if (mandatory_rectangles->empty())
return false;
1428 std::vector<Rectangle> result = *mandatory_rectangles;
1429 std::vector<Rectangle> new_optional_rectangles = *optional_rectangles;
1433 if (mandatory_rectangles->size() < 1000) {
1434 Rectangle mandatory_bounding_box = (*mandatory_rectangles)[0];
1435 for (
const Rectangle& box : *mandatory_rectangles) {
1438 const std::vector<Rectangle> mandatory_empty_holes =
1440 const std::vector<std::vector<int>> mandatory_holes_components =
1445 std::vector<Rectangle> holes_in_component;
1446 for (
const std::vector<int>& component : mandatory_holes_components) {
1447 holes_in_component.clear();
1448 holes_in_component.reserve(component.size());
1449 for (
const int index : component) {
1450 holes_in_component.push_back(mandatory_empty_holes[index]);
1454 result.insert(result.end(), holes_in_component.begin(),
1455 holes_in_component.end());
1459 new_optional_rectangles, std::move(holes_in_component));
1464 std::vector<SingleShape> shapes =
BoxesToShapes(result, neighbours);
1466 std::vector<Rectangle> original_result;
1468 original_result = result;
1473 const std::vector<Rectangle> cut_rectangles =
1475 result.insert(result.end(), cut_rectangles.begin(), cut_rectangles.end());
1482 if (result.size() >= mandatory_rectangles->size())
return false;
1483 mandatory_rectangles->swap(result);
1484 optional_rectangles->swap(new_optional_rectangles);
1489 absl::Span<const RectangleInRange> non_fixed_boxes,
1490 absl::Span<const Rectangle> fixed_boxes,
int max_num_components) {
1491 if (max_num_components <= 1)
return {};
1493 IntegerValue min_x_size = std::numeric_limits<IntegerValue>::max();
1494 IntegerValue min_y_size = std::numeric_limits<IntegerValue>::max();
1496 CHECK(!non_fixed_boxes.empty());
1497 Rectangle bounding_box = non_fixed_boxes[0].bounding_area;
1501 min_x_size = std::min(min_x_size, box.x_size);
1502 min_y_size = std::min(min_y_size, box.y_size);
1504 DCHECK_GT(min_x_size, 0);
1505 DCHECK_GT(min_y_size, 0);
1507 std::vector<Rectangle> optional_boxes = FindSpacesThatCannotBeOccupied(
1508 fixed_boxes, non_fixed_boxes, bounding_box, min_x_size, min_y_size);
1509 std::vector<Rectangle> unoccupiable_space = {fixed_boxes.begin(),
1511 unoccupiable_space.insert(unoccupiable_space.end(), optional_boxes.begin(),
1512 optional_boxes.end());
1514 std::vector<Rectangle> occupiable_space =
1517 std::vector<Rectangle> empty;
1519 std::vector<std::vector<int>> space_components =
1522 if (space_components.size() == 1 ||
1523 space_components.size() > max_num_components) {
1530 for (
const std::vector<int>& component : space_components) {
1531 Rectangle bin_bounding_box = occupiable_space[component[0]];
1532 for (
int i = 1;
i < component.size(); ++
i) {
1535 std::optional<Rectangle> reachable_area_bounding_box;
1537 for (
int idx : component) {
1538 bin.
bin_area.push_back(occupiable_space[idx]);
1540 for (
int i = 0;
i < non_fixed_boxes.size();
i++) {
1541 if (!non_fixed_boxes[
i].bounding_area.IsDisjoint(bin_bounding_box)) {
1542 if (reachable_area_bounding_box.has_value()) {
1543 reachable_area_bounding_box->GrowToInclude(
1544 non_fixed_boxes[
i].bounding_area);
1546 reachable_area_bounding_box = non_fixed_boxes[
i].bounding_area;
1552 result.
bins.pop_back();
1559 VLOG_EVERY_N_SEC(1, 1) <<
"Detected a bin packing problem with "
1560 << result.
bins.size()
1561 <<
" bins. Original problem sizes: "
1562 << non_fixed_boxes.size() <<
" non-fixed boxes, "
1563 << 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)
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)
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)
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)
std::vector< Rectangle > bin_area
std::vector< Rectangle > fixed_boxes
std::vector< int > non_fixed_box_indexes
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
std::vector< int > touching_box_index
std::vector< ShapePath > holes