HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
axis_aligned_rectangle.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../rapid/numeric_array.hpp"
8#include "../alignment.hpp"
9#include "../concepts.hpp"
10#include "../unfair_mutex.hpp"
11#include "extent.hpp"
12#include "point.hpp"
13#include <concepts>
14#include <mutex>
15
16namespace tt {
17
21private:
22 friend class sfloat_rgba32;
23
29 f32x4 v;
30
31public:
32 constexpr axis_aligned_rectangle() noexcept : v() {}
33 constexpr axis_aligned_rectangle(axis_aligned_rectangle const &rhs) noexcept = default;
34 constexpr axis_aligned_rectangle &operator=(axis_aligned_rectangle const &rhs) noexcept = default;
35 constexpr axis_aligned_rectangle(axis_aligned_rectangle &&rhs) noexcept = default;
36 constexpr axis_aligned_rectangle &operator=(axis_aligned_rectangle &&rhs) noexcept = default;
37
38 constexpr explicit axis_aligned_rectangle(f32x4 const &other) noexcept : v(other)
39 {
40 tt_axiom(is_valid());
41 }
42
50 constexpr axis_aligned_rectangle(float x, float y, float width, float height) noexcept : v{x, y, x + width, y + height}
51 {
52 tt_axiom(is_valid());
53 }
54
59 constexpr explicit axis_aligned_rectangle(extent2 const &extent) noexcept : v(static_cast<f32x4>(extent)._00xy())
60 {
61 tt_axiom(is_valid());
62 }
63
68 constexpr axis_aligned_rectangle(point2 const &p0, point2 const &p3) noexcept :
69 v(static_cast<f32x4>(p0).xy00() + static_cast<f32x4>(p3)._00xy())
70 {
71 tt_axiom(p0.is_valid());
72 tt_axiom(p3.is_valid());
73 tt_axiom(is_valid());
74 }
75
80 constexpr axis_aligned_rectangle(point2 const &p0, extent2 const &extent) noexcept :
81 v(static_cast<f32x4>(p0).xyxy() + static_cast<f32x4>(extent)._00xy())
82 {
83 tt_axiom(is_valid());
84 }
85
89 [[nodiscard]] constexpr bool is_valid() const noexcept
90 {
91 return le(v, v.zwzw()) == 0b1111;
92 }
93
96 [[nodiscard]] bool empty() const noexcept
97 {
98 return eq(v, v.zwxy()) == 0b1111;
99 }
100
103 [[nodiscard]] operator bool() const noexcept
104 {
105 return !empty();
106 }
107
114 {
115 return *this = *this | rhs;
116 }
117
124 {
125 return *this = *this | rhs;
126 }
127
128 [[nodiscard]] constexpr point2 operator[](size_t i) const noexcept
129 {
130 switch (i) {
131 case 0: return point2{v.xy01()};
132 case 1: return point2{v.zy01()};
133 case 2: return point2{v.xw01()};
134 case 3: return point2{v.zw01()};
135 default: tt_no_default();
136 }
137 }
138
139 template<int I>
140 [[nodiscard]] constexpr friend point2 get(axis_aligned_rectangle const &rhs) noexcept
141 {
142 if constexpr (I == 0) {
143 return point2{rhs.v.xy01()};
144 } else if constexpr (I == 1) {
145 return point2{rhs.v.zy01()};
146 } else if constexpr (I == 2) {
147 return point2{rhs.v.xw01()};
148 } else if constexpr (I == 3) {
149 return point2{rhs.v.zw01()};
150 } else {
151 tt_static_no_default();
152 }
153 }
154
159 [[nodiscard]] extent2 size() const noexcept
160 {
161 return extent2{v.zwzw() - v};
162 }
163
164 [[nodiscard]] float width() const noexcept
165 {
166 return (v.zwzw() - v).x();
167 }
168
169 [[nodiscard]] float height() const noexcept
170 {
171 return (v.zwzw() - v).y();
172 }
173
174 [[nodiscard]] float bottom() const noexcept
175 {
176 return v.y();
177 }
178
179 [[nodiscard]] float top() const noexcept
180 {
181 return v.w();
182 }
183
184 [[nodiscard]] float left() const noexcept
185 {
186 return v.x();
187 }
188
189 [[nodiscard]] float right() const noexcept
190 {
191 return v.z();
192 }
193
196 [[nodiscard]] float middle() const noexcept
197 {
198 return (bottom() + top()) * 0.5f;
199 }
200
203 [[nodiscard]] float center() const noexcept
204 {
205 return (left() + right()) * 0.5f;
206 }
207
208 axis_aligned_rectangle &set_width(float newWidth) noexcept
209 {
210 v = v.xyxw() + f32x4{0.0f, 0.0f, newWidth, 0.0f};
211 return *this;
212 }
213
214 axis_aligned_rectangle &set_height(float newHeight) noexcept
215 {
216 v = v.xyzy() + f32x4{0.0f, 0.0f, 0.0f, newHeight};
217 return *this;
218 }
219
224 [[nodiscard]] bool contains(point2 const &rhs) const noexcept
225 {
226 // No need to check with empty due to half open range check.
227 return ge(static_cast<f32x4>(rhs).xyxy(), v) == 0b0011;
228 }
229
236 [[nodiscard]] friend axis_aligned_rectangle
238 {
239 float x;
240 if (alignment == horizontal_alignment::left) {
241 x = haystack.left();
242
243 } else if (alignment == horizontal_alignment::right) {
244 x = haystack.right() - needle.width();
245
246 } else if (alignment == horizontal_alignment::center) {
247 x = haystack.center() - needle.width() * 0.5f;
248
249 } else {
250 tt_no_default();
251 }
252
253 float y;
254 if (alignment == vertical_alignment::bottom) {
255 y = haystack.bottom();
256
257 } else if (alignment == vertical_alignment::top) {
258 y = haystack.top() - needle.height();
259
260 } else if (alignment == vertical_alignment::middle) {
261 y = haystack.middle() - needle.height() * 0.5f;
262
263 } else {
264 tt_no_default();
265 }
266
267 return {point2{x, y}, needle};
268 }
269
276 [[nodiscard]] friend axis_aligned_rectangle
278 {
279 return align(haystack, needle.size(), alignment);
280 }
281
284 [[nodiscard]] static axis_aligned_rectangle
286 {
287 return align(outside, inside, alignment);
288 }
289
290 [[nodiscard]] friend bool operator==(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
291 {
292 return lhs.v == rhs.v;
293 }
294
295 [[nodiscard]] friend bool operator!=(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
296 {
297 return !(lhs == rhs);
298 }
299
300 [[nodiscard]] friend bool overlaps(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
301 {
302 if (lhs.empty() || rhs.empty()) {
303 return false;
304 }
305
306 ttlet rhs_swap = rhs.v.zwxy();
307
308 // lhs.p0.x > rhs.p3.x | lhs.p0.y > rhs.p3.y
309 if ((gt(lhs.v, rhs_swap) & 0b0011) != 0) {
310 return false;
311 }
312
313 // lhs.p3.x < rhs.p0.x | lhs.p3.y < rhs.p0.y
314 if ((lt(lhs.v, rhs_swap) & 0b1100) != 0) {
315 return false;
316 }
317
318 return true;
319 }
320
321 [[nodiscard]] friend axis_aligned_rectangle
322 operator|(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
323 {
324 if (!lhs) {
325 return rhs;
326 } else if (!rhs) {
327 return lhs;
328 } else {
329 return axis_aligned_rectangle{min(get<0>(lhs), get<0>(rhs)), max(get<3>(lhs), get<3>(rhs))};
330 }
331 }
332
333 [[nodiscard]] friend axis_aligned_rectangle operator|(axis_aligned_rectangle const &lhs, point2 const &rhs) noexcept
334 {
335 if (!lhs) {
336 return axis_aligned_rectangle{rhs, rhs};
337 } else {
338 return axis_aligned_rectangle{min(get<0>(lhs), rhs), max(get<3>(lhs), rhs)};
339 }
340 }
341
344 [[nodiscard]] friend point2 center(axis_aligned_rectangle const &rhs) noexcept
345 {
346 return get<0>(rhs) + (get<3>(rhs) - get<0>(rhs)) * 0.5f;
347 }
348
354 [[nodiscard]] friend axis_aligned_rectangle scale(axis_aligned_rectangle const &lhs, float rhs) noexcept
355 {
356 ttlet new_extent = lhs.size() * rhs;
357 ttlet diff = vector2{new_extent} - vector2{lhs.size()};
358 ttlet offset = diff * 0.5f;
359
360 ttlet p0 = get<0>(lhs) - offset;
361 ttlet p3 = max(get<3>(lhs) + offset, p0);
362 return axis_aligned_rectangle{p0, p3};
363 }
364
371 [[nodiscard]] friend axis_aligned_rectangle expand(axis_aligned_rectangle const &lhs, float rhs) noexcept
372 {
373 return axis_aligned_rectangle{lhs.v + neg<0b0011>(f32x4::broadcast(rhs))};
374 }
375
382 [[nodiscard]] friend axis_aligned_rectangle shrink(axis_aligned_rectangle const &lhs, float rhs) noexcept
383 {
384 return expand(lhs, -rhs);
385 }
386
387 [[nodiscard]] friend axis_aligned_rectangle round(axis_aligned_rectangle const &rhs) noexcept
388 {
389 auto p0 = round(get<0>(rhs));
390 auto p3 = round(get<3>(rhs));
391 return axis_aligned_rectangle{p0, p3};
392 }
393
396 [[nodiscard]] friend axis_aligned_rectangle ceil(axis_aligned_rectangle const &rhs) noexcept
397 {
398 auto p0 = floor(get<0>(rhs));
399 auto p3 = ceil(get<3>(rhs));
400 return axis_aligned_rectangle{p0, p3};
401 }
402
405 [[nodiscard]] friend axis_aligned_rectangle floor(axis_aligned_rectangle const &rhs) noexcept
406 {
407 auto p0 = ceil(get<0>(rhs));
408 auto p3 = floor(get<3>(rhs));
409 return axis_aligned_rectangle{p0, p3};
410 }
411
415 [[nodiscard]] friend axis_aligned_rectangle
417 {
418 ttlet p0 = max(get<0>(lhs), get<0>(rhs));
419 ttlet p3 = min(get<3>(lhs), get<3>(rhs));
420 if (p0.x() < p3.x() && p0.y() < p3.y()) {
421 return {p0, p3};
422 } else {
423 return {};
424 }
425 }
426
434 [[nodiscard]] friend axis_aligned_rectangle
436};
437
439
440} // namespace tt
441
442namespace std {
443
444template<>
445class atomic<tt::axis_aligned_rectangle> {
446public:
447 static constexpr bool is_always_lock_free = false;
448
449 constexpr atomic() noexcept : _value() {}
450 atomic(atomic const &) = delete;
451 atomic(atomic &&) = delete;
452 atomic &operator=(atomic const &) = delete;
453 atomic &operator=(atomic &&) = delete;
454
455 constexpr atomic(tt::axis_aligned_rectangle const &rhs) noexcept : _value(rhs) {}
456 atomic &operator=(tt::axis_aligned_rectangle const &rhs) noexcept
457 {
458 store(rhs);
459 return *this;
460 }
461
462 operator tt::axis_aligned_rectangle() const noexcept
463 {
464 return load();
465 }
466
467 [[nodiscard]] bool is_lock_free() const noexcept
468 {
469 return is_always_lock_free;
470 }
471
472 void store(tt::axis_aligned_rectangle desired, std::memory_order = std::memory_order_seq_cst) noexcept
473 {
474 ttlet lock = std::scoped_lock(_mutex);
475 _value = desired;
476 }
477
478 tt::axis_aligned_rectangle load(std::memory_order = std::memory_order_seq_cst) const noexcept
479 {
480 ttlet lock = std::scoped_lock(_mutex);
481 return _value;
482 }
483
485 exchange(tt::axis_aligned_rectangle desired, std::memory_order = std::memory_order_seq_cst) noexcept
486 {
487 ttlet lock = std::scoped_lock(_mutex);
488 return std::exchange(_value, desired);
489 }
490
494 std::memory_order,
495 std::memory_order) noexcept
496 {
497 ttlet lock = std::scoped_lock(_mutex);
498 if (_value == expected) {
499 _value = desired;
500 return true;
501 } else {
502 expected = _value;
503 return false;
504 }
505 }
506
510 std::memory_order success,
511 std::memory_order failure) noexcept
512 {
513 return compare_exchange_weak(expected, desired, success, failure);
514 }
515
519 std::memory_order order = std::memory_order_seq_cst) noexcept
520 {
521 return compare_exchange_weak(expected, desired, order, order);
522 }
523
527 std::memory_order order = std::memory_order_seq_cst) noexcept
528 {
529 return compare_exchange_strong(expected, desired, order, order);
530 }
531
532 tt::axis_aligned_rectangle fetch_or(tt::axis_aligned_rectangle arg, std::memory_order = std::memory_order_seq_cst) noexcept
533 {
534 ttlet lock = std::scoped_lock(_mutex);
535 auto tmp = _value;
536 _value = tmp | arg;
537 return tmp;
538 }
539
541 {
542 ttlet lock = std::scoped_lock(_mutex);
543 return _value |= arg;
544 }
545
546private:
548 mutable tt::unfair_mutex _mutex;
549};
550
551template<typename CharT>
552struct formatter<tt::axis_aligned_rectangle, CharT> : formatter<float, CharT> {
553 auto parse(auto &pc)
554 {
555 return pc.end();
556 }
557
558 auto format(tt::axis_aligned_rectangle const &t, auto &fc)
559 {
560 return std::vformat_to(fc.out(), "{}:{}", std::make_format_args(get<0>(t), t.size()));
561 }
562};
563
564} // namespace std
alignment
Vertical and horizontal alignment.
Definition alignment.hpp:47
STL namespace.
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:20
friend axis_aligned_rectangle shrink(axis_aligned_rectangle const &lhs, float rhs) noexcept
Shrink the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:382
axis_aligned_rectangle & operator|=(axis_aligned_rectangle const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:113
bool contains(point2 const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:224
static axis_aligned_rectangle _align(axis_aligned_rectangle outside, axis_aligned_rectangle inside, alignment alignment) noexcept
Need to call the hidden friend function from within another class.
Definition axis_aligned_rectangle.hpp:285
friend axis_aligned_rectangle ceil(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by expanding to pixel edge.
Definition axis_aligned_rectangle.hpp:396
friend point2 center(axis_aligned_rectangle const &rhs) noexcept
Get the center of the rectangle.
Definition axis_aligned_rectangle.hpp:344
float middle() const noexcept
The middle on the y-axis between bottom and top.
Definition axis_aligned_rectangle.hpp:196
constexpr bool is_valid() const noexcept
Make sure p0 is left/bottom from p3.
Definition axis_aligned_rectangle.hpp:89
friend axis_aligned_rectangle fit(axis_aligned_rectangle const &bounds, axis_aligned_rectangle const &rectangle) noexcept
Make a rectangle fit inside bounds.
friend axis_aligned_rectangle scale(axis_aligned_rectangle const &lhs, float rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:354
friend axis_aligned_rectangle floor(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by shrinking to pixel edge.
Definition axis_aligned_rectangle.hpp:405
constexpr axis_aligned_rectangle(point2 const &p0, extent2 const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:80
constexpr axis_aligned_rectangle(point2 const &p0, point2 const &p3) noexcept
Create a rectangle from the left-bottom and right-top points.
Definition axis_aligned_rectangle.hpp:68
friend axis_aligned_rectangle align(axis_aligned_rectangle haystack, axis_aligned_rectangle needle, alignment alignment) noexcept
Align a rectangle within another rectangle.
Definition axis_aligned_rectangle.hpp:277
friend axis_aligned_rectangle expand(axis_aligned_rectangle const &lhs, float rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:371
axis_aligned_rectangle & operator|=(point2 const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:123
extent2 size() const noexcept
Get size of the rectangle.
Definition axis_aligned_rectangle.hpp:159
constexpr axis_aligned_rectangle(float x, float y, float width, float height) noexcept
Create a box from the position and size.
Definition axis_aligned_rectangle.hpp:50
bool empty() const noexcept
Check if the rectangle has no area.
Definition axis_aligned_rectangle.hpp:96
friend axis_aligned_rectangle intersect(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
Return the overlapping part of two rectangles.
Definition axis_aligned_rectangle.hpp:416
constexpr axis_aligned_rectangle(extent2 const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:59
friend axis_aligned_rectangle align(axis_aligned_rectangle haystack, extent2 needle, alignment alignment) noexcept
Align a rectangle within another rectangle.
Definition axis_aligned_rectangle.hpp:237
float center() const noexcept
The center on the x-axis between left and right.
Definition axis_aligned_rectangle.hpp:203
Class which represents an rectangle.
Definition rectangle.hpp:16
Definition sfloat_rgba32.hpp:15
T compare_exchange_weak(T... args)
T exchange(T... args)
T fetch_or(T... args)
T is_lock_free(T... args)
T load(T... args)
T lock(T... args)
T max(T... args)
T min(T... args)
T operator=(T... args)
T operator|=(T... args)
T store(T... args)