diff --git a/Libraries/LibGfx/DisjointRectSet.cpp b/Libraries/LibGfx/DisjointRectSet.cpp index 2ca643b85b..b68f4cab05 100644 --- a/Libraries/LibGfx/DisjointRectSet.cpp +++ b/Libraries/LibGfx/DisjointRectSet.cpp @@ -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; +} + } diff --git a/Libraries/LibGfx/DisjointRectSet.h b/Libraries/LibGfx/DisjointRectSet.h index 5844d7bf75..a54ce44ce3 100644 --- a/Libraries/LibGfx/DisjointRectSet.h +++ b/Libraries/LibGfx/DisjointRectSet.h @@ -27,20 +27,119 @@ #pragma once #include +#include #include namespace Gfx { class DisjointRectSet { public: - DisjointRectSet() {} - ~DisjointRectSet() {} - DisjointRectSet(DisjointRectSet&& other) - : m_rects(move(other.m_rects)) + DisjointRectSet(const DisjointRectSet&) = delete; + DisjointRectSet& operator=(const DisjointRectSet&) = delete; + + DisjointRectSet() { } + ~DisjointRectSet() { } + + 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 + 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 + 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 + 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& rects() const { return m_rects; } + Vector take_rects() { return move(m_rects); } private: + bool add_no_shatter(const IntRect&); void shatter(); Vector m_rects;