qualia_core_db/render/physics/
admission.rs1use super::aabb::Aabb;
10use crate::render::pga::Motor;
11
12#[derive(Clone, Copy, Debug, PartialEq)]
14pub enum Refusal {
15 Contraction {
17 axis: usize,
18 resulting: f32,
19 floor: f32,
20 },
21 OutOfBounds,
23}
24
25#[derive(Clone, Copy, Debug)]
27pub struct Admission {
28 pub min_extent: f32,
31 pub world: Option<Aabb>,
33}
34
35impl Admission {
36 #[inline]
37 pub fn new(min_extent: f32, world: Option<Aabb>) -> Self {
38 Admission { min_extent, world }
39 }
40
41 pub fn admit(&self, artefact: &Aabb, motor: Motor, scale: [f32; 3]) -> Result<Aabb, Refusal> {
48 if self.min_extent > 0.0 {
49 let e = artefact.extent();
50 for axis in 0..3 {
51 let resulting = e[axis] * scale[axis].abs();
52 if resulting < self.min_extent {
53 return Err(Refusal::Contraction {
54 axis,
55 resulting,
56 floor: self.min_extent,
57 });
58 }
59 }
60 }
61 let moved = artefact.transformed(motor, scale);
62 if let Some(world) = self.world {
63 if !world.contains(&moved) {
64 return Err(Refusal::OutOfBounds);
65 }
66 }
67 Ok(moved)
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::render::pga::{motor_translate, Motor};
75
76 fn artefact() -> Aabb {
77 Aabb::new([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]) }
79
80 #[test]
81 fn admits_a_valid_rigid_move() {
82 let policy = Admission::new(0.5, None);
83 let out = policy.admit(
84 &artefact(),
85 motor_translate([3.0, 0.0, 0.0]),
86 [1.0, 1.0, 1.0],
87 );
88 assert!(out.is_ok());
89 }
90
91 #[test]
92 fn refuses_contraction_below_floor() {
93 let policy = Admission::new(0.5, None);
94 let verdict = policy.admit(&artefact(), Motor::identity(), [0.1, 1.0, 1.0]);
96 assert_eq!(
97 verdict,
98 Err(Refusal::Contraction {
99 axis: 0,
100 resulting: 0.2,
101 floor: 0.5
102 })
103 );
104 }
105
106 #[test]
107 fn rotation_is_not_contraction() {
108 use crate::render::pga::rotor_from_axis_angle;
109 let policy = Admission::new(0.5, None);
110 let spin = Motor::from_rotor(rotor_from_axis_angle([0.0, 1.0, 0.0], 0.7));
111 assert!(policy.admit(&artefact(), spin, [1.0, 1.0, 1.0]).is_ok());
113 }
114
115 #[test]
116 fn refuses_out_of_world_bounds() {
117 let world = Aabb::new([-2.0, -2.0, -2.0], [2.0, 2.0, 2.0]);
118 let policy = Admission::new(0.0, Some(world));
119 let verdict = policy.admit(
121 &artefact(),
122 motor_translate([5.0, 0.0, 0.0]),
123 [1.0, 1.0, 1.0],
124 );
125 assert_eq!(verdict, Err(Refusal::OutOfBounds));
126 assert!(policy
128 .admit(
129 &artefact(),
130 motor_translate([0.5, 0.0, 0.0]),
131 [1.0, 1.0, 1.0]
132 )
133 .is_ok());
134 }
135
136 #[test]
137 fn verdict_is_deterministic() {
138 let policy = Admission::new(0.5, None);
139 let a = policy.admit(&artefact(), Motor::identity(), [0.1, 1.0, 1.0]);
140 let b = policy.admit(&artefact(), Motor::identity(), [0.1, 1.0, 1.0]);
141 assert_eq!(a, b);
142 }
143}