4
// rectdecls.h: only brief forward declarations, no include dependencies
5
// rectdefs.h: medium level function definitions with some include dependencies
6
// rect.h: full functionality with more include dependencies
11
template<class T=double>
15
TBorder<T>& operator=(const TBorder<T>& other) = default;
16
bool operator==(const TBorder<T>& other) {
17
return left == other.left && top == other.top &&
18
right == other.right && bottom == other.bottom;
21
bool operator!=(const TBorder<T>& other) {
22
return !operator==(other);
31
typedef TBorder<double> Border;
34
std::ostream& operator<<(std::ostream& s, const TBorder<T>& b){
35
s << "Border(" << b.left << ", " << b.top << ", " << b.right << ", " << b.bottom << ")";
39
// Rectangle template class.
40
// Left and top are included, right and bottom excluded.
41
template<class T=double>
45
TRect(const TRect<T>& other) = default;
46
TRect(T x_, T y_, T w_, T h_) :
47
x(x_), y(y_), w(w_), h(h_)
49
TRect(const TPoint<T>& pt, const TSize<T>& sz) :
50
x(pt.x), y(pt.y), w(sz.w), h(sz.h)
53
TRect<T>& operator=(const TRect<T>& other) = default;
54
bool operator==(const TRect<T>& other) {
55
return x == other.x && y == other.y &&
56
w == other.w && h == other.h;
59
bool operator!=(const TRect<T>& other) {
60
return !operator==(other);
63
T& operator[](size_t index) {
70
default: assert(false); // invalid index
74
// New Rect from two points.
75
// x0 and y0 are considered inside, x1 and y1 are just outside the Rect.
76
static TRect<T> from_extents(T x0, T y0, T x1, T y1)
78
return TRect<T>(x0, y0, x1 - x0, y1 - y0);
81
// New Rect from two tuples.
82
static TRect<T> from_position_size(const TPoint<T>& position, const TSize<T>& size)
84
return TRect<T>(position.x, position.y, size.w, size.h);
87
// New Rect from two points, left-top and right-botton.
88
// The former lies inside, while the latter is considered to be
89
// just outside the rect.
90
static TRect<T> from_points(const TPoint<T>& p0, const TPoint<T>& p1)
92
return TRect<T>(p0.x, p0.y, p1.x - p0.x, p1.y - p0.y);
96
return x, y , x + w, y + h
98
def to_position_size(self):
103
{ return w <= 0 or h <= 0; }
105
TPoint<T> left_top() const
108
TPoint<T> right_bottom() const
109
{ return {right(), bottom()}; }
111
TPoint<T> get_position() const
114
TSize<T> get_size() const
117
TPoint<T> get_center() const
118
{ return {x + w / 2.0, y + h / 2.0}; }
120
T get_center_x() const
121
{ return x + w / 2; }
123
T get_center_y() const
124
{ return y + h / 2; }
138
bool contains(const TPoint<T>& pt) const {
139
return contains(pt.x, pt.y);
142
bool contains(T x_, T y_) const {
143
return this->x <= x_ && this->x + this->w > x_ &&
144
this->y <= y_ && this->y + this->h > y_;
147
TRect<T> round() const {
148
return {std::round(x), std::round(y), std::round(w), std::round(h)};
151
TRect<T> floor() const {
152
return {std::floor(x), std::floor(y), std::floor(w), std::floor(h)};
155
TRect<int> to_int() const {
156
return {int(x), int(y), int(w), int(h)};
159
TRect<T> scale(T k) const {
163
TRect<T> scale(T kx, T ky) const {
164
return {x * kx, y * ky, w * kx, h * ky};
167
// Returns a new Rect displaced by dx and dy.
168
TRect<T> offset(const TVec2<T>& v) const {
169
return offset(v.x, v.y);
172
// Returns a new Rect displaced by dx and dy.
173
TRect<T> offset(T dx, T dy) const {
174
return {x + dx, y + dy, w, h};
177
TRect<T> inflate(T k) const {
178
return inflate(k, k);
181
TRect<T> inflate(const TVec2<T>& v) const {
182
return inflate(v.y, v.x);
185
TRect<T> inflate(T kx, T ky) const {
186
TRect<T> r = {x - kx, y - ky, w + 2 * kx, h + 2 * ky};
190
// add border to rect on all four sides
191
TRect<T> inflate(const TBorder<T>& b) const {
192
return {x - b.left, y - b.top,
193
w + b.left + b.right,
194
h + b.top + b.bottom};
197
TRect<T> deflate(T k) const {
198
return deflate(k, k);
201
TRect<T> deflate(TVec2<T> v) const {
202
return deflate(v.x, v.y);
205
TRect<T> deflate(T kx, T ky) const {
206
TRect<T> r = {x + kx, y + ky, w - 2*kx, h - 2*ky};
210
// remove border from rect on all four sides
211
TRect<T> deflate(const TBorder<T>& b) const {
212
return {x + b.left, y + b.top,
213
w - b.left - b.right,
214
h - b.top - b.bottom};
217
// Returns a new Rect with its size multiplied by k.
218
TRect<T> grow(T k) const
222
TRect<T> grow(T kx, T ky) const
229
// multiply w, h by kx, ky, inplace
230
void grow(TRect<T>& r, T kx) const
234
void grow(TRect<T>& r, T kx, T ky) const
238
r.x = r.x + (r.w - w_) / 2.0;
239
r.y = r.y + (r.h - h_) / 2.0;
244
bool intersects(TRect<T> rect) const
246
return !(x >= rect.x + rect.w ||
248
y >= rect.y + rect.h ||
252
TRect<T> intersection(const TRect<T>&rect) const
254
T x0 = std::max(x, rect.x);
255
T y0 = std::max(y, rect.y);
256
T x1 = std::min(x + w, rect.x + rect.w);
257
T y1 = std::min(y + h, rect.y + rect.h);
258
if (x0 > x1 || y0 > y1)
261
return {x0, y0, x1 - x0, y1 - y0};
264
TRect<T> union_(const TRect<T>& rect) const
266
T x0 = std::min(x, rect.x);
267
T y0 = std::min(y, rect.y);
268
T x1 = std::max(x + w, rect.x + rect.w);
269
T y1 = std::max(y + h, rect.y + rect.h);
270
return {x0, y0, x1 - x0, y1 - y0};
273
// Returns a new Rect with the aspect ratio of self
274
// that fits inside the given rectangle.
275
TRect<T> inscribe_with_aspect(const TRect<T>& rect,
277
double y_align=0.5) const
279
if (empty() || rect.empty())
282
double src_aspect = w / static_cast<double>(h);
283
double dst_aspect = rect.w / static_cast<double>(rect.h);
285
TRect<T> result = rect;
286
if (dst_aspect > src_aspect)
288
result.w = static_cast<T>(rect.h * src_aspect);
289
result.x += static_cast<T>(x_align * (rect.w - result.w));
293
result.h = static_cast<T>(rect.w / src_aspect);
294
result.y += static_cast<T>(y_align * (rect.h - result.h));
299
// Resize self to the aspect ratio of aspect_rect.
300
TRect<T> resize_to_aspect(const TRect<T>& aspect_rect) const
302
if (empty() or aspect_rect.empty())
305
double src_aspect = aspect_rect.w / static_cast<double>(aspect_rect.h);
306
double dst_aspect = w / static_cast<double>(h);
308
TRect<T> result = *this;
309
if (dst_aspect > src_aspect)
310
result.w = static_cast<T>(h * src_aspect);
312
result.h = static_cast<T>(w / src_aspect);
316
// Resize self to get the aspect ratio of aspect_rect, but limited
317
// by the given aspect range.
318
TRect<T> resize_to_aspect_range(const TRect<T>& aspect_rect,
319
T min_aspect, T max_aspect) const
321
(void)min_aspect; // not implemented
323
if (empty() || aspect_rect.empty())
326
TRect<T> r = aspect_rect;
329
double a0 = r.w / static_cast<double>(r.h);
330
double a0_max = a0 * max_aspect;
331
double a1 = w / static_cast<double>(h);
332
double a = std::min(a1, a0_max);
336
return resize_to_aspect(r);
339
// Aligns the given rect inside of self.
340
// x/y_align = 0.5 centers rect.
341
TRect<T> align_rect(const TRect<T>& rect,
342
double x_align = 0.5,
343
double y_align = 0.5) const
345
return {static_cast<T>(this->x + (this->w - rect.w) * x_align),
346
static_cast<T>(this->y + (this->h - rect.h) * y_align),
350
// Aligns the given rect to a point.
351
// x/y_align = 0.5 centers rect.
352
TRect<T> align_at_point(T x_, T y_,
353
double x_align = 0.5,
354
double y_align = 0.5) const
356
return {static_cast<T>(x_ - this->w * x_align),
357
static_cast<T>(y_ - this->h * y_align),
361
// Divide self into columns x rows sub-rectangles
362
std::vector<TRect<T>> subdivide(size_t columns, size_t rows, T spacing={}) const
364
return subdivide(columns, rows, spacing, spacing);
366
std::vector<TRect<T>> subdivide(size_t columns, size_t rows, TVec2<T> spacing) const
368
return subdivide(columns, rows, spacing.x, spacing.y);
371
std::vector<TRect<T>> subdivide(size_t columns, size_t rows,
372
T x_spacing, T y_spacing) const
374
T ws = static_cast<T>(
375
(this->w - (columns - 1) * x_spacing) / static_cast<double>(columns));
376
T hs = static_cast<T>(
377
(this->h - (rows - 1) * y_spacing) / static_cast<double>(rows));
379
std::vector<TRect<T>> rects;
381
for (size_t row=0; row < rows; row++)
384
for (size_t column=0; column < columns; column++)
386
rects.emplace_back(_x, _y, ws, hs);
387
_x += ws + x_spacing;
389
_y += hs + y_spacing;
395
// Layout num_items sub-rectangles of size item_rect in grow_horizontal or
396
// columns-first order.
397
void flow_layout(std::vector<TRect<T>>& rects_out,
398
TRect<T>& bounds_out,
399
const TRect<T>& item_rect, size_t num_items,
400
T x_spacing, T y_spacing,
401
bool flow_horizontally=true,
402
bool grow_horizontally=true) const
409
if (grow_horizontally)
411
// "This" determines height and minimum width.
412
// Items may overflow the minimum width.
413
nrows = int((h + y_spacing) / (item_rect.h + y_spacing));
414
ncols = int(std::ceil(num_items / static_cast<T>(nrows)));
419
// "This" determines width and minimum height.
420
// Items may overflow the minimum height.
421
ncols = int((w + x_spacing) / (item_rect.w + x_spacing));
422
nrows = int(ceil(num_items / static_cast<T>(ncols)));
426
if (flow_horizontally)
428
for (int row=0; row < nrows; row++)
430
for (int col=0; col < ncols; col++)
432
T _x = x + item_rect.w * col +
433
x_spacing * std::max((col - 1), 0);
434
T _y = y + item_rect.h * row +
435
y_spacing * std::max((row - 1), 0);
436
TRect<T> r(_x, _y, item_rect.w, item_rect.h);
437
bounds_out = bounds_out.union_(r);
438
rects_out.push_back(r);
440
if (rects_out.size() >= num_items)
444
if (rects_out.size() >= num_items)
450
for (int col=0; col < ncols; col++)
452
for (int row=0; row < nrows; row++)
454
T _x = x + item_rect.w * col +
455
x_spacing * std::max((col - 1), 0);
456
T _y = y + item_rect.h * row +
457
y_spacing * std::max((row - 1), 0);
458
TRect<T> r(_x, _y, item_rect.w, item_rect.h);
459
bounds_out = bounds_out.union_(r);
460
rects_out.push_back(r);
462
if (rects_out.size() >= num_items)
466
if (rects_out.size() >= num_items)
479
typedef TRect<double> Rect;
480
typedef TRect<int> RectInt;
14
#include "rect_decl.h"
17
T& TRect<T>::operator[](size_t index) {
24
default: assert(false); // invalid index
29
TRect<T> TRect<T>::inflate(const TBorder<T>& b) const {
30
return {x - b.left, y - b.top,
32
h + b.top + b.bottom};
36
TRect<T> TRect<T>::round() const {
37
return {std::round(x), std::round(y), std::round(w), std::round(h)};
41
TRect<T> TRect<T>::floor() const {
42
return {std::floor(x), std::floor(y), std::floor(w), std::floor(h)};
46
TRect<T> TRect<T>::intersection(const TRect<T>& rect) const
48
T x0 = std::max(x, rect.x);
49
T y0 = std::max(y, rect.y);
50
T x1 = std::min(x + w, rect.x + rect.w);
51
T y1 = std::min(y + h, rect.y + rect.h);
52
if (x0 > x1 || y0 > y1)
55
return {x0, y0, x1 - x0, y1 - y0};
59
TRect<T> TRect<T>::union_(const TRect<T>& rect) const
61
T x0 = std::min(x, rect.x);
62
T y0 = std::min(y, rect.y);
63
T x1 = std::max(x + w, rect.x + rect.w);
64
T y1 = std::max(y + h, rect.y + rect.h);
65
return {x0, y0, x1 - x0, y1 - y0};
69
TRect<T> TRect<T>::resize_to_aspect_range(const TRect<T>& aspect_rect, T min_aspect, T max_aspect) const
71
(void)min_aspect; // not implemented
73
if (empty() || aspect_rect.empty())
76
TRect<T> r = aspect_rect;
79
double a0 = r.w / static_cast<double>(r.h);
80
double a0_max = a0 * max_aspect;
81
double a1 = w / static_cast<double>(h);
82
double a = std::min(a1, a0_max);
86
return resize_to_aspect(r);
90
std::vector<TRect<T>> TRect<T>::subdivide(size_t columns, size_t rows, T x_spacing, T y_spacing) const
92
T ws = static_cast<T>(
93
(this->w - (columns - 1) * x_spacing) / static_cast<double>(columns));
94
T hs = static_cast<T>(
95
(this->h - (rows - 1) * y_spacing) / static_cast<double>(rows));
97
std::vector<TRect<T>> rects;
99
for (size_t row=0; row < rows; row++)
102
for (size_t column=0; column < columns; column++)
104
rects.emplace_back(_x, _y, ws, hs);
105
_x += ws + x_spacing;
107
_y += hs + y_spacing;
114
void TRect<T>::flow_layout(std::vector<TRect<T> >& rects_out, TRect<T>& bounds_out, const TRect<T>& item_rect, size_t num_items, T x_spacing, T y_spacing, bool flow_horizontally, bool grow_horizontally) const
121
if (grow_horizontally)
123
// "This" determines height and minimum width.
124
// Items may overflow the minimum width.
125
nrows = int((h + y_spacing) / (item_rect.h + y_spacing));
126
ncols = int(std::ceil(num_items / static_cast<T>(nrows)));
131
// "This" determines width and minimum height.
132
// Items may overflow the minimum height.
133
ncols = int((w + x_spacing) / (item_rect.w + x_spacing));
134
nrows = int(ceil(num_items / static_cast<T>(ncols)));
138
if (flow_horizontally)
140
for (int row=0; row < nrows; row++)
142
for (int col=0; col < ncols; col++)
144
T _x = x + item_rect.w * col +
145
x_spacing * std::max((col - 1), 0);
146
T _y = y + item_rect.h * row +
147
y_spacing * std::max((row - 1), 0);
148
TRect<T> r(_x, _y, item_rect.w, item_rect.h);
149
bounds_out = bounds_out.union_(r);
150
rects_out.push_back(r);
152
if (rects_out.size() >= num_items)
156
if (rects_out.size() >= num_items)
162
for (int col=0; col < ncols; col++)
164
for (int row=0; row < nrows; row++)
166
T _x = x + item_rect.w * col +
167
x_spacing * std::max((col - 1), 0);
168
T _y = y + item_rect.h * row +
169
y_spacing * std::max((row - 1), 0);
170
TRect<T> r(_x, _y, item_rect.w, item_rect.h);
171
bounds_out = bounds_out.union_(r);
172
rects_out.push_back(r);
174
if (rects_out.size() >= num_items)
178
if (rects_out.size() >= num_items)
482
184
template<class T>
483
185
std::ostream& operator<<(std::ostream& s, const TRect<T>& r){