1use crate::error::GeometricError;
7use crate::result::TestResult;
8use crate::{bivector, vector, GA3};
9
10pub trait ManifoldConstraint: Send + Sync {
12 fn satisfied(&self, point: &GA3) -> bool;
14
15 fn distance(&self, point: &GA3) -> f64;
17
18 fn project(&self, point: &GA3) -> GA3;
20
21 fn description(&self) -> &str;
23}
24
25pub struct Manifold {
27 pub name: String,
29 constraints: Vec<Box<dyn ManifoldConstraint>>,
31 pub tolerance: f64,
33}
34
35impl Manifold {
36 pub fn new(name: impl Into<String>) -> Self {
38 Self {
39 name: name.into(),
40 constraints: Vec::new(),
41 tolerance: 1e-10,
42 }
43 }
44
45 pub fn with_tolerance(mut self, tolerance: f64) -> Self {
47 self.tolerance = tolerance;
48 self
49 }
50
51 pub fn with_constraint(mut self, constraint: impl ManifoldConstraint + 'static) -> Self {
53 self.constraints.push(Box::new(constraint));
54 self
55 }
56
57 pub fn contains(&self, point: &GA3) -> bool {
59 self.constraints
60 .iter()
61 .all(|c| c.distance(point) < self.tolerance)
62 }
63
64 pub fn distance(&self, point: &GA3) -> f64 {
66 self.constraints
67 .iter()
68 .map(|c| c.distance(point))
69 .fold(0.0, f64::max)
70 }
71
72 pub fn project(&self, point: &GA3) -> GA3 {
77 let mut result = point.clone();
78 for _ in 0..10 {
79 let mut changed = false;
81 for constraint in &self.constraints {
82 if !constraint.satisfied(&result) {
83 result = constraint.project(&result);
84 changed = true;
85 }
86 }
87 if !changed {
88 break;
89 }
90 }
91 result
92 }
93
94 pub fn verify(&self, point: &GA3) -> TestResult {
96 let dist = self.distance(point);
97 if dist < self.tolerance {
98 TestResult::Pass
99 } else {
100 let projected = self.project(point);
101 let error_mv = &projected - point;
102 let mut error = GeometricError::from_multivector(
103 &error_mv,
104 format!("Point not on manifold '{}'", self.name),
105 );
106 error.distance = dist;
107 TestResult::Fail(error)
108 }
109 }
110}
111
112pub struct MagnitudeConstraint {
114 target: f64,
115}
116
117impl MagnitudeConstraint {
118 pub fn new(target: f64) -> Self {
120 Self { target }
121 }
122
123 pub fn unit() -> Self {
125 Self { target: 1.0 }
126 }
127}
128
129impl ManifoldConstraint for MagnitudeConstraint {
130 fn satisfied(&self, point: &GA3) -> bool {
131 (point.magnitude() - self.target).abs() < 1e-10
132 }
133
134 fn distance(&self, point: &GA3) -> f64 {
135 (point.magnitude() - self.target).abs()
136 }
137
138 fn project(&self, point: &GA3) -> GA3 {
139 let mag = point.magnitude();
140 if mag > 1e-10 {
141 point * (self.target / mag)
142 } else {
143 point.clone()
145 }
146 }
147
148 fn description(&self) -> &str {
149 "Magnitude constraint"
150 }
151}
152
153pub struct ScalarConstraint {
155 target: f64,
156}
157
158impl ScalarConstraint {
159 pub fn new(target: f64) -> Self {
161 Self { target }
162 }
163
164 pub fn zero() -> Self {
166 Self { target: 0.0 }
167 }
168}
169
170impl ManifoldConstraint for ScalarConstraint {
171 fn satisfied(&self, point: &GA3) -> bool {
172 (point.get(0) - self.target).abs() < 1e-10
173 }
174
175 fn distance(&self, point: &GA3) -> f64 {
176 (point.get(0) - self.target).abs()
177 }
178
179 fn project(&self, point: &GA3) -> GA3 {
180 let mut coeffs: Vec<f64> = (0..8).map(|i| point.get(i)).collect();
182 coeffs[0] = self.target; GA3::from_coefficients(coeffs)
184 }
185
186 fn description(&self) -> &str {
187 "Scalar constraint"
188 }
189}
190
191pub struct PureVectorConstraint;
197
198impl ManifoldConstraint for PureVectorConstraint {
199 fn satisfied(&self, point: &GA3) -> bool {
200 let s = point.get(0).abs(); let b12 = point.get(3).abs(); let b13 = point.get(5).abs(); let b23 = point.get(6).abs(); let tri = point.get(7).abs(); s < 1e-10 && b12 < 1e-10 && b13 < 1e-10 && b23 < 1e-10 && tri < 1e-10
206 }
207
208 fn distance(&self, point: &GA3) -> f64 {
209 let s = point.get(0);
210 let b12 = point.get(3);
211 let b13 = point.get(5);
212 let b23 = point.get(6);
213 let tri = point.get(7);
214 (s * s + b12 * b12 + b13 * b13 + b23 * b23 + tri * tri).sqrt()
215 }
216
217 fn project(&self, point: &GA3) -> GA3 {
218 vector(point.get(1), point.get(2), point.get(4))
220 }
221
222 fn description(&self) -> &str {
223 "Pure vector constraint"
224 }
225}
226
227pub struct PureBivectorConstraint;
232
233impl ManifoldConstraint for PureBivectorConstraint {
234 fn satisfied(&self, point: &GA3) -> bool {
235 let s = point.get(0).abs(); let v1 = point.get(1).abs(); let v2 = point.get(2).abs(); let v3 = point.get(4).abs(); let tri = point.get(7).abs(); s < 1e-10 && v1 < 1e-10 && v2 < 1e-10 && v3 < 1e-10 && tri < 1e-10
241 }
242
243 fn distance(&self, point: &GA3) -> f64 {
244 let s = point.get(0);
245 let v1 = point.get(1);
246 let v2 = point.get(2);
247 let v3 = point.get(4);
248 let tri = point.get(7);
249 (s * s + v1 * v1 + v2 * v2 + v3 * v3 + tri * tri).sqrt()
250 }
251
252 fn project(&self, point: &GA3) -> GA3 {
253 bivector(point.get(3), point.get(5), point.get(6))
255 }
256
257 fn description(&self) -> &str {
258 "Pure bivector constraint"
259 }
260}
261
262pub fn unit_sphere() -> Manifold {
264 Manifold::new("Unit Sphere")
265 .with_constraint(PureVectorConstraint)
266 .with_constraint(MagnitudeConstraint::unit())
267}
268
269pub fn rotor_manifold() -> Manifold {
271 Manifold::new("Rotor Manifold").with_constraint(MagnitudeConstraint::unit())
272 }
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn test_magnitude_constraint() {
282 let constraint = MagnitudeConstraint::unit();
283 let unit_vec = vector(1.0, 0.0, 0.0);
284 assert!(constraint.satisfied(&unit_vec));
285
286 let non_unit = vector(2.0, 0.0, 0.0);
287 assert!(!constraint.satisfied(&non_unit));
288
289 let projected = constraint.project(&non_unit);
290 assert!(constraint.satisfied(&projected));
291 }
292
293 #[test]
294 fn test_manifold_contains() {
295 let manifold = unit_sphere();
296
297 let on_sphere = vector(1.0, 0.0, 0.0);
298 assert!(manifold.contains(&on_sphere));
299
300 let off_sphere = vector(2.0, 0.0, 0.0);
301 assert!(!manifold.contains(&off_sphere));
302 }
303
304 #[test]
305 fn test_manifold_project() {
306 let manifold = unit_sphere();
307
308 let off_sphere = vector(3.0, 4.0, 0.0);
309 let projected = manifold.project(&off_sphere);
310
311 assert!(
312 manifold.contains(&projected),
313 "Projected point should be on sphere"
314 );
315 assert!(
316 (projected.magnitude() - 1.0).abs() < 1e-10,
317 "Projected magnitude should be 1"
318 );
319 }
320
321 #[test]
322 fn test_manifold_verify() {
323 let manifold = unit_sphere();
324
325 let on = vector(0.0, 1.0, 0.0);
326 assert!(manifold.verify(&on).is_pass());
327
328 let off = vector(0.0, 2.0, 0.0);
329 let result = manifold.verify(&off);
330 assert!(result.is_fail());
331
332 if let TestResult::Fail(error) = result {
333 assert!(error.distance > 0.0);
334 }
335 }
336}