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
14namespace gfx {
15
16template<typename T> class PointT;
17template<typename T> class SizeT;
18template<typename T> class BorderT;
19
20// A rectangle.
21template<typename T>
22class RectT {
23public:
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
416typedef RectT<int> Rect;
417typedef RectT<double> RectF;
418
419} // namespace gfx
420
421#ifdef _DEBUG
422#include "gfx/rect_io.h"
423#endif
424
425#endif
426