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 hi::inline v1 {
17
21private:
27 f32x4 v;
28
29public:
30 constexpr axis_aligned_rectangle() noexcept : v() {}
31 constexpr axis_aligned_rectangle(axis_aligned_rectangle const &rhs) noexcept = default;
32 constexpr axis_aligned_rectangle &operator=(axis_aligned_rectangle const &rhs) noexcept = default;
33 constexpr axis_aligned_rectangle(axis_aligned_rectangle &&rhs) noexcept = default;
34 constexpr axis_aligned_rectangle &operator=(axis_aligned_rectangle &&rhs) noexcept = default;
35
36 constexpr explicit axis_aligned_rectangle(f32x4 const &other) noexcept : v(other)
37 {
38 hi_axiom(holds_invariant());
39 }
40
48 constexpr axis_aligned_rectangle(float x, float y, float width, float height) noexcept : v{x, y, x + width, y + height}
49 {
50 hi_axiom(holds_invariant());
51 }
52
57 constexpr explicit axis_aligned_rectangle(extent2 const &extent) noexcept : v(static_cast<f32x4>(extent)._00xy())
58 {
59 hi_axiom(holds_invariant());
60 }
61
66 constexpr axis_aligned_rectangle(point2 const &p0, point2 const &p3) noexcept :
67 v(static_cast<f32x4>(p0).xy00() + static_cast<f32x4>(p3)._00xy())
68 {
69 hi_axiom(p0.holds_invariant());
70 hi_axiom(p3.holds_invariant());
71 hi_axiom(holds_invariant());
72 }
73
78 constexpr axis_aligned_rectangle(point2 const &p0, extent2 const &extent) noexcept :
79 v(static_cast<f32x4>(p0).xyxy() + static_cast<f32x4>(extent)._00xy())
80 {
81 hi_axiom(holds_invariant());
82 }
83
84 constexpr explicit operator f32x4() const noexcept
85 {
86 return v;
87 }
88
92 [[nodiscard]] constexpr bool holds_invariant() const noexcept
93 {
94 return le(v, v.zwzw()) == 0b1111;
95 }
96
99 [[nodiscard]] constexpr bool empty() const noexcept
100 {
101 return eq(v, v.zwxy()) == 0b1111;
102 }
103
106 [[nodiscard]] constexpr explicit operator bool() const noexcept
107 {
108 return not empty();
109 }
110
117 {
118 return *this = *this | rhs;
119 }
120
126 constexpr axis_aligned_rectangle &operator|=(point2 const &rhs) noexcept
127 {
128 return *this = *this | rhs;
129 }
130
131 [[nodiscard]] constexpr point2 operator[](std::size_t i) const noexcept
132 {
133 switch (i) {
134 case 0: return point2{v.xy01()};
135 case 1: return point2{v.zy01()};
136 case 2: return point2{v.xw01()};
137 case 3: return point2{v.zw01()};
138 default: hi_no_default();
139 }
140 }
141
142 template<int I>
143 [[nodiscard]] constexpr friend point2 get(axis_aligned_rectangle const &rhs) noexcept
144 {
145 if constexpr (I == 0) {
146 return point2{rhs.v.xy01()};
147 } else if constexpr (I == 1) {
148 return point2{rhs.v.zy01()};
149 } else if constexpr (I == 2) {
150 return point2{rhs.v.xw01()};
151 } else if constexpr (I == 3) {
152 return point2{rhs.v.zw01()};
153 } else {
154 hi_static_no_default();
155 }
156 }
157
162 [[nodiscard]] constexpr extent2 size() const noexcept
163 {
164 return extent2{v.zwzw() - v};
165 }
166
167 [[nodiscard]] constexpr float width() const noexcept
168 {
169 return (v.zwzw() - v).x();
170 }
171
172 [[nodiscard]] constexpr float height() const noexcept
173 {
174 return (v.zwzw() - v).y();
175 }
176
177 [[nodiscard]] constexpr float bottom() const noexcept
178 {
179 return v.y();
180 }
181
182 [[nodiscard]] constexpr float top() const noexcept
183 {
184 return v.w();
185 }
186
187 [[nodiscard]] constexpr float left() const noexcept
188 {
189 return v.x();
190 }
191
192 [[nodiscard]] constexpr float right() const noexcept
193 {
194 return v.z();
195 }
196
199 [[nodiscard]] constexpr float middle() const noexcept
200 {
201 return (bottom() + top()) * 0.5f;
202 }
203
206 [[nodiscard]] constexpr float center() const noexcept
207 {
208 return (left() + right()) * 0.5f;
209 }
210
213 [[nodiscard]] constexpr friend point2 midpoint(axis_aligned_rectangle const &rhs) noexcept
214 {
215 return midpoint(get<0>(rhs), get<3>(rhs));
216 }
217
218
219 constexpr axis_aligned_rectangle &set_width(float newWidth) noexcept
220 {
221 v = v.xyxw() + f32x4{0.0f, 0.0f, newWidth, 0.0f};
222 return *this;
223 }
224
225 constexpr axis_aligned_rectangle &set_height(float newHeight) noexcept
226 {
227 v = v.xyzy() + f32x4{0.0f, 0.0f, 0.0f, newHeight};
228 return *this;
229 }
230
235 [[nodiscard]] constexpr bool contains(point2 const &rhs) const noexcept
236 {
237 // No need to check with empty due to half open range check.
238 return ge(static_cast<f32x4>(rhs).xyxy(), v) == 0b0011;
239 }
240
246 [[nodiscard]] constexpr bool contains(point3 const &rhs) const noexcept
247 {
248 return contains(point2{rhs});
249 }
250
257 [[nodiscard]] friend constexpr axis_aligned_rectangle
259 {
260 float x;
261 if (alignment == horizontal_alignment::left) {
262 x = haystack.left();
263
264 } else if (alignment == horizontal_alignment::right) {
265 x = haystack.right() - needle.width();
266
267 } else if (alignment == horizontal_alignment::center) {
268 x = haystack.center() - needle.width() * 0.5f;
269
270 } else {
271 hi_no_default();
272 }
273
274 float y;
275 if (alignment == vertical_alignment::bottom) {
276 y = haystack.bottom();
277
278 } else if (alignment == vertical_alignment::top) {
279 y = haystack.top() - needle.height();
280
281 } else if (alignment == vertical_alignment::middle) {
282 y = haystack.middle() - needle.height() * 0.5f;
283
284 } else {
285 hi_no_default();
286 }
287
288 return {point2{x, y}, needle};
289 }
290
297 [[nodiscard]] friend constexpr axis_aligned_rectangle
299 {
300 return align(haystack, needle.size(), alignment);
301 }
302
305 [[nodiscard]] static constexpr axis_aligned_rectangle
307 {
308 return align(outside, inside, alignment);
309 }
310
311 [[nodiscard]] friend constexpr bool operator==(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
312 {
313 return lhs.v == rhs.v;
314 }
315
316 [[nodiscard]] friend constexpr bool overlaps(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
317 {
318 if (lhs.empty() or rhs.empty()) {
319 return false;
320 }
321
322 hilet rhs_swap = rhs.v.zwxy();
323
324 // lhs.p0.x > rhs.p3.x | lhs.p0.y > rhs.p3.y
325 if ((gt(lhs.v, rhs_swap) & 0b0011) != 0) {
326 return false;
327 }
328
329 // lhs.p3.x < rhs.p0.x | lhs.p3.y < rhs.p0.y
330 if ((lt(lhs.v, rhs_swap) & 0b1100) != 0) {
331 return false;
332 }
333
334 return true;
335 }
336
337 [[nodiscard]] friend constexpr axis_aligned_rectangle
338 operator|(axis_aligned_rectangle const &lhs, axis_aligned_rectangle const &rhs) noexcept
339 {
340 if (!lhs) {
341 return rhs;
342 } else if (!rhs) {
343 return lhs;
344 } else {
345 return axis_aligned_rectangle{min(get<0>(lhs), get<0>(rhs)), max(get<3>(lhs), get<3>(rhs))};
346 }
347 }
348
349 [[nodiscard]] friend constexpr axis_aligned_rectangle operator|(axis_aligned_rectangle const &lhs, point2 const &rhs) noexcept
350 {
351 if (!lhs) {
352 return axis_aligned_rectangle{rhs, rhs};
353 } else {
354 return axis_aligned_rectangle{min(get<0>(lhs), rhs), max(get<3>(lhs), rhs)};
355 }
356 }
357
358
364 [[nodiscard]] friend constexpr axis_aligned_rectangle operator*(axis_aligned_rectangle const &lhs, float rhs) noexcept
365 {
366 hilet new_extent = lhs.size() * rhs;
367 hilet diff = vector2{new_extent} - vector2{lhs.size()};
368 hilet offset = diff * 0.5f;
369
370 hilet p0 = get<0>(lhs) - offset;
371 hilet p3 = max(get<3>(lhs) + offset, p0);
372 return axis_aligned_rectangle{p0, p3};
373 }
374
381 [[nodiscard]] friend constexpr axis_aligned_rectangle operator+(axis_aligned_rectangle const &lhs, float rhs) noexcept
382 {
383 return axis_aligned_rectangle{lhs.v + neg<0b0011>(f32x4::broadcast(rhs))};
384 }
385
392 [[nodiscard]] friend constexpr axis_aligned_rectangle operator-(axis_aligned_rectangle const &lhs, float rhs) noexcept
393 {
394 return lhs + -rhs;
395 }
396
397 [[nodiscard]] friend constexpr axis_aligned_rectangle round(axis_aligned_rectangle const &rhs) noexcept
398 {
399 auto p0 = round(get<0>(rhs));
400 auto p3 = round(get<3>(rhs));
401 return axis_aligned_rectangle{p0, p3};
402 }
403
406 [[nodiscard]] friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &rhs) noexcept
407 {
408 auto p0 = floor(get<0>(rhs));
409 auto p3 = ceil(get<3>(rhs));
410 return axis_aligned_rectangle{p0, p3};
411 }
412
415 [[nodiscard]] friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &lhs, extent2 const &rhs) noexcept
416 {
417 auto p0 = floor(get<0>(lhs), rhs);
418 auto p3 = ceil(get<3>(lhs), rhs);
419 return axis_aligned_rectangle{p0, p3};
420 }
421
424 [[nodiscard]] friend constexpr axis_aligned_rectangle floor(axis_aligned_rectangle const &rhs) noexcept
425 {
426 auto p0 = ceil(get<0>(rhs));
427 auto p3 = floor(get<3>(rhs));
428 return axis_aligned_rectangle{p0, p3};
429 }
430
431 [[nodiscard]] friend constexpr axis_aligned_rectangle bounding_rectangle(axis_aligned_rectangle const &rhs) noexcept
432 {
433 return rhs;
434 }
435
439 [[nodiscard]] friend constexpr axis_aligned_rectangle
441 {
442 hilet p0 = max(get<0>(lhs), get<0>(rhs));
443 hilet p3 = min(get<3>(lhs), get<3>(rhs));
444 if (p0.x() < p3.x() && p0.y() < p3.y()) {
445 return {p0, p3};
446 } else {
447 return {};
448 }
449 }
450
460
461 [[nodiscard]] constexpr friend float distance(axis_aligned_rectangle const &lhs, point2 const &rhs) noexcept
462 {
463 hilet lhs_ = static_cast<f32x4>(lhs);
464 hilet rhs_ = static_cast<f32x4>(rhs);
465 // Only (x,y) of subsequent calculations are valid, (z,w) have garbage values.
466 hilet closest_point = max(min(rhs_, lhs_.zwzw()), lhs_);
467 hilet v_closest_point = closest_point - rhs_;
468 return hypot<0b0011>(v_closest_point);
469 }
470};
471
472using aarectangle = axis_aligned_rectangle;
473
474} // namespace hi::inline v1
475
476template<>
477class std::atomic<hi::axis_aligned_rectangle> {
478public:
479 static constexpr bool is_always_lock_free = false;
480
481 constexpr atomic() noexcept = default;
482 atomic(atomic const &) = default;
483 atomic(atomic &&) = default;
484 atomic &operator=(atomic const &) = default;
485 atomic &operator=(atomic &&) = default;
486
487 constexpr atomic(hi::axis_aligned_rectangle const &rhs) noexcept : _value(rhs) {}
488 atomic &operator=(hi::axis_aligned_rectangle const &rhs) noexcept
489 {
490 store(rhs);
491 return *this;
492 }
493
494 operator hi::axis_aligned_rectangle() const noexcept
495 {
496 return load();
497 }
498
499 [[nodiscard]] bool is_lock_free() const noexcept
500 {
501 return is_always_lock_free;
502 }
503
504 void store(hi::axis_aligned_rectangle desired, std::memory_order = std::memory_order_seq_cst) noexcept
505 {
506 hilet lock = std::scoped_lock(_mutex);
507 _value = desired;
508 }
509
510 hi::axis_aligned_rectangle load(std::memory_order = std::memory_order_seq_cst) const noexcept
511 {
512 hilet lock = std::scoped_lock(_mutex);
513 return _value;
514 }
515
516 hi::axis_aligned_rectangle
517 exchange(hi::axis_aligned_rectangle desired, std::memory_order = std::memory_order_seq_cst) noexcept
518 {
519 hilet lock = std::scoped_lock(_mutex);
520 return std::exchange(_value, desired);
521 }
522
524 hi::axis_aligned_rectangle &expected,
525 hi::axis_aligned_rectangle desired,
526 std::memory_order,
527 std::memory_order) noexcept
528 {
529 hilet lock = std::scoped_lock(_mutex);
530 if (_value == expected) {
531 _value = desired;
532 return true;
533 } else {
534 expected = _value;
535 return false;
536 }
537 }
538
540 hi::axis_aligned_rectangle &expected,
541 hi::axis_aligned_rectangle desired,
542 std::memory_order success,
543 std::memory_order failure) noexcept
544 {
545 return compare_exchange_weak(expected, desired, success, failure);
546 }
547
549 hi::axis_aligned_rectangle &expected,
550 hi::axis_aligned_rectangle desired,
551 std::memory_order order = std::memory_order_seq_cst) noexcept
552 {
553 return compare_exchange_weak(expected, desired, order, order);
554 }
555
557 hi::axis_aligned_rectangle &expected,
558 hi::axis_aligned_rectangle desired,
559 std::memory_order order = std::memory_order_seq_cst) noexcept
560 {
561 return compare_exchange_strong(expected, desired, order, order);
562 }
563
564 hi::axis_aligned_rectangle fetch_or(hi::axis_aligned_rectangle arg, std::memory_order = std::memory_order_seq_cst) noexcept
565 {
566 hilet lock = std::scoped_lock(_mutex);
567 auto tmp = _value;
568 _value = tmp | arg;
569 return tmp;
570 }
571
572 hi::axis_aligned_rectangle operator|=(hi::axis_aligned_rectangle arg) noexcept
573 {
574 hilet lock = std::scoped_lock(_mutex);
575 return _value |= arg;
576 }
577
578private:
579 hi::axis_aligned_rectangle _value;
580 mutable hi::unfair_mutex _mutex;
581};
582
583template<typename CharT>
584struct std::formatter<hi::axis_aligned_rectangle, CharT> : std::formatter<float, CharT> {
585 auto parse(auto &pc)
586 {
587 return pc.end();
588 }
589
590 auto format(hi::axis_aligned_rectangle const &t, auto &fc)
591 {
592 return std::vformat_to(fc.out(), "{}:{}", std::make_format_args(get<0>(t), t.size()));
593 }
594};
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
constexpr alignment operator|(horizontal_alignment lhs, vertical_alignment rhs) noexcept
Combine vertical and horizontal alignment.
Definition alignment.hpp:200
@ bottom
Align to the bottom.
@ top
Align to the top.
Definition alignment.hpp:64
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:20
friend constexpr axis_aligned_rectangle floor(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by shrinking to pixel edge.
Definition axis_aligned_rectangle.hpp:424
friend axis_aligned_rectangle fit(axis_aligned_rectangle const &bounds, axis_aligned_rectangle const &rectangle) noexcept
Make a rectangle fit inside bounds.
constexpr float center() const noexcept
The center on the x-axis between left and right.
Definition axis_aligned_rectangle.hpp:206
friend constexpr 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:440
friend constexpr axis_aligned_rectangle operator*(axis_aligned_rectangle const &lhs, float rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:364
constexpr bool empty() const noexcept
Check if the rectangle has no area.
Definition axis_aligned_rectangle.hpp:99
static constexpr 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:306
friend constexpr 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:298
friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by expanding to pixel edge.
Definition axis_aligned_rectangle.hpp:406
constexpr axis_aligned_rectangle & operator|=(axis_aligned_rectangle const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:116
friend constexpr axis_aligned_rectangle operator-(axis_aligned_rectangle const &lhs, float rhs) noexcept
Shrink the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:392
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:66
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:48
constexpr bool holds_invariant() const noexcept
Make sure p0 is left/bottom from p3.
Definition axis_aligned_rectangle.hpp:92
constexpr axis_aligned_rectangle & operator|=(point2 const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:126
constexpr extent2 size() const noexcept
Get size of the rectangle.
Definition axis_aligned_rectangle.hpp:162
friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &lhs, extent2 const &rhs) noexcept
Round rectangle by expanding to a certain granularity.
Definition axis_aligned_rectangle.hpp:415
constexpr bool contains(point2 const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:235
friend constexpr axis_aligned_rectangle operator+(axis_aligned_rectangle const &lhs, float rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:381
constexpr bool contains(point3 const &rhs) const noexcept
Check if a 3D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:246
constexpr float middle() const noexcept
The middle on the y-axis between bottom and top.
Definition axis_aligned_rectangle.hpp:199
constexpr axis_aligned_rectangle(extent2 const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:57
constexpr friend point2 midpoint(axis_aligned_rectangle const &rhs) noexcept
Get the center of the rectangle.
Definition axis_aligned_rectangle.hpp:213
friend constexpr axis_aligned_rectangle align(axis_aligned_rectangle haystack, extent2 needle, alignment alignment) noexcept
Align a rectangle within another rectangle.
Definition axis_aligned_rectangle.hpp:258
constexpr axis_aligned_rectangle(point2 const &p0, extent2 const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:78
A rectangle / parallelogram in 3D space.
Definition rectangle.hpp:20
T compare_exchange_weak(T... args)
T exchange(T... args)
T fetch_or(T... args)
T is_lock_free(T... args)
T left(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)