Skip to content

Instantly share code, notes, and snippets.

@p0358
Created January 11, 2023 00:00
Show Gist options
  • Save p0358/43d7de3c3490eb96eecef3d7155b3233 to your computer and use it in GitHub Desktop.
Save p0358/43d7de3c3490eb96eecef3d7155b3233 to your computer and use it in GitHub Desktop.
Given rectangular area with holes inside, constructs vector of reversed rectangles (ones that are outside of holes and cover the entirety of the remaining area). This algorithm can be used to for example draw some background around windows on the screen, without drawing over the windows.
#include <iostream>
#include <vector>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
class Rectangle {
public:
int x, y, w, h;
Rectangle() : x(0), y(0), w(0), h(0) {}
Rectangle(int X, int Y, int W, int H) : x(X), y(Y), w(W), h(H) {}
/*bool operator<(const Rectangle& other) const {
if (x != other.x) return x < other.x;
if (y != other.y) return y < other.y;
if (w != other.w) return w < other.w;
return h < other.h;
}*/
};
struct AvailableLine
{
int ystart, yend;
};
// It works by "scanning" the canvas from left to right, then making a list of available y lines in given x position (available = outside of holes).
// Then if any existing rectangle ends at previous x and its y-pos equals to one of available lines, we extent the rectangle by 1 to the right;
// otherwise, we create a new rectangle at current x, with initial y-pos and height being that of the new available line.
std::vector<Rectangle> getRemainingRectangles(const Rectangle& main_rect, const std::vector<Rectangle>& hole_rects)
{
auto mx = main_rect.w;
auto my = main_rect.h;
std::vector<Rectangle> result{};
std::vector<Rectangle> current_rects{};
for (int x = 0; x < mx; x++)
{
//current.x += 1;
std::vector<AvailableLine> lines{};
AvailableLine current_line{ 0, 0 };
for (int y = 0; y < my; y++)
{
bool ok = true;
for (auto& hole : hole_rects)
{
if (x >= hole.x && x < (hole.x + hole.w) && y >= hole.y && y < (hole.y + hole.h))
{
ok = false;
std::cout << "x:" << x << " y:" << y <<
" is inside of hole x:" << hole.x << " y:" << hole.y << " w:" << hole.w << " h:" << hole.h << std::endl;
break;
}
}
if (ok)
current_line.yend++;
else
{
if (current_line.ystart != current_line.yend)
lines.push_back(current_line);
current_line.ystart = std::min(y + 1, my);
current_line.yend = current_line.ystart;
std::cout << "Resetting current line at y:" << y << std::endl;
}
}
if (current_line.ystart != current_line.yend)
lines.push_back(current_line);
std::cout << "x: " << std::to_string(x) << std::endl;
for (auto& line : lines)
{
std::cout << "line ystart:" << std::to_string(line.ystart) << " yend:" << std::to_string(line.yend) << std::endl;
}
/*for (auto& rect : current_rects)
{
bool ok = false;
for (auto& line : lines)
{
if (rect.y == line.ystart && (rect.y + rect.h) == line.yend)
{
ok = true;
break;
}
}
if (!ok)
{
// wrap this rectangle
}
}*/
for (auto& line : lines)
{
bool found = false;
for (auto& rect : current_rects)
{
if (rect.y == line.ystart && (rect.y + rect.h) == line.yend && x - 1 == rect.x + rect.w - 1)
{
found = true;
rect.w += 1; // extend this existing rectangle
break;
}
}
if (!found)
{
// time to start a new rectangle
current_rects.push_back(Rectangle{ x, line.ystart, 1, line.yend - line.ystart });
}
}
}
return current_rects;
}
int main() {
Rectangle main{0, 0, 1000, 1000};
std::vector<Rectangle> smalls = {
{100, 100, 50, 50}, //{150, 100, 50, 50},
{200, 200, 50, 50},
{200, 180, 80, 80},
//{100, 100, 50, 50}, {200, 200, 50, 50},
};
std::vector<Rectangle> rectangles = getRemainingRectangles(main, smalls);
std::cout << "Output size: " << std::to_string(rectangles.size()) << std::endl;
for (const Rectangle& rectangle : rectangles) {
std::cout << "x: " << rectangle.x << ", y: " << rectangle.y << ", w: " << rectangle.w << ", h: " << rectangle.h << std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment