Skip to main content

cliffy_core/
state.rs

1//! Geometric state with explicit transformations
2//!
3//! `GeometricState` exposes the geometric algebra operations that are
4//! hidden in regular `Behavior`. Use this when you want explicit control
5//! over geometric transformations.
6//!
7//! # Example
8//!
9//! ```rust
10//! use cliffy_core::{GeometricState, Rotor, Translation};
11//! use std::f64::consts::PI;
12//!
13//! // Create state from initial position
14//! let state = GeometricState::from_vector(1.0, 0.0, 0.0);
15//!
16//! // Apply explicit geometric transformations
17//! let rotated = state.apply_rotor(&Rotor::xy(PI / 2.0));
18//! let translated = rotated.apply_translation(&Translation::new(1.0, 0.0, 0.0));
19//!
20//! // Project to user types
21//! let (x, y, z) = translated.as_vector();
22//! ```
23
24use crate::geometric::GA3;
25use crate::projection::Projection;
26use crate::transforms::{Rotor, Transform, Translation, Versor};
27use amari_core::{Bivector, Vector};
28use std::sync::{Arc, Mutex};
29
30/// Type alias for subscriber callbacks to avoid clippy::type_complexity warning
31type SubscriberList = Arc<Mutex<Vec<Box<dyn Fn(&GA3) + Send + Sync>>>>;
32
33/// State that lives in geometric space with explicit transformation support.
34///
35/// Unlike `Behavior<T>`, which hides the geometric representation,
36/// `GeometricState` exposes it directly. This is useful for:
37/// - Animation with geometric interpolation (SLERP)
38/// - Physics simulations
39/// - Explicit rotation/translation control
40/// - Advanced users who understand geometric algebra
41#[derive(Clone)]
42pub struct GeometricState {
43    /// The underlying multivector
44    inner: Arc<Mutex<GA3>>,
45    /// Subscribers for reactive updates
46    subscribers: SubscriberList,
47}
48
49impl GeometricState {
50    /// Create a new geometric state from a multivector
51    pub fn new(mv: GA3) -> Self {
52        Self {
53            inner: Arc::new(Mutex::new(mv)),
54            subscribers: Arc::new(Mutex::new(Vec::new())),
55        }
56    }
57
58    /// Create state representing a scalar value
59    pub fn from_scalar(value: f64) -> Self {
60        Self::new(GA3::scalar(value))
61    }
62
63    /// Create state representing a 3D vector/position
64    pub fn from_vector(x: f64, y: f64, z: f64) -> Self {
65        let v = Vector::<3, 0, 0>::from_components(x, y, z);
66        Self::new(GA3::from_vector(&v))
67    }
68
69    /// Create state representing a bivector (rotation plane)
70    pub fn from_bivector(xy: f64, xz: f64, yz: f64) -> Self {
71        let b = Bivector::<3, 0, 0>::from_components(xy, xz, yz);
72        Self::new(GA3::from_bivector(&b))
73    }
74
75    /// Create state from raw coefficients
76    pub fn from_coefficients(coeffs: Vec<f64>) -> Self {
77        Self::new(GA3::from_coefficients(coeffs))
78    }
79
80    /// Create the zero state
81    pub fn zero() -> Self {
82        Self::new(GA3::zero())
83    }
84
85    /// Create the identity state (scalar 1)
86    pub fn identity() -> Self {
87        Self::new(GA3::scalar(1.0))
88    }
89
90    /// Get the underlying multivector (cloned)
91    pub fn multivector(&self) -> GA3 {
92        self.inner.lock().unwrap().clone()
93    }
94
95    /// Get the raw coefficient at a given index
96    pub fn get(&self, index: usize) -> f64 {
97        self.inner.lock().unwrap().get(index)
98    }
99
100    /// Get the scalar component
101    pub fn scalar(&self) -> f64 {
102        self.get(0)
103    }
104
105    /// Get the vector components (e1, e2, e3) as a tuple
106    pub fn as_vector(&self) -> (f64, f64, f64) {
107        let mv = self.inner.lock().unwrap();
108        (mv.get(1), mv.get(2), mv.get(4))
109    }
110
111    /// Get the vector components as a typed `Vector<3,0,0>`
112    pub fn as_typed_vector(&self) -> Vector<3, 0, 0> {
113        let mv = self.inner.lock().unwrap();
114        Vector::from_components(mv.get(1), mv.get(2), mv.get(4))
115    }
116
117    /// Get the bivector components (e12, e13, e23) as a tuple
118    pub fn as_bivector(&self) -> (f64, f64, f64) {
119        let mv = self.inner.lock().unwrap();
120        (mv.get(3), mv.get(5), mv.get(6))
121    }
122
123    /// Get the bivector components as a typed `Bivector<3,0,0>`
124    pub fn as_typed_bivector(&self) -> Bivector<3, 0, 0> {
125        let mv = self.inner.lock().unwrap();
126        Bivector::from_components(mv.get(3), mv.get(5), mv.get(6))
127    }
128
129    /// Get the magnitude (norm) of the state
130    pub fn magnitude(&self) -> f64 {
131        self.inner.lock().unwrap().magnitude()
132    }
133
134    /// Project using a projection type
135    pub fn project<P: Projection>(&self, projection: &P) -> P::Output {
136        let mv = self.inner.lock().unwrap();
137        projection.project(&mv)
138    }
139
140    /// Set the state to a new multivector
141    pub fn set(&self, mv: GA3) {
142        {
143            let mut inner = self.inner.lock().unwrap();
144            *inner = mv;
145        }
146        self.notify_subscribers();
147    }
148
149    /// Set the scalar value
150    pub fn set_scalar(&self, value: f64) {
151        self.set(GA3::scalar(value));
152    }
153
154    /// Set the vector value from components
155    pub fn set_vector(&self, x: f64, y: f64, z: f64) {
156        let v = Vector::<3, 0, 0>::from_components(x, y, z);
157        self.set(GA3::from_vector(&v));
158    }
159
160    /// Set the vector value from a typed `Vector<3,0,0>`
161    pub fn set_typed_vector(&self, v: &Vector<3, 0, 0>) {
162        self.set(GA3::from_vector(v));
163    }
164
165    /// Set the bivector value from a typed `Bivector<3,0,0>`
166    pub fn set_typed_bivector(&self, b: &Bivector<3, 0, 0>) {
167        self.set(GA3::from_bivector(b));
168    }
169
170    /// Update the state by applying a function
171    pub fn update<F>(&self, f: F)
172    where
173        F: FnOnce(&GA3) -> GA3,
174    {
175        {
176            let mut inner = self.inner.lock().unwrap();
177            *inner = f(&inner);
178        }
179        self.notify_subscribers();
180    }
181
182    /// Apply a rotor transformation (rotation)
183    ///
184    /// This creates a new state; the original is unchanged.
185    pub fn apply_rotor(&self, rotor: &Rotor) -> GeometricState {
186        let mv = self.inner.lock().unwrap();
187        let transformed = rotor.transform(&mv);
188        GeometricState::new(transformed)
189    }
190
191    /// Apply a rotor transformation in-place
192    pub fn apply_rotor_mut(&self, rotor: &Rotor) {
193        self.update(|mv| rotor.transform(mv));
194    }
195
196    /// Apply a translation
197    ///
198    /// This creates a new state; the original is unchanged.
199    pub fn apply_translation(&self, translation: &Translation) -> GeometricState {
200        let mv = self.inner.lock().unwrap();
201        let transformed = translation.transform(&mv);
202        GeometricState::new(transformed)
203    }
204
205    /// Apply a translation in-place
206    pub fn apply_translation_mut(&self, translation: &Translation) {
207        self.update(|mv| translation.transform(mv));
208    }
209
210    /// Apply a versor transformation
211    ///
212    /// This creates a new state; the original is unchanged.
213    pub fn apply_versor(&self, versor: &Versor) -> GeometricState {
214        let mv = self.inner.lock().unwrap();
215        let transformed = versor.transform(&mv);
216        GeometricState::new(transformed)
217    }
218
219    /// Apply a versor transformation in-place
220    pub fn apply_versor_mut(&self, versor: &Versor) {
221        self.update(|mv| versor.transform(mv));
222    }
223
224    /// Apply a general transform (rotation + translation)
225    ///
226    /// This creates a new state; the original is unchanged.
227    pub fn apply_transform(&self, transform: &Transform) -> GeometricState {
228        let mv = self.inner.lock().unwrap();
229        let transformed = transform.transform(&mv);
230        GeometricState::new(transformed)
231    }
232
233    /// Apply a general transform in-place
234    pub fn apply_transform_mut(&self, transform: &Transform) {
235        self.update(|mv| transform.transform(mv));
236    }
237
238    /// Add another state (geometric addition)
239    pub fn add(&self, other: &GeometricState) -> GeometricState {
240        let a = self.inner.lock().unwrap();
241        let b = other.inner.lock().unwrap();
242        GeometricState::new(&*a + &*b)
243    }
244
245    /// Subtract another state
246    pub fn sub(&self, other: &GeometricState) -> GeometricState {
247        let a = self.inner.lock().unwrap();
248        let b = other.inner.lock().unwrap();
249        GeometricState::new(&*a - &*b)
250    }
251
252    /// Scale by a scalar value
253    pub fn scale(&self, factor: f64) -> GeometricState {
254        let mv = self.inner.lock().unwrap();
255        GeometricState::new(&*mv * factor)
256    }
257
258    /// Geometric product with another state
259    pub fn geometric_product(&self, other: &GeometricState) -> GeometricState {
260        let a = self.inner.lock().unwrap();
261        let b = other.inner.lock().unwrap();
262        GeometricState::new(a.geometric_product(&b))
263    }
264
265    /// Normalize the state to unit magnitude
266    pub fn normalize(&self) -> Option<GeometricState> {
267        let mv = self.inner.lock().unwrap();
268        mv.normalize().map(GeometricState::new)
269    }
270
271    /// Normalize in-place
272    pub fn normalize_mut(&self) -> bool {
273        let mut inner = self.inner.lock().unwrap();
274        match inner.normalize() {
275            Some(normalized) => {
276                *inner = normalized;
277                drop(inner);
278                self.notify_subscribers();
279                true
280            }
281            None => false,
282        }
283    }
284
285    /// Get the reverse (reversion) of this state
286    pub fn reverse(&self) -> GeometricState {
287        let mv = self.inner.lock().unwrap();
288        GeometricState::new(mv.reverse())
289    }
290
291    /// Linear interpolation to another state
292    pub fn lerp(&self, other: &GeometricState, t: f64) -> GeometricState {
293        let a = self.inner.lock().unwrap();
294        let b = other.inner.lock().unwrap();
295
296        // lerp = a + t * (b - a) = (1-t)*a + t*b
297        let diff = &*b - &*a;
298        let interpolated = &*a + &(&diff * t);
299        GeometricState::new(interpolated)
300    }
301
302    /// Spherical linear interpolation (for rotor-like states)
303    ///
304    /// This assumes both states are unit rotors.
305    pub fn slerp(&self, other: &GeometricState, t: f64) -> GeometricState {
306        // Convert to rotors and use rotor SLERP
307        let a_mv = self.inner.lock().unwrap();
308        let b_mv = other.inner.lock().unwrap();
309
310        let rotor_a = Rotor::from_multivector(a_mv.clone());
311        let rotor_b = Rotor::from_multivector(b_mv.clone());
312
313        let interpolated = rotor_a.slerp_to(&rotor_b, t);
314        GeometricState::new(interpolated.as_multivector().clone())
315    }
316
317    /// Subscribe to state changes
318    pub fn subscribe<F>(&self, callback: F) -> GeometricSubscription
319    where
320        F: Fn(&GA3) + Send + Sync + 'static,
321    {
322        let mut subs = self.subscribers.lock().unwrap();
323        let id = subs.len();
324        subs.push(Box::new(callback));
325
326        GeometricSubscription {
327            id,
328            subscribers: self.subscribers.clone(),
329        }
330    }
331
332    /// Notify all subscribers of a state change
333    fn notify_subscribers(&self) {
334        let mv = self.inner.lock().unwrap();
335        let subs = self.subscribers.lock().unwrap();
336        for callback in subs.iter() {
337            callback(&mv);
338        }
339    }
340}
341
342impl std::fmt::Debug for GeometricState {
343    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344        let mv = self.inner.lock().unwrap();
345        write!(f, "GeometricState({:?})", mv)
346    }
347}
348
349/// A subscription handle for geometric state changes
350pub struct GeometricSubscription {
351    id: usize,
352    subscribers: SubscriberList,
353}
354
355impl GeometricSubscription {
356    /// Unsubscribe from updates
357    ///
358    /// Note: This doesn't actually remove the callback (to maintain indices),
359    /// but marks it as inactive. In practice, subscriptions typically last
360    /// for the lifetime of components.
361    pub fn unsubscribe(self) {
362        // In a more sophisticated implementation, we'd mark the slot as inactive
363        // For now, we just drop the handle
364        let _ = (self.id, self.subscribers);
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371    use crate::projection::{IntProjection, ScalarProjection, VectorProjection};
372    use std::f64::consts::PI;
373    use std::sync::atomic::{AtomicUsize, Ordering};
374
375    #[test]
376    fn test_from_scalar() {
377        let state = GeometricState::from_scalar(42.0);
378        assert!((state.scalar() - 42.0).abs() < 1e-10);
379    }
380
381    #[test]
382    fn test_from_vector() {
383        let state = GeometricState::from_vector(1.0, 2.0, 3.0);
384        let (x, y, z) = state.as_vector();
385        assert!((x - 1.0).abs() < 1e-10);
386        assert!((y - 2.0).abs() < 1e-10);
387        assert!((z - 3.0).abs() < 1e-10);
388    }
389
390    #[test]
391    fn test_apply_rotor() {
392        let state = GeometricState::from_vector(1.0, 0.0, 0.0);
393        let rotor = Rotor::xy(PI / 2.0);
394        let rotated = state.apply_rotor(&rotor);
395
396        let (x, y, z) = rotated.as_vector();
397        assert!(x.abs() < 1e-10, "x should be ~0, got {}", x);
398        assert!((y - 1.0).abs() < 1e-10, "y should be ~1, got {}", y);
399        assert!(z.abs() < 1e-10, "z should be ~0, got {}", z);
400    }
401
402    #[test]
403    fn test_apply_translation() {
404        let state = GeometricState::from_vector(0.0, 0.0, 0.0);
405        let trans = Translation::new(1.0, 2.0, 3.0);
406        let translated = state.apply_translation(&trans);
407
408        let (x, y, z) = translated.as_vector();
409        assert!((x - 1.0).abs() < 1e-10);
410        assert!((y - 2.0).abs() < 1e-10);
411        assert!((z - 3.0).abs() < 1e-10);
412    }
413
414    #[test]
415    fn test_apply_transform() {
416        let state = GeometricState::from_vector(1.0, 0.0, 0.0);
417        let transform = Transform::new(Rotor::xy(PI / 2.0), Translation::new(1.0, 0.0, 0.0));
418
419        let result = state.apply_transform(&transform);
420        let (x, y, z) = result.as_vector();
421
422        // Rotate (1,0,0) by 90deg -> (0,1,0), then translate -> (1,1,0)
423        assert!((x - 1.0).abs() < 1e-10, "x should be ~1, got {}", x);
424        assert!((y - 1.0).abs() < 1e-10, "y should be ~1, got {}", y);
425        assert!(z.abs() < 1e-10);
426    }
427
428    #[test]
429    fn test_lerp() {
430        let a = GeometricState::from_scalar(0.0);
431        let b = GeometricState::from_scalar(10.0);
432
433        let half = a.lerp(&b, 0.5);
434        assert!((half.scalar() - 5.0).abs() < 1e-10);
435
436        let quarter = a.lerp(&b, 0.25);
437        assert!((quarter.scalar() - 2.5).abs() < 1e-10);
438    }
439
440    #[test]
441    fn test_projection() {
442        let state = GeometricState::from_scalar(42.7);
443
444        let scalar = state.project(&ScalarProjection);
445        assert!((scalar - 42.7).abs() < 1e-10);
446
447        let int = state.project(&IntProjection);
448        assert_eq!(int, 42);
449    }
450
451    #[test]
452    fn test_vector_projection() {
453        let state = GeometricState::from_vector(1.0, 2.0, 3.0);
454        let (x, y, z) = state.project(&VectorProjection);
455        assert!((x - 1.0).abs() < 1e-10);
456        assert!((y - 2.0).abs() < 1e-10);
457        assert!((z - 3.0).abs() < 1e-10);
458    }
459
460    #[test]
461    fn test_subscribe() {
462        let state = GeometricState::from_scalar(0.0);
463        let call_count = Arc::new(AtomicUsize::new(0));
464        let call_count_clone = call_count.clone();
465
466        let _sub = state.subscribe(move |_mv| {
467            call_count_clone.fetch_add(1, Ordering::SeqCst);
468        });
469
470        state.set_scalar(1.0);
471        state.set_scalar(2.0);
472
473        assert_eq!(call_count.load(Ordering::SeqCst), 2);
474    }
475
476    #[test]
477    fn test_typed_vector_accessors() {
478        let state = GeometricState::from_vector(1.0, 2.0, 3.0);
479        let v = state.as_typed_vector();
480        // GA3 vector indices: 1=e1, 2=e2, 4=e3
481        assert!((v.mv.get(1) - 1.0).abs() < 1e-10);
482        assert!((v.mv.get(2) - 2.0).abs() < 1e-10);
483        assert!((v.mv.get(4) - 3.0).abs() < 1e-10);
484    }
485
486    #[test]
487    fn test_typed_bivector_accessors() {
488        let state = GeometricState::from_bivector(0.5, 0.3, 0.1);
489        let b = state.as_typed_bivector();
490        assert!((b.get(0) - 0.5).abs() < 1e-10);
491        assert!((b.get(1) - 0.3).abs() < 1e-10);
492        assert!((b.get(2) - 0.1).abs() < 1e-10);
493    }
494
495    #[test]
496    fn test_set_typed_vector() {
497        let state = GeometricState::zero();
498        let v = Vector::<3, 0, 0>::from_components(4.0, 5.0, 6.0);
499        state.set_typed_vector(&v);
500        let result = state.as_typed_vector();
501        // GA3 vector indices: 1=e1, 2=e2, 4=e3
502        assert!((result.mv.get(1) - 4.0).abs() < 1e-10);
503        assert!((result.mv.get(2) - 5.0).abs() < 1e-10);
504        assert!((result.mv.get(4) - 6.0).abs() < 1e-10);
505    }
506
507    #[test]
508    fn test_scale() {
509        let state = GeometricState::from_vector(1.0, 2.0, 3.0);
510        let scaled = state.scale(2.0);
511
512        let (x, y, z) = scaled.as_vector();
513        assert!((x - 2.0).abs() < 1e-10);
514        assert!((y - 4.0).abs() < 1e-10);
515        assert!((z - 6.0).abs() < 1e-10);
516    }
517
518    #[test]
519    fn test_normalize() {
520        let state = GeometricState::from_vector(3.0, 4.0, 0.0);
521        let normalized = state.normalize().unwrap();
522
523        let mag = normalized.magnitude();
524        assert!((mag - 1.0).abs() < 1e-10);
525
526        let (x, y, _) = normalized.as_vector();
527        assert!((x - 0.6).abs() < 1e-10);
528        assert!((y - 0.8).abs() < 1e-10);
529    }
530}