1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:27:35 +00:00

LibGfx: Add a few convenience methods to DisjointRectSet

This commit is contained in:
Tom 2020-08-04 23:01:10 -06:00 committed by Andreas Kling
parent 5b9d563b4b
commit 790eacfbd1
2 changed files with 220 additions and 9 deletions

View file

@ -28,16 +28,17 @@
namespace Gfx {
void DisjointRectSet::add(const IntRect& new_rect)
bool DisjointRectSet::add_no_shatter(const IntRect& new_rect)
{
if (new_rect.is_empty())
return false;
for (auto& rect : m_rects) {
if (rect.contains(new_rect))
return;
return false;
}
m_rects.append(new_rect);
if (m_rects.size() > 1)
shatter();
return true;
}
void DisjointRectSet::shatter()
@ -72,4 +73,113 @@ void DisjointRectSet::shatter()
} while (pass_had_intersections);
}
void DisjointRectSet::move_by(int dx, int dy)
{
for (auto& r : m_rects)
r.move_by(dx, dy);
}
bool DisjointRectSet::contains(const IntRect& rect) const
{
if (is_empty() || rect.is_empty())
return false;
// TODO: This could use some optimization
DisjointRectSet remainder(rect);
for (auto& r : m_rects) {
auto shards = remainder.shatter(r);
if (shards.is_empty())
return true;
remainder = move(shards);
}
return false;
}
bool DisjointRectSet::intersects(const IntRect& rect) const
{
for (auto& r : m_rects) {
if (r.intersects(rect))
return true;
}
return false;
}
bool DisjointRectSet::intersects(const DisjointRectSet& rects) const
{
if (this == &rects)
return true;
for (auto& r : m_rects) {
for (auto& r2 : rects.m_rects) {
if (r.intersects(r2))
return true;
}
}
return false;
}
DisjointRectSet DisjointRectSet::intersected(const IntRect& rect) const
{
DisjointRectSet intersected_rects;
intersected_rects.m_rects.ensure_capacity(m_rects.capacity());
for (auto& r : m_rects) {
auto intersected_rect = r.intersected(rect);
if (!intersected_rect.is_empty())
intersected_rects.m_rects.append(intersected_rect);
}
// Since there should be no overlaps, we don't need to call shatter()
return intersected_rects;
}
DisjointRectSet DisjointRectSet::intersected(const DisjointRectSet& rects) const
{
if (&rects == this)
return clone();
if (is_empty() || rects.is_empty())
return {};
DisjointRectSet intersected_rects;
intersected_rects.m_rects.ensure_capacity(m_rects.capacity());
for (auto& r : m_rects) {
for (auto& r2 : rects.m_rects) {
auto intersected_rect = r.intersected(r2);
if (!intersected_rect.is_empty())
intersected_rects.m_rects.append(intersected_rect);
}
}
// Since there should be no overlaps, we don't need to call shatter()
return intersected_rects;
}
DisjointRectSet DisjointRectSet::shatter(const IntRect& hammer) const
{
if (hammer.is_empty())
return clone();
DisjointRectSet shards;
for (auto& rect : m_rects) {
for (auto& shard : rect.shatter(hammer))
shards.add_no_shatter(shard);
}
// Since there should be no overlaps, we don't need to call shatter()
return shards;
}
DisjointRectSet DisjointRectSet::shatter(const DisjointRectSet& hammer) const
{
if (this == &hammer)
return {};
if (hammer.is_empty() || !intersects(hammer))
return clone();
// TODO: This could use some optimization
auto shards = clone();
for (auto& hammer_rect : hammer.m_rects) {
auto shattered = shards.shatter(hammer_rect);
shards = move(shattered);
}
// Since there should be no overlaps, we don't need to call shatter()
return shards;
}
}

View file

@ -27,20 +27,119 @@
#pragma once
#include <AK/Vector.h>
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
namespace Gfx {
class DisjointRectSet {
public:
DisjointRectSet(const DisjointRectSet&) = delete;
DisjointRectSet& operator=(const DisjointRectSet&) = delete;
DisjointRectSet() { }
~DisjointRectSet() { }
DisjointRectSet(DisjointRectSet&& other)
: m_rects(move(other.m_rects))
DisjointRectSet(const IntRect& rect)
{
m_rects.append(rect);
}
void add(const IntRect&);
DisjointRectSet(DisjointRectSet&&) = default;
DisjointRectSet& operator=(DisjointRectSet&&) = default;
DisjointRectSet clone() const
{
DisjointRectSet rects;
rects.m_rects = m_rects;
return rects;
}
void move_by(int dx, int dy);
void move_by(const IntPoint& delta)
{
move_by(delta.x(), delta.y());
}
void add(const IntRect& rect)
{
if (add_no_shatter(rect) && m_rects.size() > 1)
shatter();
}
template<typename Container>
void add_many(const Container& rects)
{
bool added = false;
for (const auto& rect : rects) {
if (add_no_shatter(rect))
added = true;
}
if (added && m_rects.size() > 1)
shatter();
}
void add(const DisjointRectSet& rect_set)
{
if (this == &rect_set)
return;
if (m_rects.is_empty()) {
m_rects = rect_set.m_rects;
} else {
add_many(rect_set.rects());
}
}
DisjointRectSet shatter(const IntRect&) const;
DisjointRectSet shatter(const DisjointRectSet& hammer) const;
bool contains(const IntRect&) const;
bool intersects(const IntRect&) const;
bool intersects(const DisjointRectSet&) const;
DisjointRectSet intersected(const IntRect&) const;
DisjointRectSet intersected(const DisjointRectSet&) const;
template<typename Function>
IterationDecision for_each_intersected(const IntRect& rect, Function f) const
{
if (is_empty() || rect.is_empty())
return IterationDecision::Continue;
for (auto& r : m_rects) {
auto intersected_rect = r.intersected(rect);
if (intersected_rect.is_empty())
continue;
IterationDecision decision = f(intersected_rect);
if (decision != IterationDecision::Continue)
return decision;
}
return IterationDecision::Continue;
}
template<typename Function>
IterationDecision for_each_intersected(const DisjointRectSet& rects, Function f) const
{
if (is_empty() || rects.is_empty())
return IterationDecision::Continue;
if (this == &rects) {
for (auto& r : m_rects) {
IterationDecision decision = f(r);
if (decision != IterationDecision::Continue)
return decision;
}
} else {
for (auto& r : m_rects) {
for (auto& r2 : rects.m_rects) {
auto intersected_rect = r.intersected(r2);
if (intersected_rect.is_empty())
continue;
IterationDecision decision = f(intersected_rect);
if (decision != IterationDecision::Continue)
return decision;
}
}
}
return IterationDecision::Continue;
}
bool is_empty() const { return m_rects.is_empty(); }
size_t size() const { return m_rects.size(); }
@ -48,8 +147,10 @@ public:
void clear() { m_rects.clear(); }
void clear_with_capacity() { m_rects.clear_with_capacity(); }
const Vector<IntRect, 32>& rects() const { return m_rects; }
Vector<IntRect, 32> take_rects() { return move(m_rects); }
private:
bool add_no_shatter(const IntRect&);
void shatter();
Vector<IntRect, 32> m_rects;