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/strings/str_cat.h"
32#include "absl/types/span.h"
44std::vector<Rectangle> FindSpacesThatCannotBeOccupied(
45 absl::Span<const Rectangle> fixed_boxes,
46 absl::Span<const RectangleInRange> non_fixed_boxes,
47 const Rectangle& bounding_box, IntegerValue min_x_size,
48 IntegerValue min_y_size) {
49 std::vector<Rectangle> optional_boxes = {fixed_boxes.begin(),
52 if (bounding_box.x_min > std::numeric_limits<IntegerValue>::min() &&
53 bounding_box.y_min > std::numeric_limits<IntegerValue>::min() &&
54 bounding_box.x_max < std::numeric_limits<IntegerValue>::max() &&
55 bounding_box.y_max < std::numeric_limits<IntegerValue>::max()) {
63 optional_boxes.push_back({.x_min = bounding_box.x_min - 1,
64 .x_max = bounding_box.x_max,
65 .y_min = bounding_box.y_min - 1,
66 .y_max = bounding_box.y_min});
67 optional_boxes.push_back({.x_min = bounding_box.x_max,
68 .x_max = bounding_box.x_max + 1,
69 .y_min = bounding_box.y_min - 1,
70 .y_max = bounding_box.y_max});
71 optional_boxes.push_back({.x_min = bounding_box.x_min,
72 .x_max = bounding_box.x_max + 1,
73 .y_min = bounding_box.y_max,
74 .y_max = bounding_box.y_max + 1});
75 optional_boxes.push_back({.x_min = bounding_box.x_min - 1,
76 .x_max = bounding_box.x_min,
77 .y_min = bounding_box.y_min,
78 .y_max = bounding_box.y_max + 1});
84 const int num_optional_boxes_to_remove = optional_boxes.size();
88 const auto add_box = [&optional_boxes](
Rectangle new_box) {
89 std::vector<Rectangle> to_add = {std::move(new_box)};
90 for (
int i = 0;
i < to_add.size(); ++
i) {
92 bool is_disjoint =
true;
93 for (
const Rectangle& existing_box : optional_boxes) {
94 if (!new_box.IsDisjoint(existing_box)) {
98 to_add.push_back(disjoint_box);
104 optional_boxes.push_back(std::move(new_box));
113 if (non_fixed_boxes.size() < 1000) {
114 std::vector<Rectangle> bounding_boxes;
115 bounding_boxes.reserve(non_fixed_boxes.size());
117 bounding_boxes.push_back(box.bounding_area);
119 std::vector<Rectangle> empty_spaces =
121 for (
const Rectangle& r : empty_spaces) {
127 for (
int i = 1;
i < optional_boxes.size(); ++
i) {
129 for (
int j = 0; j <
i; ++j) {
130 const Rectangle& other_box = optional_boxes[j];
131 const IntegerValue lower_top = std::min(cur_box.y_max, other_box.y_max);
132 const IntegerValue higher_bottom =
133 std::max(other_box.y_min, cur_box.y_min);
134 const IntegerValue rightmost_left_edge =
135 std::max(other_box.x_min, cur_box.x_min);
136 const IntegerValue leftmost_right_edge =
137 std::min(other_box.x_max, cur_box.x_max);
138 if (rightmost_left_edge < leftmost_right_edge) {
139 if (lower_top < higher_bottom &&
140 higher_bottom - lower_top < min_y_size) {
141 add_box({.x_min = rightmost_left_edge,
142 .x_max = leftmost_right_edge,
144 .y_max = higher_bottom});
147 if (higher_bottom < lower_top) {
148 if (leftmost_right_edge < rightmost_left_edge &&
149 rightmost_left_edge - leftmost_right_edge < min_x_size) {
150 add_box({.x_min = leftmost_right_edge,
151 .x_max = rightmost_left_edge,
152 .y_min = higher_bottom,
153 .y_max = lower_top});
158 optional_boxes.erase(optional_boxes.begin(),
159 optional_boxes.begin() + num_optional_boxes_to_remove);
160 return optional_boxes;
166 absl::Span<const RectangleInRange> non_fixed_boxes,
167 std::vector<Rectangle>* fixed_boxes) {
171 bool changed =
false;
174 IntegerValue original_area = 0;
175 std::vector<Rectangle> fixed_boxes_copy;
177 for (
const Rectangle& r : *fixed_boxes) {
178 original_area += r.Area();
182 fixed_boxes_copy = *fixed_boxes;
185 const int original_num_boxes = fixed_boxes->size();
189 std::vector<Rectangle> empty_vec;
194 IntegerValue min_x_size = std::numeric_limits<IntegerValue>::max();
195 IntegerValue min_y_size = std::numeric_limits<IntegerValue>::max();
197 CHECK(!non_fixed_boxes.empty());
198 Rectangle bounding_box = non_fixed_boxes[0].bounding_area;
202 min_x_size = std::min(min_x_size, box.x_size);
203 min_y_size = std::min(min_y_size, box.y_size);
205 DCHECK_GT(min_x_size, 0);
206 DCHECK_GT(min_y_size, 0);
212 while (new_size < fixed_boxes->size()) {
213 Rectangle& rectangle = (*fixed_boxes)[new_size];
214 DCHECK_GT(rectangle.
SizeX(), 0);
215 DCHECK_GT(rectangle.
SizeY(), 0);
232 if (rectangle.
SizeX() <= 0 || rectangle.
SizeY() <= 0) {
234 std::swap(rectangle, (*fixed_boxes)[fixed_boxes->size() - 1]);
235 fixed_boxes->resize(fixed_boxes->size() - 1);
243 std::vector<Rectangle> optional_boxes = FindSpacesThatCannotBeOccupied(
244 *fixed_boxes, non_fixed_boxes, bounding_box, min_x_size, min_y_size);
268 const int num_after_first_pass = fixed_boxes->size();
272 if (changed && VLOG_IS_ON(1)) {
273 IntegerValue area = 0;
274 for (
const Rectangle& r : *fixed_boxes) {
277 VLOG_EVERY_N_SEC(1, 1) <<
"Presolved " << original_num_boxes
278 <<
" fixed rectangles (area=" << original_area
279 <<
") into " << num_after_first_pass <<
" then "
280 << fixed_boxes->size() <<
" (area=" << area <<
")";
282 VLOG_EVERY_N_SEC(2, 2) <<
"Presolved rectangles:\n"
283 <<
RenderDot(bounding_box, fixed_boxes_copy)
286 << (optional_boxes.empty()
288 : absl::StrCat(
"Unused optional rects:\n",
297 IntegerValue x_start;
298 IntegerValue y_start;
301 static Edge GetEdge(
const Rectangle& rectangle,
EdgePosition pos) {
304 return {.x_start = rectangle.x_min,
305 .y_start = rectangle.y_max,
306 .size = rectangle.SizeX()};
308 return {.x_start = rectangle.x_min,
309 .y_start = rectangle.y_min,
310 .size = rectangle.SizeX()};
312 return {.x_start = rectangle.x_min,
313 .y_start = rectangle.y_min,
314 .size = rectangle.SizeY()};
316 return {.x_start = rectangle.x_max,
317 .y_start = rectangle.y_min,
318 .size = rectangle.SizeY()};
322 template <
typename H>
324 return H::combine(std::move(h), e.x_start, e.y_start, e.size);
328 return x_start == other.x_start && y_start == other.y_start &&
332 static bool CompareXThenY(
const Edge& a,
const Edge&
b) {
333 return std::tie(a.x_start, a.y_start, a.size) <
334 std::tie(
b.x_start,
b.y_start,
b.size);
336 static bool CompareYThenX(
const Edge& a,
const Edge&
b) {
337 return std::tie(a.y_start, a.x_start, a.size) <
338 std::tie(
b.y_start,
b.x_start,
b.size);
344 std::vector<Rectangle>* optional_rectangles) {
347 std::vector<std::unique_ptr<Rectangle>> rectangle_storage;
348 enum class OptionalEnum { OPTIONAL, MANDATORY };
350 std::vector<std::pair<const Rectangle*, OptionalEnum>> rectangles_ptr;
351 absl::flat_hash_map<Edge, int> top_edges_to_rectangle;
352 absl::flat_hash_map<Edge, int> bottom_edges_to_rectangle;
353 absl::flat_hash_map<Edge, int> left_edges_to_rectangle;
354 absl::flat_hash_map<Edge, int> right_edges_to_rectangle;
356 bool changed_optional =
false;
357 bool changed_mandatory =
false;
359 auto add_rectangle = [&](
const Rectangle* rectangle_ptr,
361 const int index = rectangles_ptr.size();
362 rectangles_ptr.push_back({rectangle_ptr,
optional});
363 const Rectangle& rectangle = *rectangles_ptr[index].first;
372 for (
const Rectangle& rectangle : *mandatory_rectangles) {
373 add_rectangle(&rectangle, OptionalEnum::MANDATORY);
375 for (
const Rectangle& rectangle : *optional_rectangles) {
376 add_rectangle(&rectangle, OptionalEnum::OPTIONAL);
379 auto remove_rectangle = [&](
const int index) {
380 const Rectangle& rectangle = *rectangles_ptr[index].first;
385 top_edges_to_rectangle.erase(top_edge);
386 bottom_edges_to_rectangle.erase(bottom_edge);
387 left_edges_to_rectangle.erase(left_edge);
388 right_edges_to_rectangle.erase(right_edge);
389 rectangles_ptr[index].first =
nullptr;
392 bool iteration_did_merge =
true;
393 while (iteration_did_merge) {
394 iteration_did_merge =
false;
395 for (
int i = 0;
i < rectangles_ptr.size(); ++
i) {
396 if (!rectangles_ptr[
i].first) {
399 const Rectangle& rectangle = *rectangles_ptr[
i].first;
407 if (
const auto it = right_edges_to_rectangle.find(left_edge);
408 it != right_edges_to_rectangle.end()) {
410 }
else if (
const auto it = left_edges_to_rectangle.find(right_edge);
411 it != left_edges_to_rectangle.end()) {
413 }
else if (
const auto it = bottom_edges_to_rectangle.find(top_edge);
414 it != bottom_edges_to_rectangle.end()) {
416 }
else if (
const auto it = top_edges_to_rectangle.find(bottom_edge);
417 it != top_edges_to_rectangle.end()) {
423 iteration_did_merge =
true;
426 const OptionalEnum new_optional =
427 (rectangles_ptr[
i].second == OptionalEnum::MANDATORY ||
428 rectangles_ptr[index].second == OptionalEnum::MANDATORY)
429 ? OptionalEnum::MANDATORY
430 : OptionalEnum::OPTIONAL;
432 changed_mandatory || (new_optional == OptionalEnum::MANDATORY);
435 (rectangles_ptr[
i].second == OptionalEnum::OPTIONAL ||
436 rectangles_ptr[index].second == OptionalEnum::OPTIONAL);
437 rectangle_storage.push_back(std::make_unique<Rectangle>(rectangle));
438 Rectangle& new_rectangle = *rectangle_storage.back();
441 remove_rectangle(index);
442 add_rectangle(&new_rectangle, new_optional);
446 if (changed_mandatory) {
447 std::vector<Rectangle> new_rectangles;
448 for (
auto [rectangle,
optional] : rectangles_ptr) {
449 if (rectangle &&
optional == OptionalEnum::MANDATORY) {
450 new_rectangles.push_back(*rectangle);
453 *mandatory_rectangles = std::move(new_rectangles);
455 if (changed_optional) {
456 std::vector<Rectangle> new_rectangles;
457 for (
auto [rectangle,
optional] : rectangles_ptr) {
458 if (rectangle &&
optional == OptionalEnum::OPTIONAL) {
459 new_rectangles.push_back(*rectangle);
462 *optional_rectangles = std::move(new_rectangles);
464 return changed_mandatory;
472 std::vector<std::pair<Edge, int>> edges_to_rectangle[4];
473 std::vector<std::tuple<int, EdgePosition, int>> neighbours;
474 neighbours.reserve(2 * rectangles.size());
475 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
477 edges_to_rectangle[edge_position].reserve(rectangles.size());
480 for (
int i = 0;
i < rectangles.size(); ++
i) {
482 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
484 const Edge edge = Edge::GetEdge(rectangle, edge_position);
485 edges_to_rectangle[edge_position].push_back({edge,
i});
488 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
494 ? [](
const std::pair<Edge, int>& a,
495 const std::pair<Edge, int>&
496 b) {
return Edge::CompareXThenY(a.first,
b.first); }
497 : [](
const std::pair<Edge, int>& a,
const std::pair<Edge, int>&
b) {
498 return Edge::CompareYThenX(a.first,
b.first);
500 absl::c_sort(edges_to_rectangle[edge_position], cmp);
503 constexpr struct EdgeData {
506 bool (*cmp)(
const Edge&,
const Edge&);
509 .cmp = &Edge::CompareYThenX},
512 .cmp = &Edge::CompareYThenX},
515 .cmp = &Edge::CompareXThenY},
518 .cmp = &Edge::CompareXThenY}};
520 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
521 const EdgePosition edge_position = edge_data[edge_int].edge;
523 edge_data[edge_int].opposite_edge;
524 auto it = edges_to_rectangle[edge_position].begin();
525 for (
const auto& [edge, index] :
526 edges_to_rectangle[opposite_edge_position]) {
527 while (it != edges_to_rectangle[edge_position].end() &&
528 edge_data[edge_int].cmp(it->first, edge)) {
531 if (it == edges_to_rectangle[edge_position].end()) {
536 while (it != edges_to_rectangle[edge_position].end() &&
537 it->first.y_start == edge.y_start &&
538 it->first.x_start < edge.x_start + edge.size) {
539 neighbours.push_back({index, opposite_edge_position, it->second});
540 neighbours.push_back({it->second, edge_position, index});
544 while (it != edges_to_rectangle[edge_position].end() &&
545 it->first.x_start == edge.x_start &&
546 it->first.y_start < edge.y_start + edge.size) {
547 neighbours.push_back({index, opposite_edge_position, it->second});
548 neighbours.push_back({it->second, edge_position, index});
563 explicit GraphView(
const Neighbours& neighbours)
564 : neighbours_(neighbours) {}
565 absl::Span<const int> operator[](
int node)
const {
567 for (
int edge = 0; edge < 4; ++edge) {
568 const auto edge_neighbors = neighbours_.GetSortedNeighbors(
570 for (
int neighbor : edge_neighbors) {
571 temp_.push_back(neighbor);
579 mutable std::vector<int> temp_;
582 std::vector<std::vector<int>> components;
584 GraphView(neighbours), &components);
589IntegerValue GetClockwiseStart(
EdgePosition edge,
const Rectangle& rectangle) {
592 return rectangle.y_min;
594 return rectangle.y_max;
596 return rectangle.x_max;
598 return rectangle.x_min;
605 return rectangle.y_max;
607 return rectangle.y_min;
609 return rectangle.x_min;
611 return rectangle.x_max;
628void GetAllSegmentsTouchingVoid(
629 absl::Span<const Rectangle> rectangles,
const Neighbours& neighbours,
630 std::vector<std::pair<Edge, int>>& vertical_edges_on_boundary,
631 std::vector<std::pair<Edge, int>>& horizontal_edges_on_boundary) {
632 for (
int i = 0;
i < rectangles.size(); ++
i) {
634 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
636 const auto box_neighbors = neighbours.GetSortedNeighbors(
i, edge);
637 if (box_neighbors.empty()) {
639 vertical_edges_on_boundary.push_back(
640 {Edge::GetEdge(rectangle, edge),
i});
642 horizontal_edges_on_boundary.push_back(
643 {Edge::GetEdge(rectangle, edge),
i});
647 IntegerValue previous_pos = GetClockwiseStart(edge, rectangle);
648 for (
int n = 0; n <= box_neighbors.size(); ++n) {
649 IntegerValue neighbor_start;
651 if (n == box_neighbors.size()) {
654 neighbor_start = GetClockwiseEnd(edge, rectangle);
656 const int neighbor_idx = box_neighbors[n];
657 neighbor = &rectangles[neighbor_idx];
658 neighbor_start = GetClockwiseStart(edge, *neighbor);
662 if (neighbor_start > previous_pos) {
663 vertical_edges_on_boundary.push_back(
664 {Edge{.x_start = rectangle.x_min,
665 .y_start = previous_pos,
666 .size = neighbor_start - previous_pos},
671 if (neighbor_start < previous_pos) {
672 vertical_edges_on_boundary.push_back(
673 {Edge{.x_start = rectangle.x_max,
674 .y_start = neighbor_start,
675 .size = previous_pos - neighbor_start},
680 if (neighbor_start < previous_pos) {
681 horizontal_edges_on_boundary.push_back(
682 {Edge{.x_start = neighbor_start,
683 .y_start = rectangle.y_min,
684 .size = previous_pos - neighbor_start},
689 if (neighbor_start > previous_pos) {
690 horizontal_edges_on_boundary.push_back(
691 {Edge{.x_start = previous_pos,
692 .y_start = rectangle.y_max,
693 .size = neighbor_start - previous_pos},
698 if (n != box_neighbors.size()) {
699 previous_pos = GetClockwiseEnd(edge, *neighbor);
711 std::pair<IntegerValue, IntegerValue> starting_step_point,
712 std::array<absl::btree_map<std::pair<IntegerValue, IntegerValue>,
713 std::pair<IntegerValue, int>>,
714 4>& segments_to_follow) {
720 segments_to_follow[starting_edge_position].extract(starting_step_point);
721 CHECK(!extracted.empty());
722 const int first_index = extracted.mapped().second;
724 std::pair<IntegerValue, IntegerValue> cur = starting_step_point;
725 int cur_index = first_index;
729 path.step_points.push_back(cur);
731 bool can_go[4] = {
false,
false,
false,
false};
733 for (
int edge_int = 0; edge_int < 4; ++edge_int) {
735 if (segments_to_follow[edge].contains(cur)) {
737 direction_to_take = edge;
741 if (can_go == absl::Span<const bool>{
false,
false,
false,
false}) {
760 if (cur_index == it_x->second.second) {
767 if (cur_index == it_y->second.second) {
774 auto extracted = segments_to_follow[direction_to_take].extract(cur);
775 cur_index = extracted.mapped().second;
776 switch (direction_to_take) {
778 cur.first -= extracted.mapped().first;
783 cur.first += extracted.mapped().first;
787 cur.second += extracted.mapped().first;
792 cur.second -= extracted.mapped().first;
796 path.touching_box_index.push_back(cur_index);
798 path.touching_box_index.push_back(cur_index);
804std::vector<SingleShape>
BoxesToShapes(absl::Span<const Rectangle> rectangles,
806 std::vector<std::pair<Edge, int>> vertical_edges_on_boundary;
807 std::vector<std::pair<Edge, int>> horizontal_edges_on_boundary;
808 GetAllSegmentsTouchingVoid(rectangles, neighbours, vertical_edges_on_boundary,
809 horizontal_edges_on_boundary);
811 std::array<absl::btree_map<std::pair<IntegerValue, IntegerValue>,
812 std::pair<IntegerValue, int>>,
816 for (
const auto& [edge, box_index] : vertical_edges_on_boundary) {
818 edge.size, box_index};
820 edge.x_start, edge.y_start + edge.size}] = {edge.size, box_index};
822 for (
const auto& [edge, box_index] : horizontal_edges_on_boundary) {
824 edge.size, box_index};
826 edge.x_start + edge.size, edge.y_start}] = {edge.size, box_index};
830 std::vector<SingleShape> result(components.size());
831 std::vector<int> box_to_component(rectangles.size());
832 for (
int i = 0;
i < components.size(); ++
i) {
833 for (
const int box_index : components[
i]) {
834 box_to_component[box_index] =
i;
839 const int box_index =
841 const std::pair<IntegerValue, IntegerValue> starting_step_point =
843 const int component_index = box_to_component[box_index];
848 const bool is_hole = !result[component_index].boundary.step_points.empty();
849 ShapePath& path = is_hole ? result[component_index].holes.emplace_back()
850 : result[component_index].boundary;
865 std::pair<IntegerValue, IntegerValue> start;
866 std::pair<IntegerValue, IntegerValue> end;
871 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
872 return std::tie(a.start.second, a.start.first) <
873 std::tie(
b.start.second,
b.start.first);
878 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
879 return std::tie(a.end.second, a.end.first) <
880 std::tie(
b.end.second,
b.end.first);
885 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
886 return a.start <
b.start;
891 bool operator()(
const PolygonCut& a,
const PolygonCut&
b)
const {
892 return a.end <
b.end;
896 template <
typename Sink>
897 friend void AbslStringify(Sink& sink,
const PolygonCut& diagonal) {
898 absl::Format(&sink,
"(%v,%v)-(%v,%v)", diagonal.start.first,
899 diagonal.start.second, diagonal.end.first,
900 diagonal.end.second);
911 std::vector<std::pair<IntegerValue, IntegerValue>> points;
912 std::vector<int> next;
916 const std::pair<IntegerValue, IntegerValue>& curr_segment,
917 const std::pair<IntegerValue, IntegerValue>& next_segment) {
918 if (curr_segment.first == next_segment.first) {
936std::array<std::vector<PolygonCut>, 4> GetPotentialPolygonCuts(
938 std::array<std::vector<PolygonCut>, 4> cuts;
942 for (
int i = 0;
i < shape.points.size();
i++) {
943 const auto& it = &shape.points[shape.next[
i]];
944 const auto& previous = &shape.points[
i];
945 const auto& next_segment = &shape.points[shape.next[shape.next[
i]]];
946 const EdgePosition previous_dir = GetSegmentDirection(*previous, *it);
947 const EdgePosition next_dir = GetSegmentDirection(*it, *next_segment);
954 .end = {std::numeric_limits<IntegerValue>::max(), it->second},
955 .start_index = shape.next[
i]});
962 {.start = {std::numeric_limits<IntegerValue>::min(), it->second},
964 .end_index = shape.next[
i]});
971 {.start = {it->first, std::numeric_limits<IntegerValue>::min()},
973 .end_index = shape.next[
i]});
980 .end = {it->first, std::numeric_limits<IntegerValue>::max()},
981 .start_index = shape.next[
i]});
996 PolygonCut::CmpByStartY());
998 PolygonCut::CmpByEndY());
1002 PolygonCut::CmpByStartX());
1007 const auto cut_segment_if_necessary =
1008 [&shape](
int segment_idx,
1009 std::pair<IntegerValue, IntegerValue> point_to_cut) {
1010 const auto& cur = shape.points[segment_idx];
1011 const auto& next = shape.points[shape.next[segment_idx]];
1012 if (cur.second == next.second) {
1013 DCHECK_EQ(point_to_cut.second, cur.second);
1015 const IntegerValue edge_start = std::min(cur.first, next.first);
1016 const IntegerValue edge_end = std::max(cur.first, next.first);
1018 if (edge_start < point_to_cut.first &&
1019 point_to_cut.first < edge_end) {
1020 shape.points.push_back(point_to_cut);
1021 const int next_idx = shape.next[segment_idx];
1022 shape.next[segment_idx] = shape.points.size() - 1;
1023 shape.next.push_back(next_idx);
1024 return static_cast<int>(shape.points.size() - 1);
1026 return (shape.points[segment_idx] == point_to_cut)
1028 : shape.next[segment_idx];
1030 DCHECK_EQ(cur.first, next.first);
1031 DCHECK_EQ(point_to_cut.first, cur.first);
1033 const IntegerValue edge_start = std::min(cur.second, next.second);
1034 const IntegerValue edge_end = std::max(cur.second, next.second);
1036 if (edge_start < point_to_cut.second &&
1037 point_to_cut.second < edge_end) {
1038 shape.points.push_back(point_to_cut);
1039 const int next_idx = shape.next[segment_idx];
1040 shape.next[segment_idx] = shape.points.size() - 1;
1041 shape.next.push_back(next_idx);
1042 return static_cast<int>(shape.points.size() - 1);
1044 return (shape.points[segment_idx] == point_to_cut)
1046 : shape.next[segment_idx];
1050 for (
int i = 0;
i < shape.points.size();
i++) {
1051 const auto* cur_point_ptr = &shape.points[shape.next[
i]];
1052 const auto* previous = &shape.points[
i];
1053 DCHECK(cur_point_ptr->first == previous->first ||
1054 cur_point_ptr->second == previous->second)
1055 <<
"found a segment that is neither horizontal nor vertical";
1057 GetSegmentDirection(*previous, *cur_point_ptr);
1060 const auto cut_start = absl::c_lower_bound(
1062 PolygonCut{.start = {std::numeric_limits<IntegerValue>::min(),
1063 cur_point_ptr->second}},
1064 PolygonCut::CmpByStartY());
1065 auto cut_end = absl::c_upper_bound(
1067 PolygonCut{.start = {std::numeric_limits<IntegerValue>::max(),
1069 PolygonCut::CmpByStartY());
1071 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1072 PolygonCut& diagonal = *cut_it;
1073 const IntegerValue diagonal_start_x = diagonal.start.first;
1074 const IntegerValue diagonal_cur_end_x = diagonal.end.first;
1076 DCHECK_LE(cur_point_ptr->second, diagonal.start.second);
1077 DCHECK_LE(diagonal.start.second, previous->second);
1080 if (diagonal_start_x <= previous->first &&
1081 diagonal_cur_end_x > cur_point_ptr->first) {
1082 DCHECK_LT(diagonal_start_x, cur_point_ptr->first);
1083 DCHECK_LE(previous->first, diagonal_cur_end_x);
1085 diagonal.end.first = cur_point_ptr->first;
1087 diagonal.end_index = cut_segment_if_necessary(
i, diagonal.end);
1088 DCHECK(shape.points[diagonal.end_index] == diagonal.end);
1094 cur_point_ptr = &shape.points[shape.next[
i]];
1095 previous = &shape.points[
i];
1096 cut_end = absl::c_upper_bound(
1098 PolygonCut{.start = {std::numeric_limits<IntegerValue>::max(),
1100 PolygonCut::CmpByStartY());
1106 const auto cut_start = absl::c_lower_bound(
1108 PolygonCut{.end = {std::numeric_limits<IntegerValue>::min(),
1110 PolygonCut::CmpByEndY());
1111 auto cut_end = absl::c_upper_bound(
1113 PolygonCut{.end = {std::numeric_limits<IntegerValue>::max(),
1114 cur_point_ptr->second}},
1115 PolygonCut::CmpByEndY());
1116 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1117 PolygonCut& diagonal = *cut_it;
1118 const IntegerValue diagonal_start_x = diagonal.start.first;
1119 const IntegerValue diagonal_cur_end_x = diagonal.end.first;
1121 DCHECK_LE(diagonal.end.second, cur_point_ptr->second);
1122 DCHECK_LE(previous->second, diagonal.end.second);
1125 if (diagonal_start_x < cur_point_ptr->first &&
1126 previous->first <= diagonal_cur_end_x) {
1127 DCHECK_LT(cur_point_ptr->first, diagonal_cur_end_x);
1128 DCHECK_LE(diagonal_start_x, previous->first);
1130 diagonal.start.first = cur_point_ptr->first;
1131 diagonal.start_index = cut_segment_if_necessary(
i, diagonal.start);
1132 DCHECK(shape.points[diagonal.start_index] == diagonal.start);
1133 cur_point_ptr = &shape.points[shape.next[
i]];
1134 previous = &shape.points[
i];
1135 cut_end = absl::c_upper_bound(
1137 PolygonCut{.end = {std::numeric_limits<IntegerValue>::max(),
1138 cur_point_ptr->second}},
1139 PolygonCut::CmpByEndY());
1145 const auto cut_start = absl::c_lower_bound(
1147 PolygonCut{.end = {cur_point_ptr->first,
1148 std::numeric_limits<IntegerValue>::min()}},
1149 PolygonCut::CmpByEndX());
1150 auto cut_end = absl::c_upper_bound(
1152 PolygonCut{.end = {previous->first,
1153 std::numeric_limits<IntegerValue>::max()}},
1154 PolygonCut::CmpByEndX());
1155 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1156 PolygonCut& diagonal = *cut_it;
1157 const IntegerValue diagonal_start_y = diagonal.start.second;
1158 const IntegerValue diagonal_cur_end_y = diagonal.end.second;
1161 DCHECK_LE(cur_point_ptr->first, diagonal.end.first);
1162 DCHECK_LE(diagonal.end.first, previous->first);
1165 if (diagonal_start_y < cur_point_ptr->second &&
1166 cur_point_ptr->second <= diagonal_cur_end_y) {
1167 DCHECK_LE(diagonal_start_y, previous->second);
1168 DCHECK_LT(cur_point_ptr->second, diagonal_cur_end_y);
1170 diagonal.start.second = cur_point_ptr->second;
1171 diagonal.start_index = cut_segment_if_necessary(
i, diagonal.start);
1172 DCHECK(shape.points[diagonal.start_index] == diagonal.start);
1173 cur_point_ptr = &shape.points[shape.next[
i]];
1174 previous = &shape.points[
i];
1175 cut_end = absl::c_upper_bound(
1177 PolygonCut{.end = {previous->first,
1178 std::numeric_limits<IntegerValue>::max()}},
1179 PolygonCut::CmpByEndX());
1185 const auto cut_start = absl::c_lower_bound(
1187 PolygonCut{.start = {previous->first,
1188 std::numeric_limits<IntegerValue>::min()}},
1189 PolygonCut::CmpByStartX());
1190 auto cut_end = absl::c_upper_bound(
1192 PolygonCut{.start = {cur_point_ptr->first,
1193 std::numeric_limits<IntegerValue>::max()}},
1194 PolygonCut::CmpByStartX());
1195 for (
auto cut_it = cut_start; cut_it < cut_end; ++cut_it) {
1196 PolygonCut& diagonal = *cut_it;
1197 const IntegerValue diagonal_start_y = diagonal.start.second;
1198 const IntegerValue diagonal_cur_end_y = diagonal.end.second;
1201 DCHECK_LE(previous->first, diagonal.start.first);
1202 DCHECK_LE(diagonal.start.first, cur_point_ptr->first);
1205 if (diagonal_start_y <= cur_point_ptr->second &&
1206 cur_point_ptr->second < diagonal_cur_end_y) {
1207 DCHECK_LT(diagonal_start_y, previous->second);
1208 DCHECK_LE(cur_point_ptr->second, diagonal_cur_end_y);
1210 diagonal.end.second = cur_point_ptr->second;
1211 diagonal.end_index = cut_segment_if_necessary(
i, diagonal.end);
1212 DCHECK(shape.points[diagonal.end_index] == diagonal.end);
1213 cur_point_ptr = &shape.points[shape.next[
i]];
1214 cut_end = absl::c_upper_bound(
1216 PolygonCut{.start = {cur_point_ptr->first,
1217 std::numeric_limits<IntegerValue>::max()}},
1218 PolygonCut::CmpByStartX());
1219 previous = &shape.points[
i];
1227void CutShapeWithPolygonCuts(FlatShape& shape,
1228 absl::Span<const PolygonCut> cuts) {
1229 std::vector<int> previous(shape.points.size(), -1);
1230 for (
int i = 0;
i < shape.points.size();
i++) {
1231 previous[shape.next[
i]] =
i;
1234 std::vector<std::pair<int, int>> cut_previous_index(cuts.size(), {-1, -1});
1235 for (
int i = 0;
i < cuts.size();
i++) {
1236 DCHECK(cuts[
i].start == shape.points[cuts[
i].start_index]);
1237 DCHECK(cuts[
i].end == shape.points[cuts[
i].end_index]);
1239 cut_previous_index[
i].first = previous[cuts[
i].start_index];
1240 cut_previous_index[
i].second = previous[cuts[
i].end_index];
1243 for (
const auto& [
i, j] : cut_previous_index) {
1244 const int prev_start_next = shape.next[
i];
1245 const int prev_end_next = shape.next[j];
1246 const std::pair<IntegerValue, IntegerValue> start =
1247 shape.points[prev_start_next];
1248 const std::pair<IntegerValue, IntegerValue> end =
1249 shape.points[prev_end_next];
1251 shape.points.push_back(start);
1252 shape.next[
i] = shape.points.size() - 1;
1253 shape.next.push_back(prev_end_next);
1255 shape.points.push_back(end);
1256 shape.next[j] = shape.points.size() - 1;
1257 shape.next.push_back(prev_start_next);
1268 auto is_aligned = [](
const std::pair<IntegerValue, IntegerValue>& p1,
1269 const std::pair<IntegerValue, IntegerValue>& p2,
1270 const std::pair<IntegerValue, IntegerValue>& p3) {
1271 return ((p1.first == p2.first) == (p2.first == p3.first)) &&
1272 ((p1.second == p2.second) == (p2.second == p3.second));
1274 const auto add_segment =
1275 [&is_aligned](
const std::pair<IntegerValue, IntegerValue>& segment,
1276 const int start_index,
1277 std::vector<std::pair<IntegerValue, IntegerValue>>& points,
1278 std::vector<int>& next) {
1279 if (points.size() > 1 + start_index &&
1280 is_aligned(points[points.size() - 1], points[points.size() - 2],
1282 points.back() = segment;
1284 points.push_back(segment);
1285 next.push_back(points.size());
1291 FlatShape flat_shape;
1293 const std::pair<IntegerValue, IntegerValue>& segment =
1295 add_segment(segment, 0, flat_shape.points, flat_shape.next);
1297 flat_shape.next.back() = 0;
1299 const int start = flat_shape.next.size();
1302 const std::pair<IntegerValue, IntegerValue>& segment =
1304 add_segment(segment, start, flat_shape.points, flat_shape.next);
1306 flat_shape.next.back() = start;
1309 std::array<std::vector<PolygonCut>, 4> all_cuts =
1310 GetPotentialPolygonCuts(flat_shape);
1320 std::array<std::vector<PolygonCut>, 2> good_diagonals;
1323 PolygonCut::CmpByStartX())) {
1324 good_diagonals[0].push_back(d);
1329 PolygonCut::CmpByStartY())) {
1330 good_diagonals[1].push_back(d);
1340 std::vector<std::vector<int>> arcs(good_diagonals[0].size());
1341 for (
int i = 0;
i < good_diagonals[0].size(); ++
i) {
1342 for (
int j = 0; j < good_diagonals[1].size(); ++j) {
1343 const PolygonCut& vertical = good_diagonals[0][
i];
1344 const PolygonCut& horizontal = good_diagonals[1][j];
1345 const IntegerValue vertical_x = vertical.start.first;
1346 const IntegerValue horizontal_y = horizontal.start.second;
1347 if (horizontal.start.first <= vertical_x &&
1348 vertical_x <= horizontal.end.first &&
1349 vertical.start.second <= horizontal_y &&
1350 horizontal_y <= vertical.end.second) {
1351 arcs[
i].push_back(good_diagonals[0].size() + j);
1356 const std::vector<bool> minimum_cover =
1359 std::vector<PolygonCut> minimum_cover_horizontal_diagonals;
1360 for (
int i = good_diagonals[0].size();
1361 i < good_diagonals[0].size() + good_diagonals[1].size(); ++
i) {
1362 if (minimum_cover[
i])
continue;
1363 minimum_cover_horizontal_diagonals.push_back(
1364 good_diagonals[1][
i - good_diagonals[0].size()]);
1370 CutShapeWithPolygonCuts(flat_shape, minimum_cover_horizontal_diagonals);
1374 all_cuts = GetPotentialPolygonCuts(flat_shape);
1386 PolygonCut::CmpByStartX())) {
1387 cuts.push_back(cut);
1391 CutShapeWithPolygonCuts(flat_shape, cuts);
1395 std::vector<Rectangle> result;
1396 std::vector<bool> seen(flat_shape.points.size(),
false);
1397 for (
int i = 0;
i < flat_shape.points.size(); ++
i) {
1398 if (seen[
i])
continue;
1400 .x_min = std::numeric_limits<IntegerValue>::max(),
1401 .x_max = std::numeric_limits<IntegerValue>::min(),
1402 .y_min = std::numeric_limits<IntegerValue>::max(),
1403 .y_max = std::numeric_limits<IntegerValue>::min(),
1408 rectangle.
GrowToInclude({.x_min = flat_shape.points[cur].first,
1409 .x_max = flat_shape.points[cur].first,
1410 .y_min = flat_shape.points[cur].second,
1411 .y_max = flat_shape.points[cur].second});
1412 cur = flat_shape.next[cur];
1413 DCHECK_LT(cur, flat_shape.next.size());
1421 std::vector<Rectangle>* mandatory_rectangles,
1422 std::vector<Rectangle>* optional_rectangles) {
1423 if (mandatory_rectangles->empty())
return false;
1424 std::vector<Rectangle> result = *mandatory_rectangles;
1425 std::vector<Rectangle> new_optional_rectangles = *optional_rectangles;
1429 if (mandatory_rectangles->size() < 1000) {
1430 Rectangle mandatory_bounding_box = (*mandatory_rectangles)[0];
1431 for (
const Rectangle& box : *mandatory_rectangles) {
1434 const std::vector<Rectangle> mandatory_empty_holes =
1436 const std::vector<std::vector<int>> mandatory_holes_components =
1441 std::vector<Rectangle> holes_in_component;
1442 for (
const std::vector<int>& component : mandatory_holes_components) {
1443 holes_in_component.clear();
1444 holes_in_component.reserve(component.size());
1445 for (
const int index : component) {
1446 holes_in_component.push_back(mandatory_empty_holes[index]);
1450 result.insert(result.end(), holes_in_component.begin(),
1451 holes_in_component.end());
1455 new_optional_rectangles, std::move(holes_in_component));
1460 std::vector<SingleShape> shapes =
BoxesToShapes(result, neighbours);
1462 std::vector<Rectangle> original_result;
1464 original_result = result;
1469 const std::vector<Rectangle> cut_rectangles =
1471 result.insert(result.end(), cut_rectangles.begin(), cut_rectangles.end());
1478 if (result.size() >= mandatory_rectangles->size())
return false;
1479 mandatory_rectangles->swap(result);
1480 optional_rectangles->swap(new_optional_rectangles);
1485 absl::Span<const RectangleInRange> non_fixed_boxes,
1486 absl::Span<const Rectangle> fixed_boxes,
int max_num_components) {
1487 if (max_num_components <= 1)
return {};
1489 IntegerValue min_x_size = std::numeric_limits<IntegerValue>::max();
1490 IntegerValue min_y_size = std::numeric_limits<IntegerValue>::max();
1492 CHECK(!non_fixed_boxes.empty());
1493 Rectangle bounding_box = non_fixed_boxes[0].bounding_area;
1497 min_x_size = std::min(min_x_size, box.x_size);
1498 min_y_size = std::min(min_y_size, box.y_size);
1500 DCHECK_GT(min_x_size, 0);
1501 DCHECK_GT(min_y_size, 0);
1503 std::vector<Rectangle> optional_boxes = FindSpacesThatCannotBeOccupied(
1504 fixed_boxes, non_fixed_boxes, bounding_box, min_x_size, min_y_size);
1505 std::vector<Rectangle> unoccupiable_space = {fixed_boxes.begin(),
1507 unoccupiable_space.insert(unoccupiable_space.end(), optional_boxes.begin(),
1508 optional_boxes.end());
1510 std::vector<Rectangle> occupiable_space =
1513 std::vector<Rectangle> empty;
1515 std::vector<std::vector<int>> space_components =
1518 if (space_components.size() == 1 ||
1519 space_components.size() > max_num_components) {
1526 absl::flat_hash_set<int> component_set;
1527 for (
const std::vector<int>& component : space_components) {
1528 Rectangle bin_bounding_box = occupiable_space[component[0]];
1529 for (
int i = 1;
i < component.size(); ++
i) {
1532 std::optional<Rectangle> reachable_area_bounding_box;
1534 for (
int idx : component) {
1535 bin.
bin_area.push_back(occupiable_space[idx]);
1537 for (
int i = 0;
i < non_fixed_boxes.size();
i++) {
1538 if (!non_fixed_boxes[
i].bounding_area.IsDisjoint(bin_bounding_box)) {
1539 if (reachable_area_bounding_box.has_value()) {
1540 reachable_area_bounding_box->GrowToInclude(
1541 non_fixed_boxes[
i].bounding_area);
1543 reachable_area_bounding_box = non_fixed_boxes[
i].bounding_area;
1549 result.
bins.pop_back();
1556 VLOG_EVERY_N_SEC(1, 1) <<
"Detected a bin packing problem with "
1557 << result.
bins.size()
1558 <<
" bins. Original problem sizes: "
1559 << non_fixed_boxes.size() <<
" non-fixed boxes, "
1560 << 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)
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