2022-04-16 11:41:48 +02:00
|
|
|
//
|
|
|
|
// Created by maximilian on 16.04.22.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "geometry.h"
|
|
|
|
#include "areas.h"
|
2022-04-16 12:02:27 +02:00
|
|
|
#include "segment_tree.h"
|
2022-04-16 11:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
Unit get_area_union(std::vector<Rectangle> rectangles) {
|
|
|
|
// do some input sanity checks
|
|
|
|
for(const auto &rect : rectangles) {
|
|
|
|
assert(rect.bottom_left.x <= rect.top_right.x);
|
|
|
|
assert(rect.bottom_left.y <= rect.top_right.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
//sorted vector of all x coordinates of all rectangles
|
|
|
|
std::vector<RectCoord> x_points;
|
|
|
|
x_points.reserve(2* rectangles.size());
|
|
|
|
for(auto &rect : rectangles) {
|
|
|
|
x_points.push_back({rect.bottom_left.x, &rect});
|
|
|
|
x_points.push_back({rect.top_right.x, &rect});
|
|
|
|
}
|
|
|
|
std::sort(x_points.begin(), x_points.end());
|
|
|
|
|
|
|
|
// preprocessing for constant rectangle lookup during algorithm
|
|
|
|
// technically not necessary to achieve quadratic running time
|
|
|
|
for(Index i = 0 ; i < x_points.size() ; ++i) {
|
|
|
|
if(x_points[i].rect->bottom_left.x == x_points[i].coord) {
|
|
|
|
x_points[i].rect->i_left = i;
|
|
|
|
} else {
|
|
|
|
assert(x_points[i].rect->top_right.x == x_points[i].coord);
|
|
|
|
x_points[i].rect->i_right = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// prepare vector of y-coordinates
|
|
|
|
std::vector<RectCoord> y_points;
|
|
|
|
y_points.reserve(2*rectangles.size());
|
|
|
|
for(auto &rect : rectangles) {
|
|
|
|
y_points.push_back({rect.bottom_left.y, &rect});
|
|
|
|
y_points.push_back({rect.top_right.y, &rect});
|
|
|
|
}
|
|
|
|
std::sort(y_points.begin(), y_points.end());
|
|
|
|
|
2022-04-16 12:02:27 +02:00
|
|
|
// init data and run sweepline
|
2022-04-16 11:41:48 +02:00
|
|
|
Unit total_area = 0;
|
2022-04-16 12:02:27 +02:00
|
|
|
SegmentTree segmentTree(x_points);
|
|
|
|
|
2022-04-16 11:41:48 +02:00
|
|
|
for(Index y_index = 0 ; y_index < y_points.size() ; ++y_index) {
|
|
|
|
// first, update cross section
|
|
|
|
if (y_points[y_index].rect->bottom_left.y == y_points[y_index].coord) {
|
|
|
|
// rectangle is starting now, add its cross section
|
2022-04-16 12:02:27 +02:00
|
|
|
segmentTree.add_interval(
|
|
|
|
{x_points[y_points[y_index].rect->i_left].coord,
|
|
|
|
x_points[y_points[y_index].rect->i_right].coord}
|
|
|
|
);
|
2022-04-16 11:41:48 +02:00
|
|
|
} else {
|
|
|
|
assert(y_points[y_index].rect->top_right.y == y_points[y_index].coord);
|
|
|
|
// rectangle stopping, remove its cross section
|
2022-04-16 12:02:27 +02:00
|
|
|
segmentTree.remove_interval(
|
|
|
|
{x_points[y_points[y_index].rect->i_left].coord,
|
|
|
|
x_points[y_points[y_index].rect->i_right].coord}
|
|
|
|
);
|
2022-04-16 11:41:48 +02:00
|
|
|
}
|
|
|
|
//cross section is now up to date
|
|
|
|
if(y_index + 1 < y_points.size()) {
|
|
|
|
// add area up to next y point
|
2022-04-16 12:02:27 +02:00
|
|
|
total_area += segmentTree.length_covered_intervals();
|
2022-04-16 11:41:48 +02:00
|
|
|
}
|
|
|
|
}
|
2022-04-16 12:02:27 +02:00
|
|
|
// TODO: sanity check that nothing is covered when arriving at the end
|
2022-04-16 11:41:48 +02:00
|
|
|
return total_area;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Rectangle> get_random_instance(unsigned num_rects, Coordinate range) {
|
|
|
|
std::vector<Rectangle> rects;
|
|
|
|
rects.reserve(num_rects);
|
|
|
|
for(unsigned i = 0 ; i < num_rects; ++i) {
|
|
|
|
// technically, this might not be (perfectly) evenly distributed, but it will suffice for our purposes anyways
|
|
|
|
Coordinate x1 = rand() % range;
|
|
|
|
Coordinate x2 = rand() % range;
|
|
|
|
Coordinate y1 = rand() % range;
|
|
|
|
Coordinate y2 = rand() % range;
|
|
|
|
if (x2 < x1) { int tmp = x1; x1 = x2; x2 = tmp; };
|
|
|
|
if (y2 < y1) { int tmp = y1; y1 = y2; y2 = tmp; };
|
|
|
|
rects.push_back(
|
|
|
|
{{x1,y1},{x2, y2}, 0,0}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return rects;
|
|
|
|
}
|