HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
axis_aligned_rectangle.hpp
1// Copyright Take Vos 2021-2022.
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
81 constexpr axis_aligned_rectangle(point2 const& p0, extent2 const& extent) noexcept :
82 v(static_cast<f32x4>(p0).xyxy() + static_cast<f32x4>(extent)._00xy())
83 {
84 hi_axiom(holds_invariant());
85 }
86
87 constexpr explicit operator f32x4() const noexcept
88 {
89 return v;
90 }
91
95 [[nodiscard]] constexpr bool holds_invariant() const noexcept
96 {
97 return le(v, v.zwzw()) == 0b1111;
98 }
99
102 [[nodiscard]] constexpr bool empty() const noexcept
103 {
104 return eq(v, v.zwxy()) == 0b1111;
105 }
106
109 [[nodiscard]] constexpr explicit operator bool() const noexcept
110 {
111 return not empty();
112 }
113
120 {
121 return *this = *this | rhs;
122 }
123
129 constexpr axis_aligned_rectangle& operator|=(point2 const& rhs) noexcept
130 {
131 return *this = *this | rhs;
132 }
133
134 [[nodiscard]] constexpr point2 operator[](std::size_t i) const noexcept
135 {
136 switch (i) {
137 case 0:
138 return point2{v.xy01()};
139 case 1:
140 return point2{v.zy01()};
141 case 2:
142 return point2{v.xw01()};
143 case 3:
144 return point2{v.zw01()};
145 default:
147 }
148 }
149
150 template<int I>
151 [[nodiscard]] constexpr friend point2 get(axis_aligned_rectangle const& rhs) noexcept
152 {
153 if constexpr (I == 0) {
154 return point2{rhs.v.xy01()};
155 } else if constexpr (I == 1) {
156 return point2{rhs.v.zy01()};
157 } else if constexpr (I == 2) {
158 return point2{rhs.v.xw01()};
159 } else if constexpr (I == 3) {
160 return point2{rhs.v.zw01()};
161 } else {
163 }
164 }
165
170 [[nodiscard]] constexpr extent2 size() const noexcept
171 {
172 return extent2{v.zwzw() - v};
173 }
174
175 [[nodiscard]] constexpr float width() const noexcept
176 {
177 return (v.zwzw() - v).x();
178 }
179
180 [[nodiscard]] constexpr float height() const noexcept
181 {
182 return (v.zwzw() - v).y();
183 }
184
185 [[nodiscard]] constexpr float bottom() const noexcept
186 {
187 return v.y();
188 }
189
190 [[nodiscard]] constexpr float top() const noexcept
191 {
192 return v.w();
193 }
194
195 [[nodiscard]] constexpr float left() const noexcept
196 {
197 return v.x();
198 }
199
200 [[nodiscard]] constexpr float right() const noexcept
201 {
202 return v.z();
203 }
204
207 [[nodiscard]] constexpr float middle() const noexcept
208 {
209 return (bottom() + top()) * 0.5f;
210 }
211
214 [[nodiscard]] constexpr float center() const noexcept
215 {
216 return (left() + right()) * 0.5f;
217 }
218
221 [[nodiscard]] constexpr friend point2 midpoint(axis_aligned_rectangle const& rhs) noexcept
222 {
223 return midpoint(get<0>(rhs), get<3>(rhs));
224 }
225
226 constexpr axis_aligned_rectangle& set_width(float newWidth) noexcept
227 {
228 v = v.xyxw() + f32x4{0.0f, 0.0f, newWidth, 0.0f};
229 return *this;
230 }
231
232 constexpr axis_aligned_rectangle& set_height(float newHeight) noexcept
233 {
234 v = v.xyzy() + f32x4{0.0f, 0.0f, 0.0f, newHeight};
235 return *this;
236 }
237
242 [[nodiscard]] constexpr bool contains(point2 const& rhs) const noexcept
243 {
244 // No need to check with empty due to half open range check.
245 return ge(static_cast<f32x4>(rhs).xyxy(), v) == 0b0011;
246 }
247
253 [[nodiscard]] constexpr bool contains(point3 const& rhs) const noexcept
254 {
255 return contains(point2{rhs});
256 }
257
264 [[nodiscard]] friend constexpr axis_aligned_rectangle
266 {
267 auto x = 0.0f;
268 if (alignment == horizontal_alignment::left) {
269 x = haystack.left();
270
271 } else if (alignment == horizontal_alignment::right) {
272 x = haystack.right() - needle.width();
273
274 } else if (alignment == horizontal_alignment::center) {
275 x = haystack.center() - needle.width() * 0.5f;
276
277 } else {
279 }
280
281 auto y = 0.0f;
282 if (alignment == vertical_alignment::bottom) {
283 y = haystack.bottom();
284
285 } else if (alignment == vertical_alignment::top) {
286 y = haystack.top() - needle.height();
287
288 } else if (alignment == vertical_alignment::middle) {
289 y = haystack.middle() - needle.height() * 0.5f;
290
291 } else {
293 }
294
295 return {point2{x, y}, needle};
296 }
297
304 [[nodiscard]] friend constexpr axis_aligned_rectangle
306 {
307 return align(haystack, needle.size(), alignment);
308 }
309
312 [[nodiscard]] static constexpr axis_aligned_rectangle
314 {
315 return align(outside, inside, alignment);
316 }
317
318 [[nodiscard]] friend constexpr bool operator==(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
319 {
320 return lhs.v == rhs.v;
321 }
322
323 [[nodiscard]] friend constexpr bool overlaps(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
324 {
325 if (lhs.empty() or rhs.empty()) {
326 return false;
327 }
328
329 hilet rhs_swap = rhs.v.zwxy();
330
331 // lhs.p0.x > rhs.p3.x | lhs.p0.y > rhs.p3.y
332 if ((gt(lhs.v, rhs_swap) & 0b0011) != 0) {
333 return false;
334 }
335
336 // lhs.p3.x < rhs.p0.x | lhs.p3.y < rhs.p0.y
337 if ((lt(lhs.v, rhs_swap) & 0b1100) != 0) {
338 return false;
339 }
340
341 return true;
342 }
343
344 [[nodiscard]] friend constexpr axis_aligned_rectangle
345 operator|(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
346 {
347 if (!lhs) {
348 return rhs;
349 } else if (!rhs) {
350 return lhs;
351 } else {
352 return axis_aligned_rectangle{min(get<0>(lhs), get<0>(rhs)), max(get<3>(lhs), get<3>(rhs))};
353 }
354 }
355
356 [[nodiscard]] friend constexpr axis_aligned_rectangle operator|(axis_aligned_rectangle const& lhs, point2 const& rhs) noexcept
357 {
358 if (!lhs) {
359 return axis_aligned_rectangle{rhs, rhs};
360 } else {
361 return axis_aligned_rectangle{min(get<0>(lhs), rhs), max(get<3>(lhs), rhs)};
362 }
363 }
364
370 [[nodiscard]] friend constexpr axis_aligned_rectangle operator*(axis_aligned_rectangle const& lhs, float rhs) noexcept
371 {
372 hilet new_extent = lhs.size() * rhs;
373 hilet diff = vector2{new_extent} - vector2{lhs.size()};
374 hilet offset = diff * 0.5f;
375
376 hilet p0 = get<0>(lhs) - offset;
377 hilet p3 = max(get<3>(lhs) + offset, p0);
378 return axis_aligned_rectangle{p0, p3};
379 }
380
387 [[nodiscard]] friend constexpr axis_aligned_rectangle operator+(axis_aligned_rectangle const& lhs, float rhs) noexcept
388 {
389 return axis_aligned_rectangle{lhs.v + neg<0b0011>(f32x4::broadcast(rhs))};
390 }
391
398 [[nodiscard]] friend constexpr axis_aligned_rectangle operator-(axis_aligned_rectangle const& lhs, float rhs) noexcept
399 {
400 return lhs + -rhs;
401 }
402
403 [[nodiscard]] friend constexpr axis_aligned_rectangle round(axis_aligned_rectangle const& rhs) noexcept
404 {
405 hilet p0 = round(get<0>(rhs));
406 hilet size = round(rhs.size());
407 return axis_aligned_rectangle{p0, size};
408 }
409
412 [[nodiscard]] friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const& rhs) noexcept
413 {
414 hilet p0 = floor(get<0>(rhs));
415 hilet p3 = ceil(get<3>(rhs));
416 return axis_aligned_rectangle{p0, p3};
417 }
418
421 [[nodiscard]] friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const& lhs, extent2 const& rhs) noexcept
422 {
423 hilet p0 = floor(get<0>(lhs), rhs);
424 hilet p3 = ceil(get<3>(lhs), rhs);
425 return axis_aligned_rectangle{p0, p3};
426 }
427
430 [[nodiscard]] friend constexpr axis_aligned_rectangle floor(axis_aligned_rectangle const& rhs) noexcept
431 {
432 hilet p0 = ceil(get<0>(rhs));
433 hilet p3 = floor(get<3>(rhs));
434 return axis_aligned_rectangle{p0, p3};
435 }
436
437 [[nodiscard]] friend constexpr axis_aligned_rectangle bounding_rectangle(axis_aligned_rectangle const& rhs) noexcept
438 {
439 return rhs;
440 }
441
445 [[nodiscard]] friend constexpr axis_aligned_rectangle
447 {
448 hilet p0 = max(get<0>(lhs), get<0>(rhs));
449 hilet p3 = min(get<3>(lhs), get<3>(rhs));
450 if (p0.x() < p3.x() && p0.y() < p3.y()) {
451 return {p0, p3};
452 } else {
453 return {};
454 }
455 }
456
465
466 [[nodiscard]] constexpr friend float distance(axis_aligned_rectangle const& lhs, point2 const& rhs) noexcept
467 {
468 hilet lhs_ = static_cast<f32x4>(lhs);
469 hilet rhs_ = static_cast<f32x4>(rhs);
470 // Only (x,y) of subsequent calculations are valid, (z,w) have garbage values.
471 hilet closest_point = max(min(rhs_, lhs_.zwzw()), lhs_);
472 hilet v_closest_point = closest_point - rhs_;
473 return hypot<0b0011>(v_closest_point);
474 }
475};
476
477using aarectangle = axis_aligned_rectangle;
478
479} // namespace hi::inline v1
480
481template<>
482class std::atomic<hi::axis_aligned_rectangle> {
483public:
484 static constexpr bool is_always_lock_free = false;
485
486 constexpr atomic() noexcept = default;
487 atomic(atomic const&) = delete;
488 atomic(atomic&&) = delete;
489 atomic& operator=(atomic const&) = delete;
490 atomic& operator=(atomic&&) = delete;
491
492 constexpr atomic(hi::axis_aligned_rectangle const& rhs) noexcept : _value(rhs) {}
493 atomic& operator=(hi::axis_aligned_rectangle const& rhs) noexcept
494 {
495 store(rhs);
496 return *this;
497 }
498
499 operator hi::axis_aligned_rectangle() const noexcept
500 {
501 return load();
502 }
503
504 [[nodiscard]] bool is_lock_free() const noexcept
505 {
506 return is_always_lock_free;
507 }
508
509 void store(hi::axis_aligned_rectangle desired, std::memory_order = std::memory_order_seq_cst) noexcept
510 {
511 hilet lock = std::scoped_lock(_mutex);
512 _value = desired;
513 }
514
515 hi::axis_aligned_rectangle load(std::memory_order = std::memory_order_seq_cst) const noexcept
516 {
517 hilet lock = std::scoped_lock(_mutex);
518 return _value;
519 }
520
521 hi::axis_aligned_rectangle
522 exchange(hi::axis_aligned_rectangle desired, std::memory_order = std::memory_order_seq_cst) noexcept
523 {
524 hilet lock = std::scoped_lock(_mutex);
525 return std::exchange(_value, desired);
526 }
527
529 hi::axis_aligned_rectangle& expected,
530 hi::axis_aligned_rectangle desired,
531 std::memory_order,
532 std::memory_order) noexcept
533 {
534 hilet lock = std::scoped_lock(_mutex);
535 if (_value == expected) {
536 _value = desired;
537 return true;
538 } else {
539 expected = _value;
540 return false;
541 }
542 }
543
545 hi::axis_aligned_rectangle& expected,
546 hi::axis_aligned_rectangle desired,
547 std::memory_order success,
548 std::memory_order failure) noexcept
549 {
550 return compare_exchange_weak(expected, desired, success, failure);
551 }
552
554 hi::axis_aligned_rectangle& expected,
555 hi::axis_aligned_rectangle desired,
556 std::memory_order order = std::memory_order_seq_cst) noexcept
557 {
558 return compare_exchange_weak(expected, desired, order, order);
559 }
560
562 hi::axis_aligned_rectangle& expected,
563 hi::axis_aligned_rectangle desired,
564 std::memory_order order = std::memory_order_seq_cst) noexcept
565 {
566 return compare_exchange_strong(expected, desired, order, order);
567 }
568
569 hi::axis_aligned_rectangle fetch_or(hi::axis_aligned_rectangle arg, std::memory_order = std::memory_order_seq_cst) noexcept
570 {
571 hilet lock = std::scoped_lock(_mutex);
572 auto tmp = _value;
573 _value = tmp | arg;
574 return tmp;
575 }
576
577 hi::axis_aligned_rectangle operator|=(hi::axis_aligned_rectangle arg) noexcept
578 {
579 hilet lock = std::scoped_lock(_mutex);
580 return _value |= arg;
581 }
582
583private:
584 hi::axis_aligned_rectangle _value;
585 mutable hi::unfair_mutex _mutex;
586};
587
588template<typename CharT>
589struct std::formatter<hi::axis_aligned_rectangle, CharT> : std::formatter<float, CharT> {
590 auto parse(auto& pc)
591 {
592 return pc.end();
593 }
594
595 auto format(hi::axis_aligned_rectangle const& t, auto& fc)
596 {
597 return std::vformat_to(fc.out(), "{}:{}", std::make_format_args(get<0>(t), t.size()));
598 }
599};
#define hi_axiom(expression)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_static_no_default()
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:172
#define hi_no_default()
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:145
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
constexpr alignment operator|(horizontal_alignment lhs, vertical_alignment rhs) noexcept
Combine vertical and horizontal alignment.
Definition alignment.hpp:216
@ bottom
Align to the bottom.
@ top
Align to the top.
@ inside
The border is drawn inside the edge of a quad.
@ outside
The border is drawn outside the edge of a quad.
The HikoGUI namespace.
Definition ascii.hpp:19
Definition alignment.hpp:75
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:430
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:214
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:446
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:370
constexpr bool empty() const noexcept
Check if the rectangle has no area.
Definition axis_aligned_rectangle.hpp:102
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:313
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:305
friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by expanding to pixel edge.
Definition axis_aligned_rectangle.hpp:412
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:119
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:398
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:95
constexpr axis_aligned_rectangle & operator|=(point2 const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:129
constexpr extent2 size() const noexcept
Get size of the rectangle.
Definition axis_aligned_rectangle.hpp:170
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:421
constexpr bool contains(point2 const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:242
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:387
constexpr bool contains(point3 const &rhs) const noexcept
Check if a 3D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:253
constexpr float middle() const noexcept
The middle on the y-axis between bottom and top.
Definition axis_aligned_rectangle.hpp:207
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:221
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:265
constexpr axis_aligned_rectangle(point2 const &p0, extent2 const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:81
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)