1use crate::geometric::GA3;
12use amari_core::{Bivector, Rotor as AmariRotor, Vector};
13
14#[derive(Clone, Debug)]
23pub struct Rotor {
24 inner: GA3,
26}
27
28impl Rotor {
29 pub fn from_multivector(mv: GA3) -> Self {
31 Self { inner: mv }
32 }
33
34 pub fn identity() -> Self {
36 Self {
37 inner: GA3::scalar(1.0),
38 }
39 }
40
41 pub fn xy(angle: f64) -> Self {
45 Self::from_bivector_angle(angle, 1.0, 0.0, 0.0)
46 }
47
48 pub fn xz(angle: f64) -> Self {
52 Self::from_bivector_angle(angle, 0.0, 1.0, 0.0)
53 }
54
55 pub fn yz(angle: f64) -> Self {
59 Self::from_bivector_angle(angle, 0.0, 0.0, 1.0)
60 }
61
62 pub fn from_bivector_angle(angle: f64, xy: f64, xz: f64, yz: f64) -> Self {
67 let half_angle = angle / 2.0;
68
69 let biv = Bivector::<3, 0, 0>::from_components(-xy, -xz, -yz);
72 let biv_mv = GA3::from_bivector(&biv);
73 let mag = biv_mv.magnitude();
74
75 if mag < 1e-10 {
76 return Self::identity();
78 }
79
80 let biv_unit = &biv_mv * (1.0 / mag);
81
82 let cos_part = GA3::scalar(half_angle.cos());
84 let sin_part = &biv_unit * half_angle.sin();
85
86 Self {
87 inner: &cos_part + &sin_part,
88 }
89 }
90
91 pub fn from_axis_angle(axis_x: f64, axis_y: f64, axis_z: f64, angle: f64) -> Self {
95 Self::from_bivector_angle(angle, axis_z, -axis_y, axis_x)
100 }
101
102 pub fn as_multivector(&self) -> &GA3 {
104 &self.inner
105 }
106
107 pub fn transform(&self, v: &GA3) -> GA3 {
111 let rev = self.inner.reverse();
112 self.inner.geometric_product(v).geometric_product(&rev)
113 }
114
115 pub fn then(&self, other: &Rotor) -> Rotor {
119 Rotor {
120 inner: other.inner.geometric_product(&self.inner),
121 }
122 }
123
124 pub fn inverse(&self) -> Rotor {
126 Rotor {
128 inner: self.inner.reverse(),
129 }
130 }
131
132 pub fn normalize(&self) -> Rotor {
134 match self.inner.normalize() {
135 Some(normalized) => Rotor { inner: normalized },
136 None => Self::identity(),
137 }
138 }
139
140 pub fn angle(&self) -> f64 {
142 let scalar = self.inner.get(0);
145 2.0 * scalar.clamp(-1.0, 1.0).acos()
146 }
147
148 pub fn to_amari_rotor(&self) -> AmariRotor<3, 0, 0> {
153 let e12 = self.inner.get(3);
155 let e13 = self.inner.get(5);
156 let e23 = self.inner.get(6);
157 let biv_mag = (e12 * e12 + e13 * e13 + e23 * e23).sqrt();
158
159 if biv_mag < 1e-10 {
160 return AmariRotor::identity();
162 }
163
164 let biv =
167 Bivector::<3, 0, 0>::from_components(-e12 / biv_mag, -e13 / biv_mag, -e23 / biv_mag);
168 AmariRotor::from_bivector(&biv, self.angle())
169 }
170
171 pub fn from_amari_rotor(rotor: &AmariRotor<3, 0, 0>) -> Self {
173 Self {
174 inner: rotor.as_multivector().clone(),
175 }
176 }
177
178 pub fn slerp(&self, t: f64) -> Rotor {
182 let angle = self.angle();
183 let new_angle = angle * t;
184
185 let biv_xy = -self.inner.get(3); let biv_xz = -self.inner.get(5); let biv_yz = -self.inner.get(6); let biv_mag = (biv_xy * biv_xy + biv_xz * biv_xz + biv_yz * biv_yz).sqrt();
191
192 if biv_mag < 1e-10 {
193 Self::identity()
195 } else {
196 Self::from_bivector_angle(
197 new_angle,
198 biv_xy / biv_mag,
199 biv_xz / biv_mag,
200 biv_yz / biv_mag,
201 )
202 }
203 }
204
205 pub fn slerp_to(&self, other: &Rotor, t: f64) -> Rotor {
207 let relative = other.then(&self.inverse());
210 let interpolated = relative.slerp(t);
211 interpolated.then(self)
212 }
213}
214
215#[derive(Clone, Debug)]
221pub struct Versor {
222 inner: GA3,
224 is_even: bool,
226}
227
228impl Versor {
229 pub fn from_multivector(mv: GA3, is_even: bool) -> Self {
231 Self { inner: mv, is_even }
232 }
233
234 pub fn identity() -> Self {
236 Self {
237 inner: GA3::scalar(1.0),
238 is_even: true,
239 }
240 }
241
242 pub fn reflection(normal_x: f64, normal_y: f64, normal_z: f64) -> Self {
246 let vec = Vector::<3, 0, 0>::from_components(normal_x, normal_y, normal_z);
248 let mv = GA3::from_vector(&vec);
249 let normalized = mv
250 .normalize()
251 .unwrap_or_else(|| GA3::from_vector(&Vector::from_components(1.0, 0.0, 0.0)));
252
253 Self {
254 inner: normalized,
255 is_even: false,
256 }
257 }
258
259 pub fn from_rotor(rotor: Rotor) -> Self {
261 Self {
262 inner: rotor.inner,
263 is_even: true,
264 }
265 }
266
267 pub fn as_multivector(&self) -> &GA3 {
269 &self.inner
270 }
271
272 pub fn transform(&self, v: &GA3) -> GA3 {
277 let rev = self.inner.reverse();
278 if self.is_even {
279 self.inner.geometric_product(v).geometric_product(&rev)
280 } else {
281 let result = self.inner.geometric_product(v).geometric_product(&rev);
284 &result * -1.0
285 }
286 }
287
288 pub fn then(&self, other: &Versor) -> Versor {
290 Versor {
291 inner: other.inner.geometric_product(&self.inner),
292 is_even: self.is_even == other.is_even, }
294 }
295
296 pub fn is_rotor(&self) -> bool {
298 self.is_even
299 }
300
301 pub fn to_rotor(&self) -> Option<Rotor> {
303 if self.is_even {
304 Some(Rotor {
305 inner: self.inner.clone(),
306 })
307 } else {
308 None
309 }
310 }
311}
312
313#[derive(Clone, Debug)]
319pub struct Translation {
320 x: f64,
322 y: f64,
323 z: f64,
324}
325
326impl Translation {
327 pub fn new(x: f64, y: f64, z: f64) -> Self {
329 Self { x, y, z }
330 }
331
332 pub fn x(amount: f64) -> Self {
334 Self::new(amount, 0.0, 0.0)
335 }
336
337 pub fn y(amount: f64) -> Self {
339 Self::new(0.0, amount, 0.0)
340 }
341
342 pub fn z(amount: f64) -> Self {
344 Self::new(0.0, 0.0, amount)
345 }
346
347 pub fn transform(&self, v: &GA3) -> GA3 {
352 let trans_vec = Vector::<3, 0, 0>::from_components(self.x, self.y, self.z);
353 let trans_mv = GA3::from_vector(&trans_vec);
354 v + &trans_mv
355 }
356
357 pub fn then(&self, other: &Translation) -> Translation {
359 Translation {
360 x: self.x + other.x,
361 y: self.y + other.y,
362 z: self.z + other.z,
363 }
364 }
365
366 pub fn inverse(&self) -> Translation {
368 Translation {
369 x: -self.x,
370 y: -self.y,
371 z: -self.z,
372 }
373 }
374
375 pub fn lerp(&self, t: f64) -> Translation {
377 Translation {
378 x: self.x * t,
379 y: self.y * t,
380 z: self.z * t,
381 }
382 }
383
384 pub fn lerp_to(&self, other: &Translation, t: f64) -> Translation {
386 Translation {
387 x: self.x + (other.x - self.x) * t,
388 y: self.y + (other.y - self.y) * t,
389 z: self.z + (other.z - self.z) * t,
390 }
391 }
392}
393
394#[derive(Clone, Debug)]
396pub struct Transform {
397 rotor: Rotor,
399 translation: Translation,
401}
402
403impl Transform {
404 pub fn new(rotor: Rotor, translation: Translation) -> Self {
406 Self { rotor, translation }
407 }
408
409 pub fn identity() -> Self {
411 Self {
412 rotor: Rotor::identity(),
413 translation: Translation::new(0.0, 0.0, 0.0),
414 }
415 }
416
417 pub fn rotation(rotor: Rotor) -> Self {
419 Self {
420 rotor,
421 translation: Translation::new(0.0, 0.0, 0.0),
422 }
423 }
424
425 pub fn translation(translation: Translation) -> Self {
427 Self {
428 rotor: Rotor::identity(),
429 translation,
430 }
431 }
432
433 pub fn transform(&self, v: &GA3) -> GA3 {
435 let rotated = self.rotor.transform(v);
436 self.translation.transform(&rotated)
437 }
438
439 pub fn then(&self, other: &Transform) -> Transform {
441 let combined_rotor = self.rotor.then(&other.rotor);
447
448 let self_trans_vec = Vector::<3, 0, 0>::from_components(
450 self.translation.x,
451 self.translation.y,
452 self.translation.z,
453 );
454 let self_trans_mv = GA3::from_vector(&self_trans_vec);
455 let rotated_trans = other.rotor.transform(&self_trans_mv);
456
457 let combined_translation = Translation::new(
458 rotated_trans.get(1) + other.translation.x,
459 rotated_trans.get(2) + other.translation.y,
460 rotated_trans.get(4) + other.translation.z,
461 );
462
463 Transform {
464 rotor: combined_rotor,
465 translation: combined_translation,
466 }
467 }
468
469 pub fn inverse(&self) -> Transform {
471 let inv_rotor = self.rotor.inverse();
472 let inv_trans_base = self.translation.inverse();
473
474 let trans_vec = Vector::<3, 0, 0>::from_components(
476 inv_trans_base.x,
477 inv_trans_base.y,
478 inv_trans_base.z,
479 );
480 let trans_mv = GA3::from_vector(&trans_vec);
481 let rotated = inv_rotor.transform(&trans_mv);
482
483 Transform {
484 rotor: inv_rotor,
485 translation: Translation::new(rotated.get(1), rotated.get(2), rotated.get(4)),
486 }
487 }
488
489 pub fn interpolate(&self, t: f64) -> Transform {
491 Transform {
492 rotor: self.rotor.slerp(t),
493 translation: self.translation.lerp(t),
494 }
495 }
496
497 pub fn interpolate_to(&self, other: &Transform, t: f64) -> Transform {
499 Transform {
500 rotor: self.rotor.slerp_to(&other.rotor, t),
501 translation: self.translation.lerp_to(&other.translation, t),
502 }
503 }
504}
505
506#[cfg(test)]
507mod tests {
508 use super::*;
509 use std::f64::consts::PI;
510
511 #[test]
512 fn test_rotor_identity() {
513 let r = Rotor::identity();
514 let v = Vector::<3, 0, 0>::from_components(1.0, 2.0, 3.0);
515 let mv = GA3::from_vector(&v);
516
517 let rotated = r.transform(&mv);
518
519 assert!((rotated.get(1) - 1.0).abs() < 1e-10);
521 assert!((rotated.get(2) - 2.0).abs() < 1e-10);
522 assert!((rotated.get(4) - 3.0).abs() < 1e-10);
523 }
524
525 #[test]
526 fn test_rotor_xy_90() {
527 let r = Rotor::xy(PI / 2.0);
529
530 let v = GA3::from_vector(&Vector::from_components(1.0, 0.0, 0.0));
532 let rotated = r.transform(&v);
533
534 assert!((rotated.get(1) - 0.0).abs() < 1e-10); assert!((rotated.get(2) - 1.0).abs() < 1e-10); assert!((rotated.get(4) - 0.0).abs() < 1e-10); }
538
539 #[test]
540 fn test_rotor_preserves_magnitude() {
541 let r = Rotor::from_bivector_angle(1.23, 1.0, 2.0, 3.0);
542 let v = GA3::from_vector(&Vector::from_components(3.0, 4.0, 5.0));
543
544 let original_mag = v.magnitude();
545 let rotated = r.transform(&v);
546 let rotated_mag = rotated.magnitude();
547
548 assert!(
549 (original_mag - rotated_mag).abs() < 1e-10,
550 "Magnitude changed: {} -> {}",
551 original_mag,
552 rotated_mag
553 );
554 }
555
556 #[test]
557 fn test_rotor_composition() {
558 let r1 = Rotor::xy(PI / 2.0);
560 let r2 = Rotor::xy(PI / 2.0);
561 let r_combined = r1.then(&r2);
562
563 let v = GA3::from_vector(&Vector::from_components(1.0, 0.0, 0.0));
564 let rotated = r_combined.transform(&v);
565
566 assert!((rotated.get(1) + 1.0).abs() < 1e-10);
568 assert!((rotated.get(2) - 0.0).abs() < 1e-10);
569 }
570
571 #[test]
572 fn test_rotor_inverse() {
573 let r = Rotor::from_bivector_angle(0.7, 1.0, 1.0, 1.0);
574 let v = GA3::from_vector(&Vector::from_components(1.0, 2.0, 3.0));
575
576 let rotated = r.transform(&v);
577 let back = r.inverse().transform(&rotated);
578
579 assert!((back.get(1) - 1.0).abs() < 1e-10);
580 assert!((back.get(2) - 2.0).abs() < 1e-10);
581 assert!((back.get(4) - 3.0).abs() < 1e-10);
582 }
583
584 #[test]
585 fn test_rotor_slerp() {
586 let r = Rotor::xy(PI);
587
588 let half = r.slerp(0.5);
590
591 let v = GA3::from_vector(&Vector::from_components(1.0, 0.0, 0.0));
592 let rotated = half.transform(&v);
593
594 assert!((rotated.get(1) - 0.0).abs() < 1e-10);
596 assert!((rotated.get(2) - 1.0).abs() < 1e-10);
597 }
598
599 #[test]
600 fn test_translation() {
601 let t = Translation::new(1.0, 2.0, 3.0);
602 let v = GA3::from_vector(&Vector::from_components(0.0, 0.0, 0.0));
603
604 let translated = t.transform(&v);
605
606 assert!((translated.get(1) - 1.0).abs() < 1e-10);
607 assert!((translated.get(2) - 2.0).abs() < 1e-10);
608 assert!((translated.get(4) - 3.0).abs() < 1e-10);
609 }
610
611 #[test]
612 fn test_transform_composition() {
613 let rot = Rotor::xy(PI / 2.0);
614 let trans = Translation::new(1.0, 0.0, 0.0);
615 let transform = Transform::new(rot, trans);
616
617 let v = GA3::from_vector(&Vector::from_components(1.0, 0.0, 0.0));
618 let result = transform.transform(&v);
619
620 assert!((result.get(1) - 1.0).abs() < 1e-10);
623 assert!((result.get(2) - 1.0).abs() < 1e-10);
624 }
625
626 #[test]
627 fn test_amari_rotor_roundtrip() {
628 let r = Rotor::xy(PI / 3.0);
629 let amari_r = r.to_amari_rotor();
630
631 let scalar = amari_r.as_multivector().get(0);
633 let angle = scalar.clamp(-1.0, 1.0).acos() * 2.0;
634 assert!((angle - PI / 3.0).abs() < 1e-10);
635
636 let r2 = Rotor::from_amari_rotor(&amari_r);
638 let v = GA3::from_vector(&Vector::from_components(1.0, 0.0, 0.0));
639
640 let result1 = r.transform(&v);
641 let result2 = r2.transform(&v);
642
643 assert!((result1.get(1) - result2.get(1)).abs() < 1e-10);
644 assert!((result1.get(2) - result2.get(2)).abs() < 1e-10);
645 assert!((result1.get(4) - result2.get(4)).abs() < 1e-10);
646 }
647}