Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#pragma once
28
29#include <AK/LogStream.h>
30#include <AK/String.h>
31#include <LibGfx/FloatPoint.h>
32#include <LibGfx/FloatSize.h>
33#include <LibGfx/Orientation.h>
34#include <LibGfx/Rect.h>
35#include <LibGfx/TextAlignment.h>
36#include <math.h>
37
38namespace Gfx {
39
40class FloatRect {
41public:
42 FloatRect() {}
43 FloatRect(float x, float y, float width, float height)
44 : m_location(x, y)
45 , m_size(width, height)
46 {
47 }
48 FloatRect(const FloatPoint& location, const FloatSize& size)
49 : m_location(location)
50 , m_size(size)
51 {
52 }
53 FloatRect(const FloatRect& other)
54 : m_location(other.m_location)
55 , m_size(other.m_size)
56 {
57 }
58
59 bool is_null() const
60 {
61 return width() == 0 && height() == 0;
62 }
63
64 bool is_empty() const
65 {
66 return width() <= 0 || height() <= 0;
67 }
68
69 void move_by(float dx, float dy)
70 {
71 m_location.move_by(dx, dy);
72 }
73
74 void move_by(const FloatPoint& delta)
75 {
76 m_location.move_by(delta);
77 }
78
79 FloatPoint center() const
80 {
81 return { x() + width() / 2, y() + height() / 2 };
82 }
83
84 void set_location(const FloatPoint& location)
85 {
86 m_location = location;
87 }
88
89 void set_size(const FloatSize& size)
90 {
91 m_size = size;
92 }
93
94 void set_size(float width, float height)
95 {
96 m_size.set_width(width);
97 m_size.set_height(height);
98 }
99
100 void inflate(float w, float h)
101 {
102 set_x(x() - w / 2);
103 set_width(width() + w);
104 set_y(y() - h / 2);
105 set_height(height() + h);
106 }
107
108 void shrink(float w, float h)
109 {
110 set_x(x() + w / 2);
111 set_width(width() - w);
112 set_y(y() + h / 2);
113 set_height(height() - h);
114 }
115
116 FloatRect shrunken(float w, float h) const
117 {
118 FloatRect rect = *this;
119 rect.shrink(w, h);
120 return rect;
121 }
122
123 FloatRect inflated(float w, float h) const
124 {
125 FloatRect rect = *this;
126 rect.inflate(w, h);
127 return rect;
128 }
129
130 FloatRect translated(float dx, float dy) const
131 {
132 FloatRect rect = *this;
133 rect.move_by(dx, dy);
134 return rect;
135 }
136
137 FloatRect translated(const FloatPoint& delta) const
138 {
139 FloatRect rect = *this;
140 rect.move_by(delta);
141 return rect;
142 }
143
144 bool contains_vertically(float y) const
145 {
146 return y >= top() && y <= bottom();
147 }
148
149 bool contains_horizontally(float x) const
150 {
151 return x >= left() && x <= right();
152 }
153
154 bool contains(float x, float y) const
155 {
156 return x >= m_location.x() && x <= right() && y >= m_location.y() && y <= bottom();
157 }
158
159 bool contains(const FloatPoint& point) const
160 {
161 return contains(point.x(), point.y());
162 }
163
164 bool contains(const FloatRect& other) const
165 {
166 return left() <= other.left()
167 && right() >= other.right()
168 && top() <= other.top()
169 && bottom() >= other.bottom();
170 }
171
172 float primary_offset_for_orientation(Orientation orientation) const { return m_location.primary_offset_for_orientation(orientation); }
173 void set_primary_offset_for_orientation(Orientation orientation, float value) { m_location.set_primary_offset_for_orientation(orientation, value); }
174 float secondary_offset_for_orientation(Orientation orientation) const { return m_location.secondary_offset_for_orientation(orientation); }
175 void set_secondary_offset_for_orientation(Orientation orientation, float value) { m_location.set_secondary_offset_for_orientation(orientation, value); }
176
177 float primary_size_for_orientation(Orientation orientation) const { return m_size.primary_size_for_orientation(orientation); }
178 float secondary_size_for_orientation(Orientation orientation) const { return m_size.secondary_size_for_orientation(orientation); }
179 void set_primary_size_for_orientation(Orientation orientation, float value) { m_size.set_primary_size_for_orientation(orientation, value); }
180 void set_secondary_size_for_orientation(Orientation orientation, float value) { m_size.set_secondary_size_for_orientation(orientation, value); }
181
182 float first_edge_for_orientation(Orientation orientation) const
183 {
184 if (orientation == Orientation::Vertical)
185 return top();
186 return left();
187 }
188
189 float last_edge_for_orientation(Orientation orientation) const
190 {
191 if (orientation == Orientation::Vertical)
192 return bottom();
193 return right();
194 }
195
196 float left() const { return x(); }
197 float right() const { return x() + width() - 1; }
198 float top() const { return y(); }
199 float bottom() const { return y() + height() - 1; }
200
201 void set_left(float left)
202 {
203 set_x(left);
204 }
205
206 void set_top(float top)
207 {
208 set_y(top);
209 }
210
211 void set_right(float right)
212 {
213 set_width(right - x() + 1);
214 }
215
216 void set_bottom(float bottom)
217 {
218 set_height(bottom - y() + 1);
219 }
220
221 void set_right_without_resize(float new_right)
222 {
223 float delta = new_right - right();
224 move_by(delta, 0);
225 }
226
227 void set_bottom_without_resize(float new_bottom)
228 {
229 float delta = new_bottom - bottom();
230 move_by(0, delta);
231 }
232
233 bool intersects(const FloatRect& other) const
234 {
235 return left() <= other.right()
236 && other.left() <= right()
237 && top() <= other.bottom()
238 && other.top() <= bottom();
239 }
240
241 float x() const { return location().x(); }
242 float y() const { return location().y(); }
243 float width() const { return m_size.width(); }
244 float height() const { return m_size.height(); }
245
246 void set_x(float x) { m_location.set_x(x); }
247 void set_y(float y) { m_location.set_y(y); }
248 void set_width(float width) { m_size.set_width(width); }
249 void set_height(float height) { m_size.set_height(height); }
250
251 FloatPoint location() const { return m_location; }
252 FloatSize size() const { return m_size; }
253
254 Vector<FloatRect, 4> shatter(const FloatRect& hammer) const;
255
256 bool operator==(const FloatRect& other) const
257 {
258 return m_location == other.m_location
259 && m_size == other.m_size;
260 }
261
262 void intersect(const FloatRect&);
263
264 static FloatRect intersection(const FloatRect& a, const FloatRect& b)
265 {
266 FloatRect r(a);
267 r.intersect(b);
268 return r;
269 }
270
271 FloatRect intersected(const FloatRect& other) const
272 {
273 return intersection(*this, other);
274 }
275
276 FloatRect united(const FloatRect&) const;
277
278 FloatPoint top_left() const { return { left(), top() }; }
279 FloatPoint top_right() const { return { right(), top() }; }
280 FloatPoint bottom_left() const { return { left(), bottom() }; }
281 FloatPoint bottom_right() const { return { right(), bottom() }; }
282
283 void align_within(const FloatRect&, TextAlignment);
284
285 void center_within(const FloatRect& other)
286 {
287 center_horizontally_within(other);
288 center_vertically_within(other);
289 }
290
291 void center_horizontally_within(const FloatRect& other)
292 {
293 set_x(other.center().x() - width() / 2);
294 }
295
296 void center_vertically_within(const FloatRect& other)
297 {
298 set_y(other.center().y() - height() / 2);
299 }
300
301 String to_string() const { return String::format("[%g,%g %gx%g]", x(), y(), width(), height()); }
302
303private:
304 FloatPoint m_location;
305 FloatSize m_size;
306};
307
308inline void FloatPoint::constrain(const FloatRect& rect)
309{
310 if (x() < rect.left())
311 set_x(rect.left());
312 else if (x() > rect.right())
313 set_x(rect.right());
314 if (y() < rect.top())
315 set_y(rect.top());
316 else if (y() > rect.bottom())
317 set_y(rect.bottom());
318}
319
320inline const LogStream& operator<<(const LogStream& stream, const FloatRect& value)
321{
322 return stream << value.to_string();
323}
324
325inline Rect enclosing_int_rect(const FloatRect& float_rect)
326{
327 return { (int)float_rect.x(), (int)float_rect.y(), (int)ceilf(float_rect.width()), (int)ceilf(float_rect.height()) };
328}
329
330}