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