HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
extent.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 "vector.hpp"
8#include "../rapid/numeric_array.hpp"
9#include <compare>
10
11namespace hi::inline v1 {
12namespace geo {
13template<int D>
14class scale;
15
22template<int D>
23class extent {
24public:
25 static_assert(D == 2 || D == 3, "Only 2D or 3D extents are supported");
26
27 constexpr extent(extent const &) noexcept = default;
28 constexpr extent(extent &&) noexcept = default;
29 constexpr extent &operator=(extent const &) noexcept = default;
30 constexpr extent &operator=(extent &&) noexcept = default;
31
34 template<int E>
35 requires(E < D) [[nodiscard]] constexpr extent(extent<E> const &other) noexcept : _v(static_cast<f32x4>(other))
36 {
37 hi_axiom(holds_invariant());
38 }
39
42 [[nodiscard]] constexpr explicit operator f32x4() const noexcept
43 {
44 return _v;
45 }
46
47 [[nodiscard]] constexpr explicit extent(f32x4 const &other) noexcept : _v(other) {}
48
49 [[nodiscard]] constexpr explicit operator bool() const noexcept
50 {
51 if constexpr (D == 2) {
52 return _v.x() != 0.0f or _v.y() != 0.0f;
53 } else if constexpr (D == 3) {
54 return _v.x() != 0.0f or _v.y() != 0.0f or _v.z() != 0.0f;
55 } else {
56 hi_no_default();
57 }
58 }
59
60 template<int E>
61 [[nodiscard]] constexpr explicit operator vector<E>() const noexcept requires(E >= D)
62 {
63 hi_axiom(holds_invariant());
64 return vector<E>{static_cast<f32x4>(*this)};
65 }
66
69 [[nodiscard]] constexpr extent() noexcept : _v(0.0f, 0.0f, 0.0f, 0.0f)
70 {
71 hi_axiom(holds_invariant());
72 }
73
78 [[nodiscard]] constexpr extent(float width, float height) noexcept requires(D == 2) : _v(width, height, 0.0f, 0.0f)
79 {
80 hi_axiom(holds_invariant());
81 }
82
88 [[nodiscard]] constexpr extent(float width, float height, float depth = 0.0f) noexcept requires(D == 3) :
89 _v(width, height, depth, 0.0f)
90 {
91 hi_axiom(holds_invariant());
92 }
93
94 [[nodiscard]] static constexpr extent infinity() noexcept requires(D == 2)
95 {
97 }
98
99 [[nodiscard]] static constexpr extent infinity() noexcept requires(D == 3)
100 {
101 return extent{
105 }
106
107 [[nodiscard]] static constexpr extent large() noexcept requires(D == 2)
108 {
109 return extent{32767.0f, 32767.0f};
110 }
111
112 [[nodiscard]] static constexpr extent large() noexcept requires(D == 3)
113 {
114 return extent{32767.0f, 32767.0f, 32767.0f};
115 }
116
117 [[nodiscard]] static constexpr extent nan() noexcept requires(D == 2)
118 {
119 auto r = extent{};
122 return r;
123 }
124
125 [[nodiscard]] static constexpr extent nan() noexcept requires(D == 3)
126 {
127 auto r = extent{};
131 return r;
132 }
133
140 [[nodiscard]] constexpr float &width() noexcept
141 {
142 return _v.x();
143 }
144
151 [[nodiscard]] constexpr float &height() noexcept
152 {
153 return _v.y();
154 }
155
162 [[nodiscard]] constexpr float &depth() noexcept requires(D == 3)
163 {
164 return _v.z();
165 }
166
173 [[nodiscard]] constexpr float const &width() const noexcept
174 {
175 return _v.x();
176 }
177
184 [[nodiscard]] constexpr float const &height() const noexcept
185 {
186 return _v.y();
187 }
188
195 [[nodiscard]] constexpr float const &depth() const noexcept requires(D == 3)
196 {
197 return _v.z();
198 }
199
200 [[nodiscard]] constexpr vector<D> right() const noexcept
201 {
202 return vector<D>{_v.x000()};
203 }
204
205 [[nodiscard]] constexpr vector<D> up() const noexcept
206 {
207 return vector<D>{_v._0y00()};
208 }
209
210 constexpr extent &operator+=(extent const &rhs) noexcept
211 {
212 return *this = *this + rhs;
213 }
214
220 [[nodiscard]] constexpr friend extent operator+(extent const &lhs, extent const &rhs) noexcept
221 {
222 hi_axiom(lhs.holds_invariant() && rhs.holds_invariant());
223 return extent{lhs._v + rhs._v};
224 }
225
231 [[nodiscard]] constexpr friend extent operator-(extent const &lhs, extent const &rhs) noexcept
232 {
233 hi_axiom(lhs.holds_invariant() && rhs.holds_invariant());
234 return extent{lhs._v - rhs._v};
235 }
236
237 constexpr friend scale<D> operator/(extent const &lhs, extent const &rhs) noexcept;
238
244 [[nodiscard]] constexpr friend extent operator*(extent const &lhs, float const &rhs) noexcept
245 {
246 hi_axiom(lhs.holds_invariant());
247 return extent{lhs._v * rhs};
248 }
249
250 template<int E>
251 [[nodiscard]] constexpr friend auto operator+(extent const &lhs, vector<E> const &rhs) noexcept
252 {
253 hi_axiom(lhs.holds_invariant());
254 hi_axiom(rhs.holds_invariant());
255
256 return extent<std::max(D, E)>{static_cast<f32x4>(lhs) + static_cast<f32x4>(rhs)};
257 }
258
259 template<int E>
260 [[nodiscard]] constexpr friend auto operator+(vector<E> const &lhs, extent const &rhs) noexcept
261 {
262 hi_axiom(lhs.holds_invariant());
263 hi_axiom(rhs.holds_invariant());
264
265 return vector<std::max(D, E)>{static_cast<f32x4>(lhs) + static_cast<f32x4>(rhs)};
266 }
267
273 [[nodiscard]] constexpr friend extent operator+(extent const &lhs, float const &rhs) noexcept
274 {
275 hi_axiom(lhs.holds_invariant());
276
277 auto r = extent{};
278 for (std::size_t i = 0; i != D; ++i) {
279 r._v[i] = lhs._v[i] + rhs;
280 }
281
282 return r;
283 }
284
290 [[nodiscard]] constexpr friend extent operator*(float const &lhs, extent const &rhs) noexcept
291 {
292 hi_axiom(rhs.holds_invariant());
293 return extent{lhs * rhs._v};
294 }
295
301 [[nodiscard]] constexpr friend bool operator==(extent const &lhs, extent const &rhs) noexcept
302 {
303 hi_axiom(lhs.holds_invariant() && rhs.holds_invariant());
304 return lhs._v == rhs._v;
305 }
306
307 [[nodiscard]] constexpr friend std::partial_ordering operator<=>(extent const &lhs, extent const &rhs) noexcept
308 requires(D == 3)
309 {
310 constexpr std::size_t mask = 0b111;
311
312 hilet equal = eq(lhs._v, rhs._v) & mask;
313 if (equal == mask) {
314 // Only equivalent if all elements are equal.
315 return std::partial_ordering::equivalent;
316 }
317
318 hilet less = lt(lhs._v, rhs._v) & mask;
319 if ((less | equal) == mask) {
320 // If one or more elements is less (but none are greater) then the ordering is less.
321 return std::partial_ordering::less;
322 }
323
324 hilet greater = lt(lhs._v, rhs._v) & mask;
325 if ((greater | equal) == mask) {
326 // If one or more elements is greater (but none are less) then the ordering is greater.
327 return std::partial_ordering::greater;
328 }
329
330 // Some elements are less and others are greater, we don't have an ordering.
331 return std::partial_ordering::unordered;
332 }
333
334 [[nodiscard]] constexpr friend std::partial_ordering operator<=>(extent const &lhs, extent const &rhs) noexcept
335 requires(D == 2)
336 {
337 constexpr std::size_t mask = 0b11;
338
339 hilet equal = eq(lhs._v, rhs._v) & mask;
340 if (equal == mask) {
341 // Only equivalent if all elements are equal.
342 return std::partial_ordering::equivalent;
343 }
344
345 hilet less = lt(lhs._v, rhs._v) & mask;
346 if ((less | equal) == mask) {
347 // If one or more elements is less (but none are greater) then the ordering is less.
348 return std::partial_ordering::less;
349 }
350
351 hilet greater = lt(lhs._v, rhs._v) & mask;
352 if ((greater | equal) == mask) {
353 // If one or more elements is greater (but none are less) then the ordering is greater.
354 return std::partial_ordering::greater;
355 }
356
357 // Some elements are less and others are greater, we don't have an ordering.
358 return std::partial_ordering::unordered;
359 }
360
365 [[nodiscard]] hi_force_inline constexpr friend float squared_hypot(extent const& rhs) noexcept
366 {
367 hi_axiom(rhs.holds_invariant());
368 return squared_hypot<element_mask>(rhs._v);
369 }
370
375 [[nodiscard]] constexpr friend float hypot(extent const &rhs) noexcept
376 {
377 hi_axiom(rhs.holds_invariant());
378 return hypot<element_mask>(rhs._v);
379 }
380
385 [[nodiscard]] constexpr friend float rcp_hypot(extent const &rhs) noexcept
386 {
387 hi_axiom(rhs.holds_invariant());
388 return rcp_hypot<element_mask>(rhs._v);
389 }
390
395 [[nodiscard]] constexpr friend extent normalize(extent const &rhs) noexcept
396 {
397 hi_axiom(rhs.holds_invariant());
398 return extent{normalize<element_mask>(rhs._v)};
399 }
400
401 [[nodiscard]] constexpr friend extent ceil(extent const &rhs) noexcept
402 {
403 hi_axiom(rhs.holds_invariant());
404 return extent{ceil(f32x4{rhs})};
405 }
406
407 [[nodiscard]] constexpr friend extent floor(extent const &rhs) noexcept
408 {
409 hi_axiom(rhs.holds_invariant());
410 return extent{floor(static_cast<f32x4>(rhs))};
411 }
412
413 [[nodiscard]] constexpr friend extent round(extent const &rhs) noexcept
414 {
415 hi_axiom(rhs.holds_invariant());
416 return extent{round(static_cast<f32x4>(rhs))};
417 }
418
419 [[nodiscard]] constexpr friend extent min(extent const &lhs, extent const &rhs) noexcept
420 {
421 return extent{min(static_cast<f32x4>(lhs), static_cast<f32x4>(rhs))};
422 }
423
424 [[nodiscard]] constexpr friend extent max(extent const &lhs, extent const &rhs) noexcept
425 {
426 return extent{max(static_cast<f32x4>(lhs), static_cast<f32x4>(rhs))};
427 }
428
429 [[nodiscard]] constexpr friend extent clamp(extent const &value, extent const &min, extent const &max) noexcept
430 {
431 return extent{clamp(static_cast<f32x4>(value), static_cast<f32x4>(min), static_cast<f32x4>(max))};
432 }
433
438 [[nodiscard]] constexpr bool holds_invariant() const noexcept
439 {
440 return _v.x() >= 0.0f && _v.y() >= 0.0f && _v.z() >= 0.0f && _v.w() == 0.0f && (D == 3 || _v.z() == 0.0f);
441 }
442
443 [[nodiscard]] friend std::string to_string(extent const &rhs) noexcept
444 {
445 if constexpr (D == 2) {
446 return std::format("[{}, {}]", rhs._v.x(), rhs._v.y());
447 } else if constexpr (D == 3) {
448 return std::format("[{}, {}, {}]", rhs._v.x(), rhs._v.y(), rhs._v.z());
449 } else {
450 hi_static_no_default();
451 }
452 }
453
454 friend std::ostream &operator<<(std::ostream &lhs, extent const &rhs) noexcept
455 {
456 return lhs << to_string(rhs);
457 }
458
459private:
460 f32x4 _v;
461
462 static constexpr std::size_t element_mask = (1_uz << D) - 1;
463};
464
465} // namespace geo
466
467using extent2 = geo::extent<2>;
468using extent3 = geo::extent<3>;
469
470} // namespace hi::inline v1
471
472template<typename CharT>
473struct std::formatter<hi::geo::extent<2>, CharT> {
474 auto parse(auto &pc)
475 {
476 return pc.end();
477 }
478
479 auto format(hi::geo::extent<2> const &t, auto &fc)
480 {
481 return std::vformat_to(fc.out(), "[{}, {}]", std::make_format_args(t.width(), t.height()));
482 }
483};
484
485template<typename CharT>
486struct std::formatter<hi::geo::extent<3>, CharT> : formatter<float, CharT> {
487 auto parse(auto &pc)
488 {
489 return pc.end();
490 }
491
492 auto format(hi::geo::extent<3> const &t, auto &fc)
493 {
494 return std::vformat_to(fc.out(), "[{}, {}, {}]", std::make_format_args(t.width(), t.height(), t.depth()));
495 }
496};
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
The HikoGUI namespace.
Definition ascii.hpp:19
Definition scale.hpp:16
A high-level geometric extent.
Definition extent.hpp:23
constexpr extent() noexcept
Construct a empty extent / zero length.
Definition extent.hpp:69
constexpr extent(extent< E > const &other) noexcept
Construct a extent from a lower dimension extent.
Definition extent.hpp:35
constexpr friend extent operator*(float const &lhs, extent const &rhs) noexcept
Scale the extent by a scaler.
Definition extent.hpp:290
constexpr bool holds_invariant() const noexcept
Check if the extent is valid.
Definition extent.hpp:438
constexpr friend extent operator-(extent const &lhs, extent const &rhs) noexcept
Subtract two extents from each other.
Definition extent.hpp:231
constexpr friend extent operator*(extent const &lhs, float const &rhs) noexcept
Scale the extent by a scaler.
Definition extent.hpp:244
constexpr friend float rcp_hypot(extent const &rhs) noexcept
Get the length of the extent.
Definition extent.hpp:385
constexpr float & depth() noexcept
Access the z-as-depth element from the extent.
Definition extent.hpp:162
constexpr friend extent normalize(extent const &rhs) noexcept
Normalize a extent to a unit extent.
Definition extent.hpp:395
constexpr float const & depth() const noexcept
Access the z-as-depth element from the extent.
Definition extent.hpp:195
constexpr friend float hypot(extent const &rhs) noexcept
Get the length of the extent.
Definition extent.hpp:375
constexpr friend extent operator+(extent const &lhs, float const &rhs) noexcept
Add a scaler to the extent.
Definition extent.hpp:273
constexpr float const & height() const noexcept
Access the y-as-height element from the extent.
Definition extent.hpp:184
hi_force_inline constexpr friend float squared_hypot(extent const &rhs) noexcept
Get the squared length of the extent.
Definition extent.hpp:365
constexpr float & height() noexcept
Access the y-as-height element from the extent.
Definition extent.hpp:151
constexpr extent(float width, float height) noexcept
Construct a 2D extent from the width and height.
Definition extent.hpp:78
constexpr friend bool operator==(extent const &lhs, extent const &rhs) noexcept
Compare if two extents are equal.
Definition extent.hpp:301
constexpr float const & width() const noexcept
Access the x-as-width element from the extent.
Definition extent.hpp:173
constexpr float & width() noexcept
Access the x-as-width element from the extent.
Definition extent.hpp:140
constexpr friend extent operator+(extent const &lhs, extent const &rhs) noexcept
Add two extents from each other.
Definition extent.hpp:220
constexpr extent(float width, float height, float depth=0.0f) noexcept
Construct a 3D extent from width, height and depth.
Definition extent.hpp:88
A high-level geometric vector Part of the high-level vector, point, mat and color types.
Definition vector.hpp:21
T equal(T... args)
T floor(T... args)
T infinity(T... args)
T max(T... args)
T min(T... args)
T nan(T... args)
T round(T... args)
T signaling_NaN(T... args)
T to_string(T... args)