1use f3l_core::{
2 compute_covariance_matrix, get_minmax,
3 glam::{Mat3A, Vec2, Vec3A},
4 jacobi_eigen_square_n,
5 serde::{self, Deserialize, Serialize},
6 BasicFloat, EigenSet, F3lCast,
7};
8
9#[inline]
11pub fn aabb<P, T: BasicFloat, const D: usize>(cloud: &[P]) -> (P, P)
12where
13 P: Into<[T; D]> + Clone + Copy,
14 [T; D]: Into<P>,
15{
16 get_minmax(cloud)
17}
18
19#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
35#[serde(crate = "self::serde")]
36pub struct OBB<T: BasicFloat, const D: usize, P>
37where
38 P: Into<[T; D]> + Clone + Copy + Send + Sync + std::ops::Index<usize, Output = T>,
39 [T; D]: Into<P>,
40{
41 pub center: P,
43 pub primary: P,
45 pub secondary: P,
47 pub tertiary: P,
49 pub length: P,
51}
52
53impl<T: BasicFloat, const D: usize, P> OBB<T, D, P>
54where
55 P: Into<[T; D]> + Clone + Copy + Send + Sync + std::ops::Index<usize, Output = T>,
56 [T; D]: Into<P>,
57{
58 pub fn compute(cloud: &[P]) -> Self {
59 assert!(D == 2 || D == 3);
60
61 let (cov, _) = compute_covariance_matrix(cloud);
62
63 let eigen = EigenSet(jacobi_eigen_square_n(cov));
64
65 let major: Vec3A = if D == 3 {
66 eigen[0].eigenvector.cast_f32::<3>().into()
67 } else {
68 Vec3A::from(Vec2::from(eigen[0].eigenvector.cast_f32::<2>()).extend(0f32))
69 }
70 .normalize();
71 let second: Vec3A = if D == 3 {
72 eigen[1].eigenvector.cast_f32::<3>().into()
73 } else {
74 Vec3A::from(Vec2::from(eigen[1].eigenvector.cast_f32::<2>()).extend(0f32))
75 }
76 .normalize();
77
78 let third = if D == 3 {
79 major.cross(second)
80 } else {
81 Vec3A::Z
82 }
83 .normalize();
84 let second = third.cross(major);
85 let major = second.cross(third);
86
87 let mat = Mat3A::from_cols(major, second, third);let mut mean = Vec3A::ZERO;
90 let mut min = Vec3A::MAX;
91 let mut max = Vec3A::MIN;
92
93 cloud.iter().for_each(|p| {
94 for i in 0..D {
95 mean[i] += p[i].to_f32().unwrap();
96 }
97 });
98 mean /= cloud.len() as f32;
99
100 cloud.iter().for_each(|p| {
101 let mut slice = [0f32, 0., 0.];
102 for i in 0..D {
103 slice[i] = p[i].to_f32().unwrap();
104 }
105 let p: Vec3A = slice.into();
106 let x = (p - mean).dot(major);
107 let y = (p - mean).dot(second);
108 let z = (p - mean).dot(third);
109
110 min.x = min.x.min(x);
111 min.y = min.y.min(y);
112 min.z = min.z.min(z);
113 max.x = max.x.max(x);
114 max.y = max.y.max(y);
115 max.z = max.z.max(z);
116 });
117 let shift = (max + min) * 0.5f32;
118
119 let aligned_center = mean + mat.mul_vec3a(shift);
120 let mut center = [T::zero(); D];
121 let mut primary = [T::zero(); D];
122 let mut secondary = [T::zero(); D];
123 let mut tertiary = [T::zero(); D];
124 let mut length = [T::zero(); D];
125 (0..D).for_each(|i| {
126 center[i] = T::from(aligned_center[i]).unwrap();
127 primary[i] = T::from(major[i]).unwrap();
128 secondary[i] = T::from(second[i]).unwrap();
129 tertiary[i] = T::from(third[i]).unwrap();
130 length[i] = T::from((max[i] - min[i]) * 0.5f32).unwrap().abs();
131 });
132
133 Self {
134 center: center.into(),
135 primary: primary.into(),
136 secondary: secondary.into(),
137 tertiary: tertiary.into(),
138 length: length.into(),
139 }
140 }
141}
142
143#[test]
144fn obb_2d() {
145 use f3l_core::round_slice_n;
146 let cloud = vec![[1f32, 0.], [0., 1.], [3., 2.], [2., 3.]];
147 let result = OBB::compute(&cloud);
148
149 assert_eq!([1.5f32, 1.5], round_slice_n(result.center, 4));
150 assert_eq!([1.4142, 0.7071], round_slice_n(result.length, 4));
151}