MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Bounds.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "HitTest.hpp"
4
5namespace MayaFlux::Kinesis {
6
7// =============================================================================
8// AABB1D
9// =============================================================================
10
11/**
12 * @struct AABB1D
13 * @brief Closed interval on a scalar axis.
14 *
15 * The one dimensional sibling of AABB2D and AABB3D, in double precision
16 * because scalar streams (audio, control, analysis) are double throughout.
17 * No vector type is involved, so a window of samples can be tested against
18 * it without building one.
19 */
20struct AABB1D {
21 double min { -1.0 };
22 double max { 1.0 };
23
24 [[nodiscard]] bool contains(double p) const noexcept
25 {
26 return p >= min && p <= max;
27 }
28
29 [[nodiscard]] bool overlaps(const AABB1D& other) const noexcept
30 {
31 return min <= other.max && max >= other.min;
32 }
33
34 [[nodiscard]] double width() const noexcept { return max - min; }
35
36 [[nodiscard]] double center() const noexcept { return (min + max) * 0.5; }
37
38 [[nodiscard]] AABB1D translated(double offset) const noexcept
39 {
40 return { .min = min + offset, .max = max + offset };
41 }
42
43 [[nodiscard]] AABB1D expanded(double margin) const noexcept
44 {
45 return { .min = min - margin, .max = max + margin };
46 }
47};
48
49// =============================================================================
50// AABB2D
51// =============================================================================
52
53/**
54 * @struct AABB2D
55 * @brief Axis-aligned bounding rectangle in a 2D coordinate space.
56 *
57 * Used as a fast-reject hint in Portal::Forma::Element. All coordinates
58 * are in NDC by convention when used in Forma: x and y in [-1, +1],
59 * center origin, +Y up, matching ViewTransform.hpp and Windowing.hpp.
60 *
61 * Named constructors convert from other coordinate spaces into NDC.
62 */
63struct AABB2D {
64 glm::vec2 min;
65 glm::vec2 max;
66
67 [[nodiscard]] bool contains(glm::vec2 p) const noexcept
68 {
69 return p.x >= min.x && p.x <= max.x
70 && p.y >= min.y && p.y <= max.y;
71 }
72
73 [[nodiscard]] bool overlaps(const AABB2D& other) const noexcept
74 {
75 return min.x <= other.max.x && max.x >= other.min.x
76 && min.y <= other.max.y && max.y >= other.min.y;
77 }
78
79 [[nodiscard]] float width() const noexcept { return max.x - min.x; }
80 [[nodiscard]] float height() const noexcept { return max.y - min.y; }
81
82 [[nodiscard]] glm::vec2 center() const noexcept { return (min + max) * 0.5F; }
83
84 [[nodiscard]] AABB2D translated(glm::vec2 offset) const noexcept
85 {
86 return { .min = min + offset, .max = max + offset };
87 }
88
89 [[nodiscard]] AABB2D expanded(float margin) const noexcept
90 {
91 return { .min = min - glm::vec2(margin), .max = max + glm::vec2(margin) };
92 }
93
94 [[nodiscard]] static AABB2D from_ndc(glm::vec2 center, glm::vec2 half) noexcept
95 {
96 return { .min = center - half, .max = center + half };
97 }
98
99 /**
100 * @brief Construct from pixel-space top-left rect, converted to NDC.
101 * @param x Left edge in pixels.
102 * @param y Top edge in pixels (top-left origin, +Y down).
103 * @param w Width in pixels.
104 * @param h Height in pixels.
105 * @param win_w Window width in pixels.
106 * @param win_h Window height in pixels.
107 */
108 [[nodiscard]] static AABB2D from_pixel(
109 float x, float y, float w, float h,
110 uint32_t win_w, uint32_t win_h) noexcept
111 {
112 auto nx = [&](float px) {
113 return (px / static_cast<float>(win_w)) * 2.0F - 1.0F;
114 };
115 auto ny = [&](float py) {
116 return 1.0F - (py / static_cast<float>(win_h)) * 2.0F;
117 };
118 return { .min = glm::vec2(nx(x), ny(y + h)), .max = glm::vec2(nx(x + w), ny(y)) };
119 }
120
121 /**
122 * @brief Construct from a TextureBuffer NDC position and scale.
123 * @param ndc_position Center as returned by TextureBuffer::get_position().
124 * @param ndc_scale Extent as returned by TextureBuffer::get_scale().
125 */
126 [[nodiscard]] static AABB2D from_buffer_transform(
127 glm::vec2 ndc_position, glm::vec2 ndc_scale) noexcept
128 {
129 glm::vec2 half = ndc_scale * 0.5F;
130 return { .min = ndc_position - half, .max = ndc_position + half };
131 }
132};
133
134// =============================================================================
135// BoundingSphere
136// =============================================================================
137
138/**
139 * @struct BoundingSphere
140 * @brief Spherical fast-reject hint in world space.
141 *
142 * Used as the cheap pre-filter for 3D spatial predicates, analogous to
143 * AABB2D in the 2D path. A sphere is a better pre-filter than an axis-aligned
144 * box in 3D: rotation-invariant, one dot product to test a point, one
145 * discriminant to test a ray.
146 */
148 glm::vec3 center;
149 float radius;
150
151 [[nodiscard]] bool contains(const glm::vec3& p) const noexcept
152 {
153 glm::vec3 d = p - center;
154 return glm::dot(d, d) <= radius * radius;
155 }
156
157 /**
158 * @brief Returns true if the ray passes through or originates inside the sphere.
159 * @param ray World-space ray with normalized direction.
160 */
161 [[nodiscard]] bool overlaps_ray(const Ray& ray) const noexcept
162 {
163 glm::vec3 oc = ray.origin - center;
164 float b = glm::dot(oc, ray.direction);
165 float c = glm::dot(oc, oc) - radius * radius;
166 const float discriminant = b * b - c;
167 if (discriminant < 0.0F)
168 return false;
169 return -b + std::sqrt(discriminant) >= 0.0F;
170 }
171};
172
173// =============================================================================
174// AABB3D
175// =============================================================================
176
177/**
178 * @struct AABB3D
179 * @brief Axis-aligned bounding box in 3D world space.
180 *
181 * Counterpart to AABB2D for three-dimensional geometry. Used by
182 * Kinesis::aabb() to describe the extent of any PositionCarrying span,
183 * and as a fast-reject pre-filter in 3D spatial predicates.
184 *
185 * No coordinate convention is enforced; the box is whatever the caller's
186 * position data implies (world space, object space, NDC-extended, etc.).
187 */
188struct AABB3D {
189 glm::vec3 min;
190 glm::vec3 max;
191
192 [[nodiscard]] bool contains(const glm::vec3& p) const noexcept
193 {
194 return p.x >= min.x && p.x <= max.x
195 && p.y >= min.y && p.y <= max.y
196 && p.z >= min.z && p.z <= max.z;
197 }
198
199 [[nodiscard]] bool overlaps(const AABB3D& other) const noexcept
200 {
201 return min.x <= other.max.x && max.x >= other.min.x
202 && min.y <= other.max.y && max.y >= other.min.y
203 && min.z <= other.max.z && max.z >= other.min.z;
204 }
205
206 [[nodiscard]] glm::vec3 center() const noexcept { return (min + max) * 0.5F; }
207 [[nodiscard]] glm::vec3 extent() const noexcept { return max - min; }
208 [[nodiscard]] glm::vec3 half_extent() const noexcept { return (max - min) * 0.5F; }
209
210 [[nodiscard]] AABB3D translated(const glm::vec3& offset) const noexcept
211 {
212 return { .min = min + offset, .max = max + offset };
213 }
214
215 [[nodiscard]] AABB3D expanded(float margin) const noexcept
216 {
217 return { .min = min - glm::vec3(margin), .max = max + glm::vec3(margin) };
218 }
219
220 /**
221 * @brief Construct from a center point and half-extents.
222 * @param c Box centre.
223 * @param half Half-size along each axis.
224 */
225 [[nodiscard]] static AABB3D from_center(const glm::vec3& c, const glm::vec3& half) noexcept
226 {
227 return { .min = c - half, .max = c + half };
228 }
229
230 /**
231 * @brief Construct the tightest AABB enclosing a BoundingSphere.
232 * @param s Source sphere.
233 */
234 [[nodiscard]] static AABB3D from_sphere(const BoundingSphere& s) noexcept
235 {
236 return { .min = s.center - glm::vec3(s.radius),
237 .max = s.center + glm::vec3(s.radius) };
238 }
239};
240
241// =============================================================================
242// Containment callables
243// All functions return std::function<bool(glm::vec2)> suitable for
244// direct assignment to Portal::Forma::Element::contains.
245// =============================================================================
246
247/**
248 * @brief Containment test for a circle.
249 * @param center NDC center.
250 * @param radius Radius in NDC units.
251 */
252[[nodiscard]] inline std::function<bool(glm::vec2)>
253circular_bounds(glm::vec2 center, float radius) noexcept
254{
255 float r2 = radius * radius;
256 return [center, r2](glm::vec2 p) {
257 glm::vec2 d = p - center;
258 return d.x * d.x + d.y * d.y <= r2;
259 };
260}
261
262/**
263 * @brief Containment test for a convex or concave polygon.
264 *
265 * Uses the winding number algorithm, which handles both convex and
266 * non-convex polygons correctly. Vertices are in NDC, ordered either
267 * clockwise or counter-clockwise.
268 *
269 * @param vertices Polygon vertices. Copied into the closure.
270 */
271[[nodiscard]] inline std::function<bool(glm::vec2)>
272polygon_bounds(std::span<const glm::vec2> vertices)
273{
274 std::vector<glm::vec2> verts(vertices.begin(), vertices.end());
275 return [verts = std::move(verts)](glm::vec2 p) {
276 int winding = 0;
277 size_t n = verts.size();
278 for (size_t i = 0; i < n; ++i) {
279 glm::vec2 a = verts[i];
280 glm::vec2 b = verts[(i + 1) % n];
281 if (a.y <= p.y) {
282 if (b.y > p.y) {
283 float cross = (b.x - a.x) * (p.y - a.y)
284 - (b.y - a.y) * (p.x - a.x);
285 if (cross > 0.0F)
286 ++winding;
287 }
288 } else {
289 if (b.y <= p.y) {
290 float cross = (b.x - a.x) * (p.y - a.y)
291 - (b.y - a.y) * (p.x - a.x);
292 if (cross < 0.0F)
293 --winding;
294 }
295 }
296 }
297 return winding != 0;
298 };
299}
300
301/**
302 * @brief Containment test for a polyline with a uniform half-thickness.
303 *
304 * Covers open and closed curves equally. A point is inside if its
305 * perpendicular distance to any segment is within @p half_thickness.
306 * Suitable for stroke-based interactive regions and cable hit testing.
307 *
308 * @param points Ordered polyline vertices in NDC.
309 * @param half_thickness Distance threshold in NDC units.
310 */
311[[nodiscard]] inline std::function<bool(glm::vec2)>
312stroke_bounds(std::span<const glm::vec2> points, float half_thickness)
313{
314 std::vector<glm::vec2> pts(points.begin(), points.end());
315 float t2 = half_thickness * half_thickness;
316 return [pts = std::move(pts), t2](glm::vec2 p) {
317 for (size_t i = 0; i + 1 < pts.size(); ++i) {
318 glm::vec2 a = pts[i];
319 glm::vec2 b = pts[i + 1];
320 glm::vec2 ab = b - a;
321 float len2 = glm::dot(ab, ab);
322 float d2 = 0.0F;
323 if (len2 < 1e-12F) {
324 glm::vec2 ap = p - a;
325 d2 = glm::dot(ap, ap);
326 } else {
327 float t = glm::clamp(glm::dot(p - a, ab) / len2, 0.0F, 1.0F);
328 glm::vec2 proj = a + t * ab;
329 glm::vec2 diff = p - proj;
330 d2 = glm::dot(diff, diff);
331 }
332 if (d2 <= t2)
333 return true;
334 }
335 return false;
336 };
337}
338
339// =============================================================================
340// 3D containment callables
341// All functions return std::function<bool(glm::vec3)> suitable for
342// direct use as spatial predicates in Nexus and NavigationState constraints.
343// Pair with BoundingSphere for a cheap pre-filter where the predicate is
344// expensive.
345// =============================================================================
346
347/**
348 * @brief Containment test for a sphere.
349 * @param center World-space center.
350 * @param radius Radius in world units.
351 */
352[[nodiscard]] inline std::function<bool(glm::vec3)>
353circular_bounds(glm::vec3 center, float radius) noexcept
354{
355 float r2 = radius * radius;
356 return [center, r2](const glm::vec3& p) {
357 glm::vec3 d = p - center;
358 return glm::dot(d, d) <= r2;
359 };
360}
361
362/**
363 * @brief Containment test for a convex volume defined by inward-facing half-planes.
364 *
365 * A point is inside if it satisfies all half-planes: dot(normal, p) >= offset
366 * for every plane. Normals must point inward. Suitable for authored rooms,
367 * corridors, and any convex spatial zone. Non-convex volumes are composed
368 * via union_region3 / subtract_region3.
369 *
370 * @param planes Pairs of (inward normal, signed offset). Copied into closure.
371 */
372[[nodiscard]] inline std::function<bool(glm::vec3)>
373convex_region(std::span<const std::pair<glm::vec3, float>> planes)
374{
375 std::vector<std::pair<glm::vec3, float>> ps(planes.begin(), planes.end());
376 return [ps = std::move(ps)](const glm::vec3& p) {
377 return std::ranges::all_of(
378 ps,
379 [&p](const auto& plane) {
380 const auto& [n, d] = plane;
381 return glm::dot(n, p) >= d;
382 });
383 };
384}
385
386/**
387 * @brief Containment test for a vertically extruded 2D polygon.
388 *
389 * The footprint is tested with the winding number algorithm in the XZ plane.
390 * The Y axis is the vertical: the point must satisfy y_min <= p.y <= y_max.
391 * Covers authored rooms, columns, corridors where the cross-section is
392 * constant along Y.
393 *
394 * @param footprint Polygon vertices in XZ. Copied into closure.
395 * @param y_min Lower Y bound (inclusive).
396 * @param y_max Upper Y bound (inclusive).
397 */
398[[nodiscard]] inline std::function<bool(glm::vec3)>
400 std::span<const glm::vec2> footprint,
401 float y_min,
402 float y_max)
403{
404 std::vector<glm::vec2> verts(footprint.begin(), footprint.end());
405 return [verts = std::move(verts), y_min, y_max](const glm::vec3& p) {
406 if (p.y < y_min || p.y > y_max)
407 return false;
408 glm::vec2 q { p.x, p.z };
409 int winding = 0;
410 const size_t n = verts.size();
411 for (size_t i = 0; i < n; ++i) {
412 glm::vec2 a = verts[i];
413 glm::vec2 b = verts[(i + 1) % n];
414 if (a.y <= q.y) {
415 if (b.y > q.y) {
416 float cross = (b.x - a.x) * (q.y - a.y)
417 - (b.y - a.y) * (q.x - a.x);
418 if (cross > 0.0F)
419 ++winding;
420 }
421 } else {
422 if (b.y <= q.y) {
423 float cross = (b.x - a.x) * (q.y - a.y)
424 - (b.y - a.y) * (q.x - a.x);
425 if (cross < 0.0F)
426 --winding;
427 }
428 }
429 }
430 return winding != 0;
431 };
432}
433
434// =============================================================================
435// Combinators
436// =============================================================================
437
438/**
439 * @brief Union of two containment tests. Point is inside if either returns true.
440 */
441[[nodiscard]] inline std::function<bool(glm::vec2)>
443 std::function<bool(glm::vec2)> a,
444 std::function<bool(glm::vec2)> b)
445{
446 return [a = std::move(a), b = std::move(b)](glm::vec2 p) {
447 return a(p) || b(p);
448 };
449}
450
451/**
452 * @brief Intersection of two containment tests. Point is inside only if both return true.
453 */
454[[nodiscard]] inline std::function<bool(glm::vec2)>
456 std::function<bool(glm::vec2)> a,
457 std::function<bool(glm::vec2)> b)
458{
459 return [a = std::move(a), b = std::move(b)](glm::vec2 p) {
460 return a(p) && b(p);
461 };
462}
463
464/**
465 * @brief Difference of two containment tests. Point is inside if @p a is true and @p b is false.
466 * Useful for donuts, cutouts, and exclusion zones.
467 */
468[[nodiscard]] inline std::function<bool(glm::vec2)>
470 std::function<bool(glm::vec2)> a,
471 std::function<bool(glm::vec2)> b)
472{
473 return [a = std::move(a), b = std::move(b)](glm::vec2 p) {
474 return a(p) && !b(p);
475 };
476}
477
478/**
479 * @brief Convert an NDC AABB's extent to integer pixel dimensions.
480 *
481 * Equivalent to ndc_size_to_pixels(region.max - region.min, width, height).
482 *
483 * @param region NDC axis-aligned bounding box.
484 * @param width Window width in pixels.
485 * @param height Window height in pixels.
486 * @return Integer pixel dimensions of the region's extent.
487 */
488[[nodiscard]] inline glm::uvec2 ndc_size_to_pixels(
489 const AABB2D& region,
490 uint32_t width, uint32_t height)
491{
492 return ndc_size_to_pixels(region.max - region.min, width, height);
493}
494
495// =============================================================================
496// 3D combinators
497// =============================================================================
498
499/**
500 * @brief Union of two 3D containment tests.
501 */
502[[nodiscard]] inline std::function<bool(glm::vec3)>
504 std::function<bool(glm::vec3)> a,
505 std::function<bool(glm::vec3)> b)
506{
507 return [a = std::move(a), b = std::move(b)](const glm::vec3& p) {
508 return a(p) || b(p);
509 };
510}
511
512/**
513 * @brief Intersection of two 3D containment tests.
514 */
515[[nodiscard]] inline std::function<bool(glm::vec3)>
517 std::function<bool(glm::vec3)> a,
518 std::function<bool(glm::vec3)> b)
519{
520 return [a = std::move(a), b = std::move(b)](const glm::vec3& p) {
521 return a(p) && b(p);
522 };
523}
524
525/**
526 * @brief Difference of two 3D containment tests. Inside @p a but not @p b.
527 */
528[[nodiscard]] inline std::function<bool(glm::vec3)>
530 std::function<bool(glm::vec3)> a,
531 std::function<bool(glm::vec3)> b)
532{
533 return [a = std::move(a), b = std::move(b)](const glm::vec3& p) {
534 return a(p) && !b(p);
535 };
536}
537
538/**
539 * @brief Direction of a placed region relative to its anchor.
540 *
541 * Over ignores the gap argument and centers the new region on the anchor.
542 */
543enum class Side : uint8_t {
544 Right,
545 Left,
546 Above,
547 Below,
548 Over,
549};
550
551/**
552 * @brief Cross-axis alignment of a placed region against its anchor.
553 *
554 * Start and End refer to the anchor's leading and trailing edge on the cross
555 * axis: min.x and max.x for vertical placement, max.y and min.y for
556 * horizontal placement, so Start is always the visually upper or leftward
557 * edge in NDC. Span matches the anchor's full cross-axis extent and ignores
558 * the corresponding size argument.
559 */
560enum class Align : uint8_t {
561 Center,
562 Start,
563 End,
564 Span,
565};
566
567/**
568 * @brief Compute a region of size (@p w, @p h) positioned relative to @p anchor.
569 *
570 * @param anchor Region to place against.
571 * @param side Direction of placement.
572 * @param w Width in NDC units. Ignored when @p side is vertical and
573 * @p align is Span.
574 * @param h Height in NDC units. Ignored when @p side is horizontal and
575 * @p align is Span.
576 * @param gap Separation from the anchor edge. Ignored for Side::Over.
577 * @param align Cross-axis alignment.
578 */
579[[nodiscard]] inline AABB2D place(
580 AABB2D anchor, Side side, float w, float h,
581 float gap = 0.F, Align align = Align::Center) noexcept
582{
583 const glm::vec2 c = anchor.center();
584
585 if (side == Side::Over) {
586 return { .min = { c.x - w * 0.5F, c.y - h * 0.5F },
587 .max = { c.x + w * 0.5F, c.y + h * 0.5F } };
588 }
589
590 if (side == Side::Below || side == Side::Above) {
591 float x0 {};
592 float x1 {};
593 switch (align) {
594 case Align::Center:
595 x0 = c.x - w * 0.5F;
596 x1 = c.x + w * 0.5F;
597 break;
598 case Align::Start:
599 x0 = anchor.min.x;
600 x1 = anchor.min.x + w;
601 break;
602 case Align::End:
603 x0 = anchor.max.x - w;
604 x1 = anchor.max.x;
605 break;
606 case Align::Span:
607 x0 = anchor.min.x;
608 x1 = anchor.max.x;
609 break;
610 }
611 return side == Side::Below
612 ? AABB2D { .min = { x0, anchor.min.y - gap - h }, .max = { x1, anchor.min.y - gap } }
613 : AABB2D { .min = { x0, anchor.max.y + gap }, .max = { x1, anchor.max.y + gap + h } };
614 }
615
616 float y0 {};
617 float y1 {};
618 switch (align) {
619 case Align::Center:
620 y0 = c.y - h * 0.5F;
621 y1 = c.y + h * 0.5F;
622 break;
623 case Align::Start:
624 y0 = anchor.max.y - h;
625 y1 = anchor.max.y;
626 break;
627 case Align::End:
628 y0 = anchor.min.y;
629 y1 = anchor.min.y + h;
630 break;
631 case Align::Span:
632 y0 = anchor.min.y;
633 y1 = anchor.max.y;
634 break;
635 }
636 return side == Side::Right
637 ? AABB2D { .min = { anchor.max.x + gap, y0 }, .max = { anchor.max.x + gap + w, y1 } }
638 : AABB2D { .min = { anchor.min.x - gap - w, y0 }, .max = { anchor.min.x - gap, y1 } };
639}
640
641// =============================================================================
642// HasBounds
643// =============================================================================
644
645/**
646 * @brief Any type carrying a spatial footprint usable as a placement anchor.
647 *
648 * Satisfied by Portal::Forma::Mapped<T>, Collapsible, Scrollable, EntryGroup,
649 * and Entry. AABB2D itself does not satisfy it, so the overloads below
650 * never collide with the AABB2D-taking primaries.
651 */
652template <typename T>
653concept HasBounds = requires(const T& t) {
654 { t.bounds() } -> std::convertible_to<AABB2D>;
655};
656
657/// @brief Extract the anchor region from any HasBounds object.
658template <HasBounds T>
659[[nodiscard]] AABB2D bounds_of(const T& obj)
660{
661 return obj.bounds();
662}
663
664/// @brief place() accepting any HasBounds anchor.
665template <HasBounds A>
666[[nodiscard]] AABB2D place(
667 const A& anchor, Side side, float w, float h,
668 float gap = 0.F, Align align = Align::Center)
669{
670 return place(bounds_of(anchor), side, w, h, gap, align);
671}
672
673} // namespace MayaFlux::Kinesis
uint64_t offset
Definition Composite.cpp:10
std::vector< glm::vec2 > * points
float radius
uint32_t h
Definition InkPress.cpp:29
size_t a
size_t b
double q
uint32_t py
uint32_t px
uint32_t width
uint32_t height
Any type carrying a spatial footprint usable as a placement anchor.
Definition Bounds.hpp:653
Align
Cross-axis alignment of a placed region against its anchor.
Definition Bounds.hpp:560
std::function< bool(glm::vec3)> convex_region(std::span< const std::pair< glm::vec3, float > > planes)
Containment test for a convex volume defined by inward-facing half-planes.
Definition Bounds.hpp:373
std::function< bool(glm::vec2)> intersect_bounds(std::function< bool(glm::vec2)> a, std::function< bool(glm::vec2)> b)
Intersection of two containment tests.
Definition Bounds.hpp:455
std::function< bool(glm::vec2)> polygon_bounds(std::span< const glm::vec2 > vertices)
Containment test for a convex or concave polygon.
Definition Bounds.hpp:272
std::function< bool(glm::vec3)> extruded_polygon_region(std::span< const glm::vec2 > footprint, float y_min, float y_max)
Containment test for a vertically extruded 2D polygon.
Definition Bounds.hpp:399
std::function< bool(glm::vec2)> subtract_bounds(std::function< bool(glm::vec2)> a, std::function< bool(glm::vec2)> b)
Difference of two containment tests.
Definition Bounds.hpp:469
glm::uvec2 ndc_size_to_pixels(const AABB2D &region, uint32_t width, uint32_t height)
Convert an NDC AABB's extent to integer pixel dimensions.
Definition Bounds.hpp:488
AABB2D place(AABB2D anchor, Side side, float w, float h, float gap=0.F, Align align=Align::Center) noexcept
Compute a region of size (w, h) positioned relative to anchor.
Definition Bounds.hpp:579
Side
Direction of a placed region relative to its anchor.
Definition Bounds.hpp:543
std::function< bool(glm::vec2)> stroke_bounds(std::span< const glm::vec2 > points, float half_thickness)
Containment test for a polyline with a uniform half-thickness.
Definition Bounds.hpp:312
std::function< bool(glm::vec2)> circular_bounds(glm::vec2 center, float radius) noexcept
Containment test for a circle.
Definition Bounds.hpp:253
std::function< bool(glm::vec2)> union_bounds(std::function< bool(glm::vec2)> a, std::function< bool(glm::vec2)> b)
Union of two containment tests.
Definition Bounds.hpp:442
AABB2D bounds_of(const T &obj)
Extract the anchor region from any HasBounds object.
Definition Bounds.hpp:659
double center() const noexcept
Definition Bounds.hpp:36
double width() const noexcept
Definition Bounds.hpp:34
AABB1D translated(double offset) const noexcept
Definition Bounds.hpp:38
bool overlaps(const AABB1D &other) const noexcept
Definition Bounds.hpp:29
AABB1D expanded(double margin) const noexcept
Definition Bounds.hpp:43
bool contains(double p) const noexcept
Definition Bounds.hpp:24
Closed interval on a scalar axis.
Definition Bounds.hpp:20
static AABB2D from_pixel(float x, float y, float w, float h, uint32_t win_w, uint32_t win_h) noexcept
Construct from pixel-space top-left rect, converted to NDC.
Definition Bounds.hpp:108
static AABB2D from_ndc(glm::vec2 center, glm::vec2 half) noexcept
Definition Bounds.hpp:94
float height() const noexcept
Definition Bounds.hpp:80
AABB2D expanded(float margin) const noexcept
Definition Bounds.hpp:89
bool overlaps(const AABB2D &other) const noexcept
Definition Bounds.hpp:73
bool contains(glm::vec2 p) const noexcept
Definition Bounds.hpp:67
AABB2D translated(glm::vec2 offset) const noexcept
Definition Bounds.hpp:84
glm::vec2 center() const noexcept
Definition Bounds.hpp:82
float width() const noexcept
Definition Bounds.hpp:79
static AABB2D from_buffer_transform(glm::vec2 ndc_position, glm::vec2 ndc_scale) noexcept
Construct from a TextureBuffer NDC position and scale.
Definition Bounds.hpp:126
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
glm::vec3 center() const noexcept
Definition Bounds.hpp:206
bool contains(const glm::vec3 &p) const noexcept
Definition Bounds.hpp:192
bool overlaps(const AABB3D &other) const noexcept
Definition Bounds.hpp:199
glm::vec3 half_extent() const noexcept
Definition Bounds.hpp:208
static AABB3D from_sphere(const BoundingSphere &s) noexcept
Construct the tightest AABB enclosing a BoundingSphere.
Definition Bounds.hpp:234
static AABB3D from_center(const glm::vec3 &c, const glm::vec3 &half) noexcept
Construct from a center point and half-extents.
Definition Bounds.hpp:225
AABB3D translated(const glm::vec3 &offset) const noexcept
Definition Bounds.hpp:210
AABB3D expanded(float margin) const noexcept
Definition Bounds.hpp:215
glm::vec3 extent() const noexcept
Definition Bounds.hpp:207
Axis-aligned bounding box in 3D world space.
Definition Bounds.hpp:188
bool overlaps_ray(const Ray &ray) const noexcept
Returns true if the ray passes through or originates inside the sphere.
Definition Bounds.hpp:161
bool contains(const glm::vec3 &p) const noexcept
Definition Bounds.hpp:151
Spherical fast-reject hint in world space.
Definition Bounds.hpp:147
Origin and normalized direction in world space.
Definition HitTest.hpp:17