HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
axis_aligned_rectangle.hpp
Go to the documentation of this file.
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
9#pragma once
10
11#include "alignment.hpp"
12#include "extent.hpp"
13#include "point.hpp"
14#include "../rapid/numeric_array.hpp"
15#include "../concepts.hpp"
16#include "../unfair_mutex.hpp"
17#include "../cast.hpp"
18#include "../numbers.hpp"
19#include <concepts>
20#include <mutex>
21
22namespace hi { inline namespace v1 {
23namespace geo {
27template<typename T>
29public:
30 using value_type = T;
31 using array_type = numeric_array<value_type, 4>;
32
33 constexpr axis_aligned_rectangle() noexcept : v() {}
34 constexpr axis_aligned_rectangle(axis_aligned_rectangle const& rhs) noexcept = default;
35 constexpr axis_aligned_rectangle& operator=(axis_aligned_rectangle const& rhs) noexcept = default;
36 constexpr axis_aligned_rectangle(axis_aligned_rectangle&& rhs) noexcept = default;
37 constexpr axis_aligned_rectangle& operator=(axis_aligned_rectangle&& rhs) noexcept = default;
38
41 [[nodiscard]] constexpr static axis_aligned_rectangle large() noexcept
42 {
43 return {
44 point<value_type, 2>{-large_number_v<value_type>, -large_number_v<value_type>},
45 point<value_type, 2>{large_number_v<value_type>, large_number_v<value_type>}};
46 }
47
48 constexpr explicit axis_aligned_rectangle(array_type const& other) noexcept : v(other)
49 {
51 }
52
60 constexpr axis_aligned_rectangle(value_type x, value_type y, value_type width, value_type height) noexcept :
61 v{x, y, x + width, y + height}
62 {
64 }
65
70 constexpr explicit axis_aligned_rectangle(extent<value_type, 2> const& extent) noexcept :
71 v(static_cast<array_type>(extent)._00xy())
72 {
74 }
75
80 constexpr axis_aligned_rectangle(point<value_type, 2> const& p0, point<value_type, 2> const& p3) noexcept :
81 v(static_cast<array_type>(p0).xy00() + static_cast<array_type>(p3)._00xy())
82 {
83 hi_axiom(p0.holds_invariant());
84 hi_axiom(p3.holds_invariant());
86 }
87
95 constexpr axis_aligned_rectangle(point<value_type, 2> const& p0, extent<value_type, 2> const& extent) noexcept :
96 v(static_cast<array_type>(p0).xyxy() + static_cast<array_type>(extent)._00xy())
97 {
99 }
100
101 constexpr explicit operator array_type() const noexcept
102 {
103 return v;
104 }
105
109 [[nodiscard]] constexpr bool holds_invariant() const noexcept
110 {
111 return le(v, v.zwzw()) == 0b1111;
112 }
113
116 [[nodiscard]] constexpr bool empty() const noexcept
117 {
118 return eq(v, v.zwxy()) == 0b1111;
119 }
120
123 [[nodiscard]] constexpr explicit operator bool() const noexcept
124 {
125 return not empty();
126 }
127
134 {
135 return *this = *this | rhs;
136 }
137
143 constexpr axis_aligned_rectangle& operator|=(point<value_type, 2> const& rhs) noexcept
144 {
145 return *this = *this | rhs;
146 }
147
148 [[nodiscard]] constexpr point<value_type, 2> operator[](std::size_t i) const noexcept
149 {
150 switch (i) {
151 case 0:
152 return point<value_type, 2>{v.xy01()};
153 case 1:
154 return point<value_type, 2>{v.zy01()};
155 case 2:
156 return point<value_type, 2>{v.xw01()};
157 case 3:
158 return point<value_type, 2>{v.zw01()};
159 default:
161 }
162 }
163
164 template<int I>
165 [[nodiscard]] constexpr friend point<value_type, 2> get(axis_aligned_rectangle const& rhs) noexcept
166 {
167 if constexpr (I == 0) {
168 return point<value_type, 2>{rhs.v.xy01()};
169 } else if constexpr (I == 1) {
170 return point<value_type, 2>{rhs.v.zy01()};
171 } else if constexpr (I == 2) {
172 return point<value_type, 2>{rhs.v.xw01()};
173 } else if constexpr (I == 3) {
174 return point<value_type, 2>{rhs.v.zw01()};
175 } else {
177 }
178 }
179
184 [[nodiscard]] constexpr extent<value_type, 2> size() const noexcept
185 {
186 return extent<value_type, 2>{v.zwzw() - v};
187 }
188
189 [[nodiscard]] constexpr value_type x() const noexcept
190 {
191 return v.x();
192 }
193
194 [[nodiscard]] constexpr value_type y() const noexcept
195 {
196 return v.y();
197 }
198
199 [[nodiscard]] constexpr value_type width() const noexcept
200 {
201 return (v.zwzw() - v).x();
202 }
203
204 [[nodiscard]] constexpr value_type height() const noexcept
205 {
206 return (v.zwzw() - v).y();
207 }
208
209 [[nodiscard]] constexpr value_type bottom() const noexcept
210 {
211 return v.y();
212 }
213
214 [[nodiscard]] constexpr value_type top() const noexcept
215 {
216 return v.w();
217 }
218
219 [[nodiscard]] constexpr value_type left() const noexcept
220 {
221 return v.x();
222 }
223
224 [[nodiscard]] constexpr value_type right() const noexcept
225 {
226 return v.z();
227 }
228
231 [[nodiscard]] constexpr value_type middle() const noexcept
232 {
233 return (bottom() + top()) / value_type{2};
234 }
235
238 [[nodiscard]] constexpr value_type center() const noexcept
239 {
240 return (left() + right()) / value_type{2};
241 }
242
245 [[nodiscard]] constexpr friend point<value_type, 2> midpoint(axis_aligned_rectangle const& rhs) noexcept
246 {
247 return midpoint(get<0>(rhs), get<3>(rhs));
248 }
249
250 constexpr axis_aligned_rectangle& set_width(value_type newWidth) noexcept
251 {
252 v = v.xyxw() + array_type{value_type{0}, value_type{0}, newWidth, value_type{0}};
253 return *this;
254 }
255
256 constexpr axis_aligned_rectangle& set_height(value_type newHeight) noexcept
257 {
258 v = v.xyzy() + array_type{value_type{0}, value_type{0}, value_type{0}, newHeight};
259 return *this;
260 }
261
266 [[nodiscard]] constexpr bool contains(point<value_type, 2> const& rhs) const noexcept
267 {
268 // No need to check with empty due to half open range check.
269 return ge(static_cast<array_type>(rhs).xyxy(), v) == 0b0011;
270 }
271
277 [[nodiscard]] constexpr bool contains(point<value_type, 3> const& rhs) const noexcept
278 {
279 return contains(point<value_type, 2>{rhs});
280 }
281
288 [[nodiscard]] friend constexpr axis_aligned_rectangle
289 align(axis_aligned_rectangle haystack, extent<value_type, 2> needle, alignment alignment) noexcept
290 {
291 auto x = value_type{0};
292 if (alignment == horizontal_alignment::left) {
293 x = haystack.left();
294
295 } else if (alignment == horizontal_alignment::right) {
296 x = haystack.right() - needle.width();
297
298 } else if (alignment == horizontal_alignment::center) {
299 x = haystack.center() - needle.width() / value_type{2};
300
301 } else {
303 }
304
305 auto y = value_type{0};
306 if (alignment == vertical_alignment::bottom) {
307 y = haystack.bottom();
308
309 } else if (alignment == vertical_alignment::top) {
310 y = haystack.top() - needle.height();
311
312 } else if (alignment == vertical_alignment::middle) {
313 y = haystack.middle() - needle.height() / value_type{2};
314
315 } else {
317 }
318
319 return {point<value_type, 2>{x, y}, needle};
320 }
321
328 [[nodiscard]] friend constexpr axis_aligned_rectangle
329 align(axis_aligned_rectangle haystack, axis_aligned_rectangle needle, alignment alignment) noexcept
330 {
331 return align(haystack, needle.size(), alignment);
332 }
333
336 [[nodiscard]] static constexpr axis_aligned_rectangle
337 _align(axis_aligned_rectangle outside, axis_aligned_rectangle inside, alignment alignment) noexcept
338 {
339 return align(outside, inside, alignment);
340 }
341
342 [[nodiscard]] friend constexpr bool operator==(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
343 {
344 return lhs.v == rhs.v;
345 }
346
347 [[nodiscard]] friend constexpr bool overlaps(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
348 {
349 if (lhs.empty() or rhs.empty()) {
350 return false;
351 }
352
353 hilet rhs_swap = rhs.v.zwxy();
354
355 // lhs.p0.x > rhs.p3.x | lhs.p0.y > rhs.p3.y
356 if ((gt(lhs.v, rhs_swap) & 0b0011) != 0) {
357 return false;
358 }
359
360 // lhs.p3.x < rhs.p0.x | lhs.p3.y < rhs.p0.y
361 if ((lt(lhs.v, rhs_swap) & 0b1100) != 0) {
362 return false;
363 }
364
365 return true;
366 }
367
368 [[nodiscard]] friend constexpr axis_aligned_rectangle
369 operator|(axis_aligned_rectangle const& lhs, axis_aligned_rectangle const& rhs) noexcept
370 {
371 if (!lhs) {
372 return rhs;
373 } else if (!rhs) {
374 return lhs;
375 } else {
376 return axis_aligned_rectangle{min(get<0>(lhs), get<0>(rhs)), max(get<3>(lhs), get<3>(rhs))};
377 }
378 }
379
380 [[nodiscard]] friend constexpr axis_aligned_rectangle
381 operator|(axis_aligned_rectangle const& lhs, point<value_type, 2> const& rhs) noexcept
382 {
383 if (!lhs) {
384 return axis_aligned_rectangle{rhs, rhs};
385 } else {
386 return axis_aligned_rectangle{min(get<0>(lhs), rhs), max(get<3>(lhs), rhs)};
387 }
388 }
389
395 [[nodiscard]] friend constexpr axis_aligned_rectangle operator*(axis_aligned_rectangle const& lhs, value_type rhs) noexcept
396 {
397 hilet new_extent = lhs.size() * rhs;
398 hilet diff = vector<value_type, 2>{new_extent} - vector<value_type, 2>{lhs.size()};
399 hilet offset = diff * 0.5f;
400
401 hilet p0 = get<0>(lhs) - offset;
402 hilet p3 = max(get<3>(lhs) + offset, p0);
403 return axis_aligned_rectangle{p0, p3};
404 }
405
412 [[nodiscard]] friend constexpr axis_aligned_rectangle operator+(axis_aligned_rectangle const& lhs, value_type rhs) noexcept
413 {
414 return axis_aligned_rectangle{lhs.v + neg<0b0011>(array_type::broadcast(rhs))};
415 }
416
423 [[nodiscard]] friend constexpr axis_aligned_rectangle operator-(axis_aligned_rectangle const& lhs, value_type rhs) noexcept
424 {
425 return lhs + -rhs;
426 }
427
428 [[nodiscard]] friend constexpr axis_aligned_rectangle round(axis_aligned_rectangle const& rhs) noexcept
429 requires std::is_same_v<value_type, float>
430 {
431 hilet p0 = round(get<0>(rhs));
432 hilet size = round(rhs.size());
433 return axis_aligned_rectangle{p0, size};
434 }
435
438 [[nodiscard]] friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const& rhs) noexcept
439 requires std::is_same_v<value_type, float>
440 {
441 hilet p0 = floor(get<0>(rhs));
442 hilet p3 = ceil(get<3>(rhs));
443 return axis_aligned_rectangle{p0, p3};
444 }
445
448 [[nodiscard]] friend constexpr axis_aligned_rectangle
449 ceil(axis_aligned_rectangle const& lhs, extent<value_type, 2> const& rhs) noexcept
450 {
451 hilet p0 = floor(get<0>(lhs), rhs);
452 hilet p3 = ceil(get<3>(lhs), rhs);
453 return axis_aligned_rectangle{p0, p3};
454 }
455
458 [[nodiscard]] friend constexpr axis_aligned_rectangle floor(axis_aligned_rectangle const& rhs) noexcept
459 requires std::is_same_v<value_type, float>
460 {
461 hilet p0 = ceil(get<0>(rhs));
462 hilet p3 = floor(get<3>(rhs));
463 return axis_aligned_rectangle{p0, p3};
464 }
465
466 [[nodiscard]] friend constexpr axis_aligned_rectangle bounding_rectangle(axis_aligned_rectangle const& rhs) noexcept
467 {
468 return rhs;
469 }
470
474 [[nodiscard]] friend constexpr axis_aligned_rectangle
476 {
477 hilet p0 = max(get<0>(lhs), get<0>(rhs));
478 hilet p3 = min(get<3>(lhs), get<3>(rhs));
479 if (p0.x() < p3.x() && p0.y() < p3.y()) {
480 return {p0, p3};
481 } else {
482 return {};
483 }
484 }
485
486 [[nodiscard]] constexpr friend value_type
487 distance(axis_aligned_rectangle const& lhs, point<value_type, 2> const& rhs) noexcept
488 {
489 hilet lhs_ = static_cast<array_type>(lhs);
490 hilet rhs_ = static_cast<array_type>(rhs);
491 // Only (x,y) of subsequent calculations are valid, (z,w) have garbage values.
492 hilet closest_point = max(min(rhs_, lhs_.zwzw()), lhs_);
493 hilet v_closest_point = closest_point - rhs_;
494 return hypot<0b0011>(v_closest_point);
495 }
496
497private:
503 array_type v;
504};
505
506} // namespace geo
507
508using aarectangle = geo::axis_aligned_rectangle<float>;
509using aarectanglei = geo::axis_aligned_rectangle<int>;
510
518[[nodiscard]] aarectangle fit(aarectangle const& bounds, aarectangle const& rectangle) noexcept;
519
527[[nodiscard]] aarectanglei fit(aarectanglei const& bounds, aarectanglei const& rectangle) noexcept;
528
529template<>
530[[nodiscard]] constexpr aarectanglei narrow_cast(aarectangle const& rhs) noexcept
531{
532 return {narrow_cast<int>(rhs.x()), narrow_cast<int>(rhs.y()), narrow_cast<int>(rhs.width()), narrow_cast<int>(rhs.height())};
533}
534
535template<>
536[[nodiscard]] constexpr aarectangle narrow_cast(aarectanglei const& rhs) noexcept
537{
538 return {
539 narrow_cast<float>(rhs.x()),
540 narrow_cast<float>(rhs.y()),
541 narrow_cast<float>(rhs.width()),
542 narrow_cast<float>(rhs.height())};
543}
544
545}} // namespace hi::v1
546
547template<typename T>
548class std::atomic<hi::geo::axis_aligned_rectangle<T>> {
549public:
550 using value_type = hi::geo::axis_aligned_rectangle<T>;
551 static constexpr bool is_always_lock_free = false;
552
553 constexpr atomic() noexcept = default;
554 atomic(atomic const&) = delete;
555 atomic(atomic&&) = delete;
556 atomic& operator=(atomic const&) = delete;
557 atomic& operator=(atomic&&) = delete;
558
559 constexpr atomic(value_type const& rhs) noexcept : _value(rhs) {}
560 atomic& operator=(value_type const& rhs) noexcept
561 {
562 store(rhs);
563 return *this;
564 }
565
566 operator value_type() const noexcept
567 {
568 return load();
569 }
570
571 [[nodiscard]] bool is_lock_free() const noexcept
572 {
573 return is_always_lock_free;
574 }
575
576 void store(value_type desired, std::memory_order = std::memory_order_seq_cst) noexcept
577 {
578 hilet lock = std::scoped_lock(_mutex);
579 _value = desired;
580 }
581
582 value_type load(std::memory_order = std::memory_order_seq_cst) const noexcept
583 {
584 hilet lock = std::scoped_lock(_mutex);
585 return _value;
586 }
587
588 value_type exchange(value_type desired, std::memory_order = std::memory_order_seq_cst) noexcept
589 {
590 hilet lock = std::scoped_lock(_mutex);
591 return std::exchange(_value, desired);
592 }
593
594 bool compare_exchange_weak(value_type& expected, value_type desired, std::memory_order, std::memory_order) noexcept
595 {
596 hilet lock = std::scoped_lock(_mutex);
597 if (_value == expected) {
598 _value = desired;
599 return true;
600 } else {
601 expected = _value;
602 return false;
603 }
604 }
605
607 value_type& expected,
608 value_type desired,
609 std::memory_order success,
610 std::memory_order failure) noexcept
611 {
612 return compare_exchange_weak(expected, desired, success, failure);
613 }
614
615 bool
616 compare_exchange_weak(value_type& expected, value_type desired, std::memory_order order = std::memory_order_seq_cst) noexcept
617 {
618 return compare_exchange_weak(expected, desired, order, order);
619 }
620
622 value_type& expected,
623 value_type desired,
624 std::memory_order order = std::memory_order_seq_cst) noexcept
625 {
626 return compare_exchange_strong(expected, desired, order, order);
627 }
628
629 value_type fetch_or(value_type arg, std::memory_order = std::memory_order_seq_cst) noexcept
630 {
631 hilet lock = std::scoped_lock(_mutex);
632 auto tmp = _value;
633 _value = tmp | arg;
634 return tmp;
635 }
636
637 value_type operator|=(value_type arg) noexcept
638 {
639 hilet lock = std::scoped_lock(_mutex);
640 return _value |= arg;
641 }
642
643private:
644 value_type _value;
645 mutable hi::unfair_mutex _mutex;
646};
647
648template<typename CharT>
649struct std::formatter<hi::geo::axis_aligned_rectangle<float>, CharT> {
650 auto parse(auto& pc)
651 {
652 return pc.end();
653 }
654
655 auto format(hi::geo::axis_aligned_rectangle<float> const& t, auto& fc)
656 {
657 return std::vformat_to(fc.out(), "{}:{}", std::make_format_args(get<0>(t), t.size()));
658 }
659};
660
661template<typename CharT>
662struct std::formatter<hi::geo::axis_aligned_rectangle<int>, CharT> {
663 auto parse(auto& pc)
664 {
665 return pc.end();
666 }
667
668 auto format(hi::geo::axis_aligned_rectangle<int> const& t, auto& fc)
669 {
670 return std::vformat_to(fc.out(), "{}:{}", std::make_format_args(get<0>(t), t.size()));
671 }
672};
#define hi_static_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:181
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:148
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
Defined the geo::extent, extent2 and extent3 types.
types and utilities for alignment.
This file contains constants and conversion functions.
@ other
The gui_event does not have associated data.
@ rectangle
The gui_event has rectangle data.
DOXYGEN BUG.
Definition algorithm.hpp:15
geometry/margins.hpp
Definition assert.hpp:18
aarectangle fit(aarectangle const &bounds, aarectangle const &rectangle) noexcept
Make a rectangle fit inside bounds.
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:28
static constexpr axis_aligned_rectangle large() noexcept
Create a large axis aligned rectangle.
Definition axis_aligned_rectangle.hpp:41
friend constexpr axis_aligned_rectangle operator-(axis_aligned_rectangle const &lhs, value_type rhs) noexcept
Shrink the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:423
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:337
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:475
constexpr axis_aligned_rectangle(point< value_type, 2 > const &p0, extent< value_type, 2 > const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:95
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:329
friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by expanding to pixel edge.
Definition axis_aligned_rectangle.hpp:438
constexpr bool contains(point< value_type, 3 > const &rhs) const noexcept
Check if a 3D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:277
constexpr extent< value_type, 2 > size() const noexcept
Get size of the rectangle.
Definition axis_aligned_rectangle.hpp:184
constexpr bool empty() const noexcept
Check if the rectangle has no area.
Definition axis_aligned_rectangle.hpp:116
constexpr bool holds_invariant() const noexcept
Make sure p0 is left/bottom from p3.
Definition axis_aligned_rectangle.hpp:109
constexpr bool contains(point< value_type, 2 > const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:266
friend constexpr axis_aligned_rectangle floor(axis_aligned_rectangle const &rhs) noexcept
Round rectangle by shrinking to pixel edge.
Definition axis_aligned_rectangle.hpp:458
constexpr friend point< value_type, 2 > midpoint(axis_aligned_rectangle const &rhs) noexcept
Get the center of the rectangle.
Definition axis_aligned_rectangle.hpp:245
constexpr value_type center() const noexcept
The center on the x-axis between left and right.
Definition axis_aligned_rectangle.hpp:238
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:133
constexpr axis_aligned_rectangle(value_type x, value_type y, value_type width, value_type height) noexcept
Create a box from the position and size.
Definition axis_aligned_rectangle.hpp:60
friend constexpr axis_aligned_rectangle operator*(axis_aligned_rectangle const &lhs, value_type rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:395
friend constexpr axis_aligned_rectangle operator+(axis_aligned_rectangle const &lhs, value_type rhs) noexcept
Expand the rectangle for the same amount in all directions.
Definition axis_aligned_rectangle.hpp:412
constexpr value_type middle() const noexcept
The middle on the y-axis between bottom and top.
Definition axis_aligned_rectangle.hpp:231
constexpr axis_aligned_rectangle(point< value_type, 2 > const &p0, point< value_type, 2 > const &p3) noexcept
Create a rectangle from the left-bottom and right-top points.
Definition axis_aligned_rectangle.hpp:80
friend constexpr axis_aligned_rectangle align(axis_aligned_rectangle haystack, extent< value_type, 2 > needle, alignment alignment) noexcept
Align a rectangle within another rectangle.
Definition axis_aligned_rectangle.hpp:289
constexpr axis_aligned_rectangle & operator|=(point< value_type, 2 > const &rhs) noexcept
Expand the current rectangle to include the new rectangle.
Definition axis_aligned_rectangle.hpp:143
constexpr axis_aligned_rectangle(extent< value_type, 2 > const &extent) noexcept
Create a rectangle from the size.
Definition axis_aligned_rectangle.hpp:70
friend constexpr axis_aligned_rectangle ceil(axis_aligned_rectangle const &lhs, extent< value_type, 2 > const &rhs) noexcept
Round rectangle by expanding to a certain granularity.
Definition axis_aligned_rectangle.hpp:449
A high-level geometric extent.
Definition extent.hpp:31
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)