1/**************************************************************************/
2/* rect2.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef RECT2_H
32#define RECT2_H
33
34#include "core/error/error_macros.h"
35#include "core/math/vector2.h"
36
37class String;
38struct Rect2i;
39struct Transform2D;
40
41struct _NO_DISCARD_ Rect2 {
42 Point2 position;
43 Size2 size;
44
45 const Vector2 &get_position() const { return position; }
46 void set_position(const Vector2 &p_pos) { position = p_pos; }
47 const Vector2 &get_size() const { return size; }
48 void set_size(const Vector2 &p_size) { size = p_size; }
49
50 real_t get_area() const { return size.width * size.height; }
51
52 _FORCE_INLINE_ Vector2 get_center() const { return position + (size * 0.5f); }
53
54 inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const {
55#ifdef MATH_CHECKS
56 if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
57 ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
58 }
59#endif
60 if (p_include_borders) {
61 if (position.x > (p_rect.position.x + p_rect.size.width)) {
62 return false;
63 }
64 if ((position.x + size.width) < p_rect.position.x) {
65 return false;
66 }
67 if (position.y > (p_rect.position.y + p_rect.size.height)) {
68 return false;
69 }
70 if ((position.y + size.height) < p_rect.position.y) {
71 return false;
72 }
73 } else {
74 if (position.x >= (p_rect.position.x + p_rect.size.width)) {
75 return false;
76 }
77 if ((position.x + size.width) <= p_rect.position.x) {
78 return false;
79 }
80 if (position.y >= (p_rect.position.y + p_rect.size.height)) {
81 return false;
82 }
83 if ((position.y + size.height) <= p_rect.position.y) {
84 return false;
85 }
86 }
87
88 return true;
89 }
90
91 inline real_t distance_to(const Vector2 &p_point) const {
92#ifdef MATH_CHECKS
93 if (unlikely(size.x < 0 || size.y < 0)) {
94 ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
95 }
96#endif
97 real_t dist = 0.0;
98 bool inside = true;
99
100 if (p_point.x < position.x) {
101 real_t d = position.x - p_point.x;
102 dist = d;
103 inside = false;
104 }
105 if (p_point.y < position.y) {
106 real_t d = position.y - p_point.y;
107 dist = inside ? d : MIN(dist, d);
108 inside = false;
109 }
110 if (p_point.x >= (position.x + size.x)) {
111 real_t d = p_point.x - (position.x + size.x);
112 dist = inside ? d : MIN(dist, d);
113 inside = false;
114 }
115 if (p_point.y >= (position.y + size.y)) {
116 real_t d = p_point.y - (position.y + size.y);
117 dist = inside ? d : MIN(dist, d);
118 inside = false;
119 }
120
121 if (inside) {
122 return 0;
123 } else {
124 return dist;
125 }
126 }
127
128 bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const;
129
130 bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = nullptr, Point2 *r_normal = nullptr) const;
131
132 inline bool encloses(const Rect2 &p_rect) const {
133#ifdef MATH_CHECKS
134 if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
135 ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
136 }
137#endif
138 return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) &&
139 ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) &&
140 ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));
141 }
142
143 _FORCE_INLINE_ bool has_area() const {
144 return size.x > 0.0f && size.y > 0.0f;
145 }
146
147 // Returns the intersection between two Rect2s or an empty Rect2 if there is no intersection.
148 inline Rect2 intersection(const Rect2 &p_rect) const {
149 Rect2 new_rect = p_rect;
150
151 if (!intersects(new_rect)) {
152 return Rect2();
153 }
154
155 new_rect.position.x = MAX(p_rect.position.x, position.x);
156 new_rect.position.y = MAX(p_rect.position.y, position.y);
157
158 Point2 p_rect_end = p_rect.position + p_rect.size;
159 Point2 end = position + size;
160
161 new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x;
162 new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y;
163
164 return new_rect;
165 }
166
167 inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect
168#ifdef MATH_CHECKS
169 if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
170 ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
171 }
172#endif
173 Rect2 new_rect;
174
175 new_rect.position.x = MIN(p_rect.position.x, position.x);
176 new_rect.position.y = MIN(p_rect.position.y, position.y);
177
178 new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x);
179 new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y);
180
181 new_rect.size = new_rect.size - new_rect.position; // Make relative again.
182
183 return new_rect;
184 }
185
186 inline bool has_point(const Point2 &p_point) const {
187#ifdef MATH_CHECKS
188 if (unlikely(size.x < 0 || size.y < 0)) {
189 ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
190 }
191#endif
192 if (p_point.x < position.x) {
193 return false;
194 }
195 if (p_point.y < position.y) {
196 return false;
197 }
198
199 if (p_point.x >= (position.x + size.x)) {
200 return false;
201 }
202 if (p_point.y >= (position.y + size.y)) {
203 return false;
204 }
205
206 return true;
207 }
208
209 bool is_equal_approx(const Rect2 &p_rect) const;
210 bool is_finite() const;
211
212 bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
213 bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }
214
215 inline Rect2 grow(real_t p_amount) const {
216 Rect2 g = *this;
217 g.grow_by(p_amount);
218 return g;
219 }
220
221 inline void grow_by(real_t p_amount) {
222 position.x -= p_amount;
223 position.y -= p_amount;
224 size.width += p_amount * 2;
225 size.height += p_amount * 2;
226 }
227
228 inline Rect2 grow_side(Side p_side, real_t p_amount) const {
229 Rect2 g = *this;
230 g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0,
231 (SIDE_TOP == p_side) ? p_amount : 0,
232 (SIDE_RIGHT == p_side) ? p_amount : 0,
233 (SIDE_BOTTOM == p_side) ? p_amount : 0);
234 return g;
235 }
236
237 inline Rect2 grow_side_bind(uint32_t p_side, real_t p_amount) const {
238 return grow_side(Side(p_side), p_amount);
239 }
240
241 inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const {
242 Rect2 g = *this;
243 g.position.x -= p_left;
244 g.position.y -= p_top;
245 g.size.width += p_left + p_right;
246 g.size.height += p_top + p_bottom;
247
248 return g;
249 }
250
251 _FORCE_INLINE_ Rect2 expand(const Vector2 &p_vector) const {
252 Rect2 r = *this;
253 r.expand_to(p_vector);
254 return r;
255 }
256
257 inline void expand_to(const Vector2 &p_vector) { // In place function for speed.
258#ifdef MATH_CHECKS
259 if (unlikely(size.x < 0 || size.y < 0)) {
260 ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
261 }
262#endif
263 Vector2 begin = position;
264 Vector2 end = position + size;
265
266 if (p_vector.x < begin.x) {
267 begin.x = p_vector.x;
268 }
269 if (p_vector.y < begin.y) {
270 begin.y = p_vector.y;
271 }
272
273 if (p_vector.x > end.x) {
274 end.x = p_vector.x;
275 }
276 if (p_vector.y > end.y) {
277 end.y = p_vector.y;
278 }
279
280 position = begin;
281 size = end - begin;
282 }
283
284 _FORCE_INLINE_ Rect2 abs() const {
285 return Rect2(Point2(position.x + MIN(size.x, (real_t)0), position.y + MIN(size.y, (real_t)0)), size.abs());
286 }
287
288 Vector2 get_support(const Vector2 &p_normal) const {
289 Vector2 half_extents = size * 0.5f;
290 Vector2 ofs = position + half_extents;
291 return Vector2(
292 (p_normal.x > 0) ? -half_extents.x : half_extents.x,
293 (p_normal.y > 0) ? -half_extents.y : half_extents.y) +
294 ofs;
295 }
296
297 _FORCE_INLINE_ bool intersects_filled_polygon(const Vector2 *p_points, int p_point_count) const {
298 Vector2 center = get_center();
299 int side_plus = 0;
300 int side_minus = 0;
301 Vector2 end = position + size;
302
303 int i_f = p_point_count - 1;
304 for (int i = 0; i < p_point_count; i++) {
305 const Vector2 &a = p_points[i_f];
306 const Vector2 &b = p_points[i];
307 i_f = i;
308
309 Vector2 r = (b - a);
310 float l = r.length();
311 if (l == 0.0f) {
312 continue;
313 }
314
315 // Check inside.
316 Vector2 tg = r.orthogonal();
317 float s = tg.dot(center) - tg.dot(a);
318 if (s < 0.0f) {
319 side_plus++;
320 } else {
321 side_minus++;
322 }
323
324 // Check ray box.
325 r /= l;
326 Vector2 ir(1.0f / r.x, 1.0f / r.y);
327
328 // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
329 // r.org is origin of ray
330 Vector2 t13 = (position - a) * ir;
331 Vector2 t24 = (end - a) * ir;
332
333 float tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y));
334 float tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y));
335
336 // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us
337 if (tmax < 0 || tmin > tmax || tmin >= l) {
338 continue;
339 }
340
341 return true;
342 }
343
344 if (side_plus * side_minus == 0) {
345 return true; // All inside.
346 } else {
347 return false;
348 }
349 }
350
351 _FORCE_INLINE_ void set_end(const Vector2 &p_end) {
352 size = p_end - position;
353 }
354
355 _FORCE_INLINE_ Vector2 get_end() const {
356 return position + size;
357 }
358
359 operator String() const;
360 operator Rect2i() const;
361
362 Rect2() {}
363 Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) :
364 position(Point2(p_x, p_y)),
365 size(Size2(p_width, p_height)) {
366 }
367 Rect2(const Point2 &p_pos, const Size2 &p_size) :
368 position(p_pos),
369 size(p_size) {
370 }
371};
372
373#endif // RECT2_H
374