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}