1 | // LAF Gfx Library |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2017 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifndef GFX_RECT_H_INCLUDED |
9 | #define GFX_RECT_H_INCLUDED |
10 | #pragma once |
11 | |
12 | #include <cmath> |
13 | |
14 | namespace gfx { |
15 | |
16 | template<typename T> class PointT; |
17 | template<typename T> class SizeT; |
18 | template<typename T> class BorderT; |
19 | |
20 | // A rectangle. |
21 | template<typename T> |
22 | class RectT { |
23 | public: |
24 | T x, y, w, h; |
25 | |
26 | T x2() const { return x+w; } |
27 | T y2() const { return y+h; } |
28 | |
29 | // Creates a new empty rectangle with the origin in 0,0. |
30 | RectT() : x(0), y(0), w(0), h(0) { |
31 | } |
32 | |
33 | // Creates a new rectangle with the specified size with the origin in 0,0. |
34 | RectT(const T& w, const T& h) : |
35 | x(0), y(0), |
36 | w(w), h(h) { |
37 | } |
38 | |
39 | // Creates a new rectangle with the specified size with the origin in 0,0. |
40 | explicit RectT(const SizeT<T>& size) : |
41 | x(0), y(0), |
42 | w(size.w), h(size.h) { |
43 | } |
44 | |
45 | RectT(const RectT<T>& rect) : |
46 | x(rect.x), y(rect.y), |
47 | w(rect.w), h(rect.h) { |
48 | } |
49 | |
50 | template<typename U> |
51 | RectT(const RectT<U>& rect) : |
52 | x(static_cast<T>(rect.x)), y(static_cast<T>(rect.y)), |
53 | w(static_cast<T>(rect.w)), h(static_cast<T>(rect.h)) { |
54 | } |
55 | |
56 | RectT(const PointT<T>& point, const SizeT<T>& size) : |
57 | x(point.x), y(point.y), |
58 | w(size.w), h(size.h) { |
59 | } |
60 | |
61 | // Creates a new rectangle with the origin in point1 and size |
62 | // equal to point2-point1. |
63 | // |
64 | // If a coordinate of point1 is greater than point2, the coordinates |
65 | // are swapped. The resulting rectangle will be: |
66 | // |
67 | // x = min(point1.x, point2.x) |
68 | // y = min(point1.y, point2.y) |
69 | // w = max(point1.x, point2.x) - x |
70 | // h = max(point1.x, point2.x) - y |
71 | // |
72 | // See that point2 isn't included in the rectangle, it's like the |
73 | // point returned by point2() member function. |
74 | RectT(const PointT<T>& point1, const PointT<T>& point2) { |
75 | PointT<T> leftTop = point1; |
76 | PointT<T> rightBottom = point2; |
77 | T t; |
78 | |
79 | if (leftTop.x > rightBottom.x) { |
80 | t = leftTop.x; |
81 | leftTop.x = rightBottom.x; |
82 | rightBottom.x = t; |
83 | } |
84 | |
85 | if (leftTop.y > rightBottom.y) { |
86 | t = leftTop.y; |
87 | leftTop.y = rightBottom.y; |
88 | rightBottom.y = t; |
89 | } |
90 | |
91 | this->x = leftTop.x; |
92 | this->y = leftTop.y; |
93 | this->w = rightBottom.x - leftTop.x; |
94 | this->h = rightBottom.y - leftTop.y; |
95 | } |
96 | |
97 | RectT(const T& x, const T& y, const T& w, const T& h) : x(x), y(y), w(w), h(h) { |
98 | } |
99 | |
100 | // Verifies if the width and/or height of the rectangle are less or |
101 | // equal than zero. |
102 | bool isEmpty() const { |
103 | return (w <= 0 || h <= 0); |
104 | } |
105 | |
106 | // Returns the middle point of the rectangle (x+w/2, y+h/2). |
107 | PointT<T> center() const { |
108 | return PointT<T>(x+w/2, y+h/2); |
109 | } |
110 | |
111 | // Returns the point in the upper-left corner (that is inside the |
112 | // rectangle). |
113 | PointT<T> origin() const { |
114 | return PointT<T>(x, y); |
115 | } |
116 | |
117 | // Returns point in the lower-right corner that is outside the |
118 | // rectangle (x+w, y+h). |
119 | PointT<T> point2() const { |
120 | return PointT<T>(x+w, y+h); |
121 | } |
122 | |
123 | SizeT<T> size() const { |
124 | return SizeT<T>(w, h); |
125 | } |
126 | |
127 | RectT& setOrigin(const PointT<T>& pt) { |
128 | x = pt.x; |
129 | y = pt.y; |
130 | return *this; |
131 | } |
132 | |
133 | RectT& setSize(const SizeT<T>& sz) { |
134 | w = sz.w; |
135 | h = sz.h; |
136 | return *this; |
137 | } |
138 | |
139 | // Moves the rectangle origin in the specified delta. |
140 | RectT& offset(const T& dx, const T& dy) { |
141 | x += dx; |
142 | y += dy; |
143 | return *this; |
144 | } |
145 | |
146 | template<typename U> |
147 | RectT& offset(const PointT<U>& delta) { |
148 | x += delta.x; |
149 | y += delta.y; |
150 | return *this; |
151 | } |
152 | |
153 | RectT& inflate(const T& delta) { |
154 | w += delta; |
155 | h += delta; |
156 | return *this; |
157 | } |
158 | |
159 | RectT& inflate(const T& dw, const T& dh) { |
160 | w += dw; |
161 | h += dh; |
162 | return *this; |
163 | } |
164 | |
165 | RectT& inflate(const SizeT<T>& delta) { |
166 | w += delta.w; |
167 | h += delta.h; |
168 | return *this; |
169 | } |
170 | |
171 | RectT& enlarge(const T& unit) { |
172 | x -= unit; |
173 | y -= unit; |
174 | w += unit*2; |
175 | h += unit*2; |
176 | return *this; |
177 | } |
178 | |
179 | RectT& enlarge(const BorderT<T>& br) { |
180 | x -= br.left(); |
181 | y -= br.top(); |
182 | w += br.left() + br.right(); |
183 | h += br.top() + br.bottom(); |
184 | return *this; |
185 | } |
186 | |
187 | RectT& enlargeXW(const T& unit) { |
188 | x -= unit; |
189 | w += unit*2; |
190 | return *this; |
191 | } |
192 | |
193 | RectT& enlargeYH(const T& unit) { |
194 | y -= unit; |
195 | h += unit*2; |
196 | return *this; |
197 | } |
198 | |
199 | RectT& shrink(const T& unit) { |
200 | x += unit; |
201 | y += unit; |
202 | w -= unit*2; |
203 | h -= unit*2; |
204 | return *this; |
205 | } |
206 | |
207 | RectT& shrink(const BorderT<T>& br) { |
208 | x += br.left(); |
209 | y += br.top(); |
210 | w -= br.left() + br.right(); |
211 | h -= br.top() + br.bottom(); |
212 | return *this; |
213 | } |
214 | |
215 | // Adjusts the x/y floating-point coordinates to the left most/top |
216 | // most near integer respectively (instead of rounding towards |
217 | // zero). Basically applies the std::floor() function to the origin |
218 | // of the rectangle. |
219 | // |
220 | // Generally only useful to convert a gfx::RectF to a gfx::Rect as |
221 | // an alternative to gfx::Rect(gfx::RectF(...)). |
222 | // |
223 | // Example with floor(): |
224 | // |
225 | // gfx::RectF boundsF(-0.25, -0.75, 1, 2); |
226 | // gfx::Rect bounds = boundsF.floor(); |
227 | // ASSERT(bounds == gfx::Rect(-1, -1, 1, 2)); |
228 | // |
229 | // Without floor(), using the Rect(RectF()) ctor converting floating |
230 | // point to integer directly: |
231 | // |
232 | // gfx::RectF boundsF(-0.25, -0.75, 1, 2); |
233 | // gfx::Rect bounds = boundsF; |
234 | // ASSERT(bounds == gfx::Rect(0, 0, 1, 2)); |
235 | // |
236 | RectT& floor() { |
237 | x = std::floor(x); |
238 | y = std::floor(y); |
239 | return *this; |
240 | } |
241 | |
242 | // Returns true if this rectangle encloses the pt point. |
243 | bool contains(const PointT<T>& pt) const { |
244 | return |
245 | pt.x >= x && pt.x < x+w && |
246 | pt.y >= y && pt.y < y+h; |
247 | } |
248 | |
249 | bool contains(const T& u, const T& v) const { |
250 | return |
251 | u >= x && u < x+w && |
252 | v >= y && v < y+h; |
253 | } |
254 | |
255 | // Returns true if this rectangle entirely contains the rc rectangle. |
256 | bool contains(const RectT& rc) const { |
257 | if (isEmpty() || rc.isEmpty()) |
258 | return false; |
259 | |
260 | return |
261 | rc.x >= x && rc.x+rc.w <= x+w && |
262 | rc.y >= y && rc.y+rc.h <= y+h; |
263 | } |
264 | |
265 | // Returns true if the intersection between this rectangle with rc |
266 | // rectangle is not empty. |
267 | bool intersects(const RectT& rc) const { |
268 | if (isEmpty() || rc.isEmpty()) |
269 | return false; |
270 | |
271 | return |
272 | rc.x < x+w && rc.x+rc.w > x && |
273 | rc.y < y+h && rc.y+rc.h > y; |
274 | } |
275 | |
276 | // Returns the union rectangle between this and rc rectangle. |
277 | RectT createUnion(const RectT& rc) const { |
278 | if (isEmpty()) |
279 | return rc; |
280 | else if (rc.isEmpty()) |
281 | return *this; |
282 | else |
283 | return RectT(PointT<T>(x < rc.x ? x: rc.x, |
284 | y < rc.y ? y: rc.y), |
285 | PointT<T>(x+w > rc.x+rc.w ? x+w: rc.x+rc.w, |
286 | y+h > rc.y+rc.h ? y+h: rc.y+rc.h)); |
287 | } |
288 | |
289 | // Returns the intersection rectangle between this and rc rectangles. |
290 | RectT createIntersection(const RectT& rc) const { |
291 | if (intersects(rc)) |
292 | return RectT(PointT<T>(x > rc.x ? x: rc.x, |
293 | y > rc.y ? y: rc.y), |
294 | PointT<T>(x+w < rc.x+rc.w ? x+w: rc.x+rc.w, |
295 | y+h < rc.y+rc.h ? y+h: rc.y+rc.h)); |
296 | else |
297 | return RectT(); |
298 | } |
299 | |
300 | const RectT& operator+=(const BorderT<T>& br) { |
301 | enlarge(br); |
302 | return *this; |
303 | } |
304 | |
305 | const RectT& operator-=(const BorderT<T>& br) { |
306 | shrink(br); |
307 | return *this; |
308 | } |
309 | |
310 | RectT& operator*=(const T factor) { |
311 | x *= factor; |
312 | y *= factor; |
313 | w *= factor; |
314 | h *= factor; |
315 | return *this; |
316 | } |
317 | |
318 | RectT& operator/=(const T factor) { |
319 | x /= factor; |
320 | y /= factor; |
321 | w /= factor; |
322 | h /= factor; |
323 | return *this; |
324 | } |
325 | |
326 | RectT& operator*=(const SizeT<T>& size) { |
327 | x *= size.w; |
328 | y *= size.h; |
329 | w *= size.w; |
330 | h *= size.h; |
331 | return *this; |
332 | } |
333 | |
334 | RectT& operator/=(const SizeT<T>& size) { |
335 | x /= size.w; |
336 | y /= size.h; |
337 | w /= size.w; |
338 | h /= size.h; |
339 | return *this; |
340 | } |
341 | |
342 | const RectT& operator|=(const RectT& rc) { |
343 | return *this = createUnion(rc); |
344 | } |
345 | |
346 | const RectT& operator&=(const RectT& rc) { |
347 | return *this = createIntersection(rc); |
348 | } |
349 | |
350 | RectT operator+(const BorderT<T>& br) const { |
351 | return RectT(*this).enlarge(br); |
352 | } |
353 | |
354 | RectT operator-(const BorderT<T>& br) const { |
355 | return RectT(*this).shrink(br); |
356 | } |
357 | |
358 | RectT operator|(const RectT& other) const { |
359 | return createUnion(other); |
360 | } |
361 | |
362 | RectT operator&(const RectT& other) const { |
363 | return createIntersection(other); |
364 | } |
365 | |
366 | RectT operator*(const T factor) const { |
367 | return RectT(x*factor, y*factor, |
368 | w*factor, h*factor); |
369 | } |
370 | |
371 | RectT operator*(const SizeT<T>& size) const { |
372 | return RectT(x*size.w, y*size.h, |
373 | w*size.w, h*size.h); |
374 | } |
375 | |
376 | RectT operator/(const T scale) const { |
377 | return RectT(x/scale, y/scale, |
378 | w/scale, h/scale); |
379 | } |
380 | |
381 | RectT operator/(const SizeT<T>& size) const { |
382 | return RectT(x/size.w, y/size.h, |
383 | w/size.w, h/size.h); |
384 | } |
385 | |
386 | bool operator==(const RectT& rc) const { |
387 | return |
388 | x == rc.x && w == rc.w && |
389 | y == rc.y && h == rc.h; |
390 | } |
391 | |
392 | bool operator!=(const RectT& rc) const { |
393 | return |
394 | x != rc.x || w != rc.w || |
395 | y != rc.y || h != rc.h; |
396 | } |
397 | |
398 | RectT& fitIn(const RectT& bounds) { |
399 | if (w < h) { |
400 | w = w * bounds.h / h; |
401 | x = bounds.x + bounds.w/2 - w/2; |
402 | y = bounds.y; |
403 | h = bounds.h; |
404 | } |
405 | else { |
406 | h = h * bounds.w / w; |
407 | y = bounds.y + bounds.h/2 - h/2; |
408 | x = bounds.x; |
409 | w = bounds.w; |
410 | } |
411 | return *this; |
412 | } |
413 | |
414 | }; |
415 | |
416 | typedef RectT<int> Rect; |
417 | typedef RectT<double> RectF; |
418 | |
419 | } // namespace gfx |
420 | |
421 | #ifdef _DEBUG |
422 | #include "gfx/rect_io.h" |
423 | #endif |
424 | |
425 | #endif |
426 | |