1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub enum CollisionPrimitive {
11 Sphere {
12 center: [f32; 3],
13 radius: f32,
14 },
15 Box {
16 center: [f32; 3],
17 half_extents: [f32; 3],
18 },
19 Capsule {
20 a: [f32; 3],
21 b: [f32; 3],
22 radius: f32,
23 },
24}
25
26#[allow(dead_code)]
28#[derive(Debug, Clone, Default)]
29pub struct CompoundCollision {
30 pub name: String,
31 pub primitives: Vec<CollisionPrimitive>,
32}
33
34#[allow(dead_code)]
36pub fn new_compound(name: &str) -> CompoundCollision {
37 CompoundCollision {
38 name: name.to_string(),
39 primitives: vec![],
40 }
41}
42
43#[allow(dead_code)]
45pub fn add_sphere(compound: &mut CompoundCollision, center: [f32; 3], radius: f32) {
46 compound
47 .primitives
48 .push(CollisionPrimitive::Sphere { center, radius });
49}
50
51#[allow(dead_code)]
53pub fn add_box(compound: &mut CompoundCollision, center: [f32; 3], half_extents: [f32; 3]) {
54 compound.primitives.push(CollisionPrimitive::Box {
55 center,
56 half_extents,
57 });
58}
59
60#[allow(dead_code)]
62pub fn add_capsule(compound: &mut CompoundCollision, a: [f32; 3], b: [f32; 3], radius: f32) {
63 compound
64 .primitives
65 .push(CollisionPrimitive::Capsule { a, b, radius });
66}
67
68#[allow(dead_code)]
70pub fn primitive_count(compound: &CompoundCollision) -> usize {
71 compound.primitives.len()
72}
73
74#[allow(dead_code)]
76pub fn compound_volume(compound: &CompoundCollision) -> f32 {
77 use std::f32::consts::PI;
78 compound
79 .primitives
80 .iter()
81 .map(|p| match p {
82 CollisionPrimitive::Sphere { radius, .. } => {
83 (4.0 / 3.0) * PI * radius * radius * radius
84 }
85 CollisionPrimitive::Box { half_extents, .. } => {
86 8.0 * half_extents[0] * half_extents[1] * half_extents[2]
87 }
88 CollisionPrimitive::Capsule { a, b, radius } => {
89 let len = {
90 let d = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
91 (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt()
92 };
93 PI * radius * radius * len + (4.0 / 3.0) * PI * radius * radius * radius
94 }
95 })
96 .sum()
97}
98
99#[allow(dead_code)]
101pub fn aabb_of_centers(compound: &CompoundCollision) -> Option<([f32; 3], [f32; 3])> {
102 let centers: Vec<[f32; 3]> = compound
103 .primitives
104 .iter()
105 .map(|p| match p {
106 CollisionPrimitive::Sphere { center, .. } => *center,
107 CollisionPrimitive::Box { center, .. } => *center,
108 CollisionPrimitive::Capsule { a, b, .. } => [
109 (a[0] + b[0]) * 0.5,
110 (a[1] + b[1]) * 0.5,
111 (a[2] + b[2]) * 0.5,
112 ],
113 })
114 .collect();
115 if centers.is_empty() {
116 return None;
117 }
118 let mn = centers
119 .iter()
120 .cloned()
121 .reduce(|a, b| [a[0].min(b[0]), a[1].min(b[1]), a[2].min(b[2])])?;
122 let mx = centers
123 .iter()
124 .cloned()
125 .reduce(|a, b| [a[0].max(b[0]), a[1].max(b[1]), a[2].max(b[2])])?;
126 Some((mn, mx))
127}
128
129#[allow(dead_code)]
131pub fn serialise_compound(compound: &CompoundCollision) -> Vec<f32> {
132 let mut buf = Vec::new();
133 for p in &compound.primitives {
134 match p {
135 CollisionPrimitive::Sphere { center, radius } => {
136 buf.extend_from_slice(&[0.0, center[0], center[1], center[2], *radius]);
137 }
138 CollisionPrimitive::Box {
139 center,
140 half_extents,
141 } => {
142 buf.extend_from_slice(&[
143 1.0,
144 center[0],
145 center[1],
146 center[2],
147 half_extents[0],
148 half_extents[1],
149 half_extents[2],
150 ]);
151 }
152 CollisionPrimitive::Capsule { a, b, radius } => {
153 buf.extend_from_slice(&[2.0, a[0], a[1], a[2], b[0], b[1], b[2], *radius]);
154 }
155 }
156 }
157 buf
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_new_compound() {
166 let c = new_compound("body");
167 assert_eq!(primitive_count(&c), 0);
168 }
169
170 #[test]
171 fn test_add_sphere() {
172 let mut c = new_compound("c");
173 add_sphere(&mut c, [0.0; 3], 1.0);
174 assert_eq!(primitive_count(&c), 1);
175 }
176
177 #[test]
178 fn test_add_box() {
179 let mut c = new_compound("c");
180 add_box(&mut c, [0.0; 3], [1.0; 3]);
181 assert_eq!(primitive_count(&c), 1);
182 }
183
184 #[test]
185 fn test_add_capsule() {
186 let mut c = new_compound("c");
187 add_capsule(&mut c, [0.0; 3], [0.0, 1.0, 0.0], 0.1);
188 assert_eq!(primitive_count(&c), 1);
189 }
190
191 #[test]
192 fn test_compound_volume_positive() {
193 let mut c = new_compound("c");
194 add_sphere(&mut c, [0.0; 3], 1.0);
195 assert!(compound_volume(&c) > 0.0);
196 }
197
198 #[test]
199 fn test_aabb_of_centers_none_for_empty() {
200 let c = new_compound("c");
201 assert!(aabb_of_centers(&c).is_none());
202 }
203
204 #[test]
205 fn test_aabb_of_centers_some() {
206 let mut c = new_compound("c");
207 add_sphere(&mut c, [-1.0, 0.0, 0.0], 0.5);
208 add_sphere(&mut c, [1.0, 0.0, 0.0], 0.5);
209 let (mn, mx) = aabb_of_centers(&c).expect("should succeed");
210 assert!((mn[0] - (-1.0)).abs() < 1e-6);
211 assert!((mx[0] - 1.0).abs() < 1e-6);
212 }
213
214 #[test]
215 fn test_serialise_sphere() {
216 let mut c = new_compound("c");
217 add_sphere(&mut c, [0.0; 3], 1.0);
218 let buf = serialise_compound(&c);
219 assert_eq!(buf.len(), 5);
220 assert!((buf[0] - 0.0).abs() < 1e-6);
221 }
222
223 #[test]
224 fn test_serialise_empty() {
225 let c = new_compound("c");
226 assert!(serialise_compound(&c).is_empty());
227 }
228
229 #[test]
230 fn test_name_stored() {
231 let c = new_compound("spine");
232 assert_eq!(c.name, "spine".to_string());
233 }
234}