ranim_core/anchor.rs
1//! Anchor
2//!
3//! Ranim has an anchor system based on generics, an anchor can be any type `T`,
4//! and types that implement [`crate::anchor::Locate<T>`] can use
5//! [`crate::anchor::Locate::locate`] to convert the anchor to a [`glam::DVec3`] point.
6//!
7//! Ranim provides some built-in anchors and related [`crate::anchor::Locate`] implementations:
8//! - [`glam::DVec3`]: The point itself in 3d space.
9//! - [`crate::anchor::Centroid`]: The avg point of all points.
10//! Note that sometime the center of Aabb is not the centroid.
11//! (0, 0, 0) is the center point.
12//! - [`crate::anchor::AabbPoint`]: A point based on [`crate::anchor::Aabb`]'s size,
13//! the number in each axis means the fraction of the size of the [`crate::anchor::Aabb`].
14
15use glam::DVec3;
16use tracing::warn;
17
18/// Locate a point.
19pub trait Locate<T: ?Sized> {
20 /// Locate self on the target
21 fn locate(&self, target: &T) -> DVec3;
22}
23
24impl<T: ?Sized> Locate<T> for DVec3 {
25 fn locate(&self, _target: &T) -> DVec3 {
26 *self
27 }
28}
29
30/// The centroid.
31///
32/// Avg of all points.
33pub struct Centroid;
34
35impl Locate<DVec3> for Centroid {
36 fn locate(&self, target: &DVec3) -> DVec3 {
37 *target
38 }
39}
40
41impl<T> Locate<[T]> for Centroid
42where
43 Centroid: Locate<T>,
44{
45 fn locate(&self, target: &[T]) -> DVec3 {
46 target.iter().map(|x| self.locate(x)).sum::<DVec3>() / target.len() as f64
47 }
48}
49
50/// A point based on [`Aabb`], the number in each axis means the fraction of the size of the [`Aabb`].
51/// (0, 0, 0) is the center point.
52/// ```text
53/// +Y
54/// |
55/// |
56/// +----- +X
57/// /
58/// +Z
59/// ```
60#[derive(Debug, Clone, Copy, PartialEq)]
61pub struct AabbPoint(pub DVec3);
62
63impl AabbPoint {
64 /// Center point, shorthand of `Anchor(DVec3::ZERO)`.
65 pub const CENTER: Self = Self(DVec3::ZERO);
66 // /// Left point (-X)
67 // pub const LEFT: Self = Self(DVec3::NEG_X);
68 // /// Right point (+X)
69 // pub const RIGHT: Self = Self(DVec3::X);
70 // /// Top point (+Y)
71 // pub const TOP: Self = Self(DVec3::Y);
72 // /// Bottom point (-Y)
73 // pub const BOTTOM: Self = Self(DVec3::NEG_Y);
74 // /// Top Right point (+X, +Y)
75 // pub const TOP_RIGHT: Self = Self(dvec3(1.0, 1.0, 0.0));
76 // /// Top Left point (-X, +Y)
77 // pub const TOP_LEFT: Self = Self(dvec3(-1.0, 1.0, 0.0));
78 // /// Bottom Right point (+X, -Y)
79 // pub const BOTTOM_RIGHT: Self = Self(dvec3(1.0, -1.0, 0.0));
80 // /// Bottom Left point (-X, -Y)
81 // pub const BOTTOM_LEFT: Self = Self(dvec3(-1.0, -1.0, 0.0));
82}
83
84impl<T: Aabb + ?Sized> Locate<T> for AabbPoint {
85 fn locate(&self, target: &T) -> DVec3 {
86 let center = target.aabb_center();
87 let half_size = target.aabb_size() / 2.0;
88 center + self.0 * half_size
89 }
90}
91
92/// Axis-Aligned Bounding Box
93///
94/// This is the basic trait for an item.
95pub trait Aabb {
96 /// Get the Axis-aligned bounding box represent in `[<min>, <max>]`.
97 fn aabb(&self) -> [DVec3; 2];
98 /// Get the size of the Aabb.
99 fn aabb_size(&self) -> DVec3 {
100 let [min, max] = self.aabb();
101 max - min
102 }
103 /// Get the center of the Aabb.
104 fn aabb_center(&self) -> DVec3 {
105 let [min, max] = self.aabb();
106 (max + min) / 2.0
107 }
108}
109
110impl Aabb for DVec3 {
111 fn aabb(&self) -> [DVec3; 2] {
112 [*self; 2]
113 }
114}
115
116impl<T: Aabb> Aabb for [T] {
117 fn aabb(&self) -> [DVec3; 2] {
118 let [min, max] = self
119 .iter()
120 .map(|x| x.aabb())
121 .reduce(|[acc_min, acc_max], [min, max]| [acc_min.min(min), acc_max.max(max)])
122 .unwrap_or([DVec3::ZERO, DVec3::ZERO]);
123 if min == max {
124 warn!("Empty bounding box, is the slice empty?")
125 }
126 [min, max]
127 }
128}
129
130impl<T: Aabb> Aabb for Vec<T> {
131 fn aabb(&self) -> [DVec3; 2] {
132 self.as_slice().aabb()
133 }
134}