summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibGfx/DisjointRectSet.h
blob: ccb17b30dfce4e0371a64a585910fd37acaa1589 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#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(const IntRect& rect)
    {
        m_rects.append(rect);
    }

    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(); }

    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;
};

}