1#![forbid(unsafe_code)]
26#![forbid(missing_docs)]
27#![allow(clippy::doc_markdown)]
28#![allow(clippy::type_complexity)]
29
30pub use approx;
31pub use nalgebra;
32
33use approx::{abs_diff_eq, AbsDiffEq};
34use nalgebra::{
35 base::allocator::Allocator,
36 base::dimension::{U1, U2, U3, U4, U5, U6},
37 constraint::{
38 AreMultipliable, DimEq, SameDimension, SameNumberOfColumns, SameNumberOfRows,
39 ShapeConstraint,
40 },
41 storage::{Owned, RawStorage, RawStorageMut, Storage, StorageMut},
42 DefaultAllocator, Dim, DimName, DimNameDiff, DimNameSub, Matrix, MatrixView, MatrixViewMut,
43 OMatrix, OVector, RealField, Scalar, Unit, VectorView,
44};
45use std::ops::{Add, Neg, Sub};
46
47#[inline]
48fn neg<T: RealField>(n: &mut T) {
49 *n = -n.clone();
50}
51
52pub trait Lorentzian<T, R, C>
60where
61 T: Scalar,
62 R: DimName,
63 C: DimName,
64{
65 #[must_use]
111 fn metric() -> Self
112 where
113 ShapeConstraint: SameDimension<R, C>;
114
115 #[must_use]
119 fn dual(&self) -> Self;
120
121 #[must_use]
125 fn r_dual(&self) -> Self;
126
127 #[must_use]
131 fn c_dual(&self) -> Self;
132
133 fn dual_mut(&mut self);
137
138 fn r_dual_mut(&mut self);
143
144 fn c_dual_mut(&mut self);
148
149 #[must_use]
154 fn contr<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> OMatrix<T, R, C2>
155 where
156 R2: Dim,
157 C2: Dim,
158 SB: Storage<T, R2, C2>,
159 ShapeConstraint: AreMultipliable<R, C, R2, C2>,
160 DefaultAllocator: Allocator<R, C2>;
161
162 #[must_use]
167 fn tr_contr<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> OMatrix<T, C, C2>
168 where
169 R2: Dim,
170 C2: Dim,
171 SB: Storage<T, R2, C2>,
172 ShapeConstraint: SameNumberOfRows<R, R2>,
173 DefaultAllocator: Allocator<C, C2>;
174
175 #[must_use]
189 fn scalar<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> T
190 where
191 R2: Dim,
192 C2: Dim,
193 SB: Storage<T, R2, C2>,
194 ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>;
195
196 #[must_use]
200 fn tr_scalar<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> T
201 where
202 R2: Dim,
203 C2: Dim,
204 SB: Storage<T, R2, C2>,
205 ShapeConstraint: DimEq<C, R2> + DimEq<R, C2>;
206
207 #[must_use]
213 fn timelike_norm(&self) -> T;
214
215 #[must_use]
221 fn spacelike_norm(&self) -> T;
222
223 #[must_use]
230 fn interval(&self, rhs: &Self) -> (T, LightCone)
231 where
232 T: AbsDiffEq,
233 ShapeConstraint: DimEq<U1, C>;
234
235 #[must_use]
249 fn interval_fn<P, L>(&self, rhs: &Self, is_present: P, is_lightlike: L) -> (T, LightCone)
250 where
251 ShapeConstraint: DimEq<U1, C>,
252 P: Fn(&T) -> bool,
253 L: Fn(&T) -> bool;
254
255 #[must_use]
302 fn new_boost<D>(frame: &OFrame<T, D>) -> Self
303 where
304 D: DimNameSub<U1>,
305 ShapeConstraint: AreMultipliable<R, C, R, C> + DimEq<R, D>,
306 DefaultAllocator: Allocator<DimNameDiff<D, U1>>;
307
308 #[must_use]
325 fn boost<D>(&self, frame: &OFrame<T, D>) -> Self
326 where
327 R: DimNameSub<U1>,
328 D: DimNameSub<U1>,
329 ShapeConstraint: SameNumberOfRows<R, D>
330 + SameNumberOfColumns<C, U1>
331 + DimEq<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>
332 + SameNumberOfRows<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>,
333 DefaultAllocator: Allocator<DimNameDiff<D, U1>>;
334
335 fn boost_mut<D>(&mut self, frame: &OFrame<T, D>)
373 where
374 R: DimNameSub<U1>,
375 D: DimNameSub<U1>,
376 ShapeConstraint: SameNumberOfRows<R, D>
377 + SameNumberOfColumns<C, U1>
378 + DimEq<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>
379 + SameNumberOfRows<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>,
380 DefaultAllocator: Allocator<DimNameDiff<D, U1>>;
381
382 #[must_use]
384 fn new_velocity<D>(frame: &OFrame<T, D>) -> OVector<T, D>
385 where
386 D: DimNameSub<U1>,
387 ShapeConstraint: SameNumberOfRows<R, D> + SameNumberOfColumns<C, U1>,
388 DefaultAllocator: Allocator<DimNameDiff<D, U1>> + Allocator<D>;
389
390 #[must_use]
392 fn frame(&self) -> OFrame<T, R>
393 where
394 R: DimNameSub<U1>,
395 ShapeConstraint: SameNumberOfColumns<C, U1>,
396 DefaultAllocator: Allocator<DimNameDiff<R, U1>>;
397
398 #[must_use]
400 fn from_split<D>(temporal: &T, spatial: &OVector<T, DimNameDiff<D, U1>>) -> OVector<T, D>
401 where
402 D: DimNameSub<U1>,
403 ShapeConstraint: SameNumberOfRows<R, D> + SameNumberOfColumns<C, U1>,
404 DefaultAllocator: Allocator<DimNameDiff<D, U1>> + Allocator<D>,
405 <DefaultAllocator as Allocator<D, U1>>::Buffer<T>:
406 StorageMut<T, D, U1, RStride = U1, CStride = D>;
407
408 #[must_use]
410 fn split(&self) -> (&T, MatrixView<T, DimNameDiff<R, U1>, C, U1, R>)
411 where
412 R: DimNameSub<U1>,
413 ShapeConstraint: DimEq<U1, C>,
414 DefaultAllocator: Allocator<R, C>,
415 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
416 Storage<T, R, C, RStride = U1, CStride = R>;
417
418 fn split_mut(&mut self) -> (&mut T, MatrixViewMut<T, DimNameDiff<R, U1>, C>)
435 where
436 R: DimNameSub<U1>,
437 ShapeConstraint: DimEq<U1, C>,
438 DefaultAllocator: Allocator<R, C>,
439 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
440 Storage<T, R, C, RStride = U1, CStride = R>;
441
442 #[must_use]
444 fn temporal(&self) -> &T
445 where
446 R: DimNameSub<U1>,
447 ShapeConstraint: DimEq<U1, C>;
448
449 fn temporal_mut(&mut self) -> &mut T
451 where
452 R: DimNameSub<U1>,
453 ShapeConstraint: DimEq<U1, C>;
454
455 #[must_use]
457 fn spatial(&self) -> MatrixView<T, DimNameDiff<R, U1>, C, U1, R>
458 where
459 R: DimNameSub<U1>,
460 ShapeConstraint: DimEq<U1, C>,
461 DefaultAllocator: Allocator<R, C>,
462 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
463 Storage<T, R, C, RStride = U1, CStride = R>;
464
465 fn spatial_mut(&mut self) -> MatrixViewMut<T, DimNameDiff<R, U1>, C, U1, R>
467 where
468 R: DimNameSub<U1>,
469 ShapeConstraint: DimEq<U1, C>,
470 DefaultAllocator: Allocator<R, C>,
471 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
472 StorageMut<T, R, C, RStride = U1, CStride = R>;
473}
474
475impl<T, R, C> Lorentzian<T, R, C> for OMatrix<T, R, C>
476where
477 T: RealField,
478 R: DimName + DimNameSub<U1>,
479 C: DimName + DimNameSub<U1>,
480 DefaultAllocator: Allocator<R, C>,
481{
482 #[inline]
483 fn metric() -> Self
484 where
485 ShapeConstraint: SameDimension<R, C>,
486 {
487 let mut m = Self::identity();
488 neg(m.get_mut((0, 0)).unwrap());
489 m
490 }
491
492 #[inline]
493 fn dual(&self) -> Self {
494 let mut m = self.clone_owned();
495 m.dual_mut();
496 m
497 }
498
499 #[inline]
500 fn r_dual(&self) -> Self {
501 let mut m = self.clone_owned();
502 m.r_dual_mut();
503 m
504 }
505
506 #[inline]
507 fn c_dual(&self) -> Self {
508 let mut m = self.clone_owned();
509 m.c_dual_mut();
510 m
511 }
512
513 #[inline]
514 fn dual_mut(&mut self) {
515 if R::is::<U1>() || C::is::<U1>() {
516 neg(self.get_mut(0).unwrap());
517 } else if R::is::<C>() {
518 for i in 1..R::dim() {
519 neg(self.get_mut((i, 0)).unwrap());
520 neg(self.get_mut((0, i)).unwrap());
521 }
522 } else {
523 for r in 1..R::dim() {
524 neg(self.get_mut((r, 0)).unwrap());
525 }
526 for c in 1..C::dim() {
527 neg(self.get_mut((0, c)).unwrap());
528 }
529 }
530 }
531
532 #[inline]
533 fn r_dual_mut(&mut self) {
534 for c in 0..C::dim() {
535 neg(self.get_mut((0, c)).unwrap());
536 }
537 }
538
539 #[inline]
540 fn c_dual_mut(&mut self) {
541 for r in 0..R::dim() {
542 neg(self.get_mut((r, 0)).unwrap());
543 }
544 }
545
546 #[inline]
547 fn contr<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> OMatrix<T, R, C2>
548 where
549 R2: Dim,
550 C2: Dim,
551 SB: Storage<T, R2, C2>,
552 ShapeConstraint: AreMultipliable<R, C, R2, C2>,
553 DefaultAllocator: Allocator<R, C2>,
554 {
555 self.c_dual() * rhs
556 }
557
558 #[inline]
559 fn tr_contr<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> OMatrix<T, C, C2>
560 where
561 R2: Dim,
562 C2: Dim,
563 SB: Storage<T, R2, C2>,
564 ShapeConstraint: SameNumberOfRows<R, R2>,
565 DefaultAllocator: Allocator<C, C2>,
566 {
567 self.r_dual().tr_mul(rhs)
568 }
569
570 #[inline]
571 fn scalar<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> T
572 where
573 R2: Dim,
574 C2: Dim,
575 SB: Storage<T, R2, C2>,
576 ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
577 {
578 self.dual().dot(rhs)
579 }
580
581 #[inline]
582 fn tr_scalar<R2, C2, SB>(&self, rhs: &Matrix<T, R2, C2, SB>) -> T
583 where
584 R2: Dim,
585 C2: Dim,
586 SB: Storage<T, R2, C2>,
587 ShapeConstraint: DimEq<C, R2> + DimEq<R, C2>,
588 {
589 self.dual().tr_dot(rhs)
590 }
591
592 #[inline]
593 fn timelike_norm(&self) -> T {
594 self.scalar(self).neg().sqrt()
595 }
596
597 #[inline]
598 fn spacelike_norm(&self) -> T {
599 self.scalar(self).sqrt()
600 }
601
602 #[inline]
603 fn interval(&self, rhs: &Self) -> (T, LightCone)
604 where
605 T: AbsDiffEq,
606 ShapeConstraint: DimEq<U1, C>,
607 {
608 self.interval_fn(
609 rhs,
610 |time| abs_diff_eq!(time, &T::zero()),
611 |interval| abs_diff_eq!(interval, &T::zero()),
612 )
613 }
614
615 fn interval_fn<P, L>(&self, rhs: &Self, is_present: P, is_lightlike: L) -> (T, LightCone)
616 where
617 ShapeConstraint: DimEq<U1, C>,
618 P: Fn(&T) -> bool,
619 L: Fn(&T) -> bool,
620 {
621 let time = self[0].clone();
622 let difference = rhs - self;
623 let interval = difference.scalar(&difference);
624 let light_cone = if is_lightlike(&interval) {
625 if is_present(&time) {
626 LightCone::Origin
627 } else if time.is_sign_positive() {
628 LightCone::LightlikeFuture
629 } else {
630 LightCone::LightlikePast
631 }
632 } else if interval.is_sign_positive() || is_present(&time) {
633 LightCone::Spacelike
634 } else if time.is_sign_positive() {
635 LightCone::TimelikeFuture
636 } else {
637 LightCone::TimelikePast
638 };
639 (interval, light_cone)
640 }
641
642 fn new_boost<D>(frame: &OFrame<T, D>) -> Self
643 where
644 D: DimNameSub<U1>,
645 ShapeConstraint: AreMultipliable<R, C, R, C> + DimEq<R, D>,
646 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
647 {
648 let OFrame {
649 zeta_cosh,
650 zeta_sinh,
651 axis,
652 } = frame;
653 let mut b = Self::zeros();
654 for (i, u) in axis.iter().enumerate() {
655 b[(i + 1, 0)] = u.clone();
656 b[(0, i + 1)] = u.clone();
657 }
658 let uk = b.clone_owned();
659 b.gemm(zeta_cosh.clone() - T::one(), &uk, &uk, -zeta_sinh.clone());
660 for i in 0..D::dim() {
661 b[(i, i)] += T::one();
662 }
663 b
664 }
665
666 #[inline]
667 fn boost<D>(&self, frame: &OFrame<T, D>) -> Self
668 where
669 R: DimNameSub<U1>,
670 D: DimNameSub<U1>,
671 ShapeConstraint: SameNumberOfRows<R, D>
672 + SameNumberOfColumns<C, U1>
673 + DimEq<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>
674 + SameNumberOfRows<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>,
675 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
676 {
677 let mut v = self.clone_owned();
678 v.boost_mut(frame);
679 v
680 }
681
682 fn boost_mut<D>(&mut self, frame: &OFrame<T, D>)
683 where
684 R: DimNameSub<U1>,
685 D: DimNameSub<U1>,
686 ShapeConstraint: SameNumberOfRows<R, D>
687 + SameNumberOfColumns<C, U1>
688 + DimEq<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>
689 + SameNumberOfRows<<R as DimNameSub<U1>>::Output, <D as DimNameSub<U1>>::Output>,
690 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
691 {
692 let OFrame {
693 zeta_cosh,
694 zeta_sinh,
695 axis,
696 } = frame;
697 let u = axis.as_ref();
698 let a = self[0].clone();
699 let (rows, _cols) = self.shape_generic();
700 let zu = self.rows_generic(1, rows.sub(U1)).dot(u);
701 self[0] = zeta_cosh.clone() * a.clone() - zeta_sinh.clone() * zu.clone();
702 let mut z = self.rows_generic_mut(1, rows.sub(U1));
703 z += u * ((zeta_cosh.clone() - T::one()) * zu - zeta_sinh.clone() * a);
704 }
705
706 #[inline]
707 fn new_velocity<D>(frame: &OFrame<T, D>) -> OVector<T, D>
708 where
709 D: DimNameSub<U1>,
710 ShapeConstraint: SameNumberOfRows<R, D> + SameNumberOfColumns<C, U1>,
711 DefaultAllocator: Allocator<DimNameDiff<D, U1>> + Allocator<D>,
712 {
713 frame.velocity()
714 }
715
716 #[inline]
717 fn frame(&self) -> OFrame<T, R>
718 where
719 R: DimNameSub<U1>,
720 ShapeConstraint: SameNumberOfColumns<C, U1>,
721 DefaultAllocator: Allocator<DimNameDiff<R, U1>>,
722 {
723 OFrame::<T, R>::from_velocity(self)
724 }
725
726 #[inline]
727 fn from_split<D>(temporal: &T, spatial: &OVector<T, DimNameDiff<D, U1>>) -> OVector<T, D>
728 where
729 D: DimNameSub<U1>,
730 ShapeConstraint: SameNumberOfRows<R, D> + SameNumberOfColumns<C, U1>,
731 DefaultAllocator: Allocator<DimNameDiff<D, U1>> + Allocator<D>,
732 <DefaultAllocator as Allocator<D, U1>>::Buffer<T>:
733 StorageMut<T, D, U1, RStride = U1, CStride = D>,
734 {
735 let mut v = OVector::<T, D>::zeros();
736 *v.temporal_mut() = temporal.clone();
737 v.spatial_mut().copy_from(spatial);
738 v
739 }
740
741 #[inline]
742 fn split(&self) -> (&T, MatrixView<T, DimNameDiff<R, U1>, C, U1, R>)
743 where
744 R: DimNameSub<U1>,
745 ShapeConstraint: DimEq<U1, C>,
746 DefaultAllocator: Allocator<R, C>,
747 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
748 Storage<T, R, C, RStride = U1, CStride = R>,
749 {
750 (self.temporal(), self.spatial())
751 }
752
753 #[inline]
754 fn split_mut(&mut self) -> (&mut T, MatrixViewMut<T, DimNameDiff<R, U1>, C>)
755 where
756 R: DimNameSub<U1>,
757 ShapeConstraint: DimEq<U1, C>,
758 DefaultAllocator: Allocator<R, C>,
759 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
760 Storage<T, R, C, RStride = U1, CStride = R>,
761 {
762 let (temporal, spatial) = self.as_mut_slice().split_at_mut(1);
763 (
764 temporal.get_mut(0).unwrap(),
765 MatrixViewMut::<T, DimNameDiff<R, U1>, C>::from_slice(spatial),
766 )
767 }
768
769 #[inline]
770 fn temporal(&self) -> &T
771 where
772 R: DimNameSub<U1>,
773 ShapeConstraint: DimEq<U1, C>,
774 {
775 self.get(0).unwrap()
776 }
777
778 #[inline]
779 fn temporal_mut(&mut self) -> &mut T
780 where
781 R: DimNameSub<U1>,
782 ShapeConstraint: DimEq<U1, C>,
783 {
784 self.get_mut(0).unwrap()
785 }
786
787 #[inline]
788 fn spatial(&self) -> MatrixView<T, DimNameDiff<R, U1>, C, U1, R>
789 where
790 R: DimNameSub<U1>,
791 ShapeConstraint: DimEq<U1, C>,
792 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
793 RawStorage<T, R, C, RStride = U1, CStride = R>,
794 {
795 let (rows, _cols) = self.shape_generic();
796 self.rows_generic(1, rows.sub(U1))
797 }
798
799 #[inline]
800 fn spatial_mut(&mut self) -> MatrixViewMut<T, DimNameDiff<R, U1>, C, U1, R>
801 where
802 R: DimNameSub<U1>,
803 ShapeConstraint: DimEq<U1, C>,
804 <DefaultAllocator as Allocator<R, C>>::Buffer<T>:
805 RawStorageMut<T, R, C, RStride = U1, CStride = R>,
806 {
807 let (rows, _cols) = self.shape_generic();
808 self.rows_generic_mut(1, rows.sub(U1))
809 }
810}
811
812#[derive(Debug, PartialEq, Eq, Clone, Copy)]
814pub enum LightCone {
815 TimelikeFuture,
817 LightlikeFuture,
819 Origin,
821 LightlikePast,
823 TimelikePast,
825 Spacelike,
827}
828
829pub type Frame2<T> = OFrame<T, U2>;
832pub type Frame3<T> = OFrame<T, U3>;
835pub type Frame4<T> = OFrame<T, U4>;
838pub type Frame5<T> = OFrame<T, U5>;
841pub type Frame6<T> = OFrame<T, U6>;
844
845#[derive(Debug, PartialEq, Eq, Clone)]
861pub struct OFrame<T, D>
862where
863 T: Scalar,
864 D: DimNameSub<U1>,
865 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
866{
867 zeta_cosh: T,
868 zeta_sinh: T,
869 axis: Unit<OVector<T, DimNameDiff<D, U1>>>,
870}
871
872impl<T, D> OFrame<T, D>
873where
874 T: RealField,
875 D: DimNameSub<U1>,
876 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
877{
878 #[must_use]
880 pub fn from_velocity<R, C>(u: &OMatrix<T, R, C>) -> Self
881 where
882 R: DimNameSub<U1>,
883 C: Dim,
884 ShapeConstraint: SameNumberOfRows<R, D> + SameNumberOfColumns<C, U1>,
885 DefaultAllocator: Allocator<R, C>,
886 {
887 let mut scaled_axis = OVector::zeros();
888 let zeta_cosh = u[0].clone();
889 let (rows, _cols) = u.shape_generic();
890 scaled_axis
891 .iter_mut()
892 .zip(u.rows_generic(1, rows.sub(U1)).iter())
893 .for_each(|(scaled_axis, u)| *scaled_axis = u.clone());
894 let (axis, zeta_sinh) = Unit::new_and_get(scaled_axis);
895 Self {
896 zeta_cosh,
897 zeta_sinh,
898 axis,
899 }
900 }
901
902 #[must_use]
904 #[inline]
905 pub fn from_zeta(scaled_axis: OVector<T, DimNameDiff<D, U1>>) -> Self {
906 let (axis, zeta) = Unit::new_and_get(scaled_axis);
907 Self::from_axis_zeta(axis, zeta)
908 }
909
910 #[must_use]
912 #[inline]
913 pub fn from_axis_zeta(axis: Unit<OVector<T, DimNameDiff<D, U1>>>, zeta: T) -> Self {
914 Self {
915 zeta_cosh: zeta.clone().cosh(),
916 zeta_sinh: zeta.sinh(),
917 axis,
918 }
919 }
920
921 #[must_use]
923 #[inline]
924 pub fn from_beta(scaled_axis: OVector<T, DimNameDiff<D, U1>>) -> Self {
925 let (axis, beta) = Unit::new_and_get(scaled_axis);
926 Self::from_axis_beta(axis, beta)
927 }
928
929 #[must_use]
931 #[inline]
932 pub fn from_axis_beta(axis: Unit<OVector<T, DimNameDiff<D, U1>>>, beta: T) -> Self {
933 debug_assert!(
934 -T::one() < beta && beta < T::one(),
935 "Velocity ratio `beta` is out of range (-1, +1)"
936 );
937 let gamma = T::one() / (T::one() - beta.clone() * beta.clone()).sqrt();
938 Self {
939 zeta_cosh: gamma.clone(),
940 zeta_sinh: beta * gamma,
941 axis,
942 }
943 }
944
945 #[must_use]
947 pub fn velocity(&self) -> OVector<T, D>
948 where
949 DefaultAllocator: Allocator<D>,
950 {
951 let mut u = OVector::<T, D>::zeros();
952 u[0] = self.gamma();
953 let (rows, _cols) = u.shape_generic();
954 u.rows_generic_mut(1, rows.sub(U1))
955 .iter_mut()
956 .zip(self.axis.iter())
957 .for_each(|(u, axis)| *u = self.beta_gamma() * axis.clone());
958 u
959 }
960
961 #[must_use]
963 #[inline]
964 pub fn axis(&self) -> Unit<OVector<T, DimNameDiff<D, U1>>> {
965 self.axis.clone()
966 }
967
968 #[must_use]
970 #[inline]
971 pub fn zeta(&self) -> T {
972 self.beta().atanh()
973 }
974
975 #[must_use]
977 #[inline]
978 pub fn beta(&self) -> T {
979 self.beta_gamma() / self.gamma()
980 }
981
982 #[must_use]
984 #[inline]
985 pub fn gamma(&self) -> T {
986 self.zeta_cosh.clone()
987 }
988
989 #[must_use]
991 #[inline]
992 pub fn beta_gamma(&self) -> T {
993 self.zeta_sinh.clone()
994 }
995
996 #[must_use]
1000 #[inline]
1001 pub fn compose(&self, frame: &Self) -> Self
1002 where
1003 DefaultAllocator: Allocator<D>,
1004 {
1005 frame.velocity().boost(&-self.clone()).frame()
1006 }
1007
1008 #[must_use]
1018 pub fn angle(&self, frame: &Self) -> T
1019 where
1020 DefaultAllocator: Allocator<D>,
1021 {
1022 let (u, v) = (self, frame);
1023 let ucv = u.compose(v).axis();
1024 let vcu = v.compose(u).axis();
1025 ucv.dot(&vcu).clamp(-T::one(), T::one()).acos()
1026 }
1027
1028 #[must_use]
1096 #[allow(clippy::similar_names, clippy::many_single_char_names)]
1097 pub fn rotation(&self, frame: &Self) -> (OMatrix<T, D, D>, T)
1098 where
1099 DefaultAllocator: Allocator<D>
1100 + Allocator<U1, DimNameDiff<D, U1>>
1101 + Allocator<D, D>
1102 + Allocator<DimNameDiff<D, U1>, DimNameDiff<D, U1>>,
1103 {
1104 let (u, v) = (self, frame);
1105 let a = &u.compose(v).axis().into_inner();
1106 let b = &v.compose(u).axis().into_inner();
1107 let ab = a.dot(b);
1108 let d = T::one() + ab.clone();
1109 let [at, bt] = &[a.transpose(), b.transpose()];
1110 let [b_at, a_bt, a_at, b_bt] = &[b * at, a * bt, a * at, b * bt];
1111 let [k1, k2] = [b_at - a_bt, (b_at + a_bt) * ab.clone() - (a_at + b_bt)];
1112 let mut mat = OMatrix::<T, D, D>::identity();
1113 let (r, c) = mat.shape_generic();
1114 let mut rot = mat.generic_view_mut((1, 1), (r.sub(U1), c.sub(U1)));
1115 rot += k1 + k2 / d;
1116 (mat, ab.clamp(-T::one(), T::one()).acos())
1117 }
1118
1119 #[must_use]
1155 pub fn axis_angle(&self, frame: &Self) -> (Unit<OVector<T, DimNameDiff<D, U1>>>, T)
1156 where
1157 T: RealField,
1158 D: DimNameSub<U1>,
1159 ShapeConstraint: DimEq<D, U4>,
1160 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
1161 {
1162 let (u, v) = (self, frame);
1163 let (axis, sin) = Unit::new_and_get(u.axis().cross(&v.axis()));
1164 let uv = u.axis().dot(&v.axis());
1165 let bg = u.beta_gamma() * v.beta_gamma();
1166 let ug = u.gamma();
1167 let vg = v.gamma();
1168 let cg = ug.clone() * vg.clone() + bg.clone() * uv;
1169 let sum = T::one() + cg.clone() + ug.clone() + vg.clone();
1170 let prod = (T::one() + cg) * (T::one() + ug) * (T::one() + vg);
1171 (axis, (sum / prod * bg * sin).asin())
1172 }
1173}
1174
1175impl<T, R, C> From<OMatrix<T, R, C>> for OFrame<T, R>
1176where
1177 T: RealField,
1178 R: DimNameSub<U1>,
1179 C: DimNameSub<U1>,
1180 ShapeConstraint: SameNumberOfColumns<C, U1>,
1181 DefaultAllocator: Allocator<R, C> + Allocator<DimNameDiff<R, U1>>,
1182{
1183 #[inline]
1184 fn from(u: OMatrix<T, R, C>) -> Self {
1185 u.frame()
1186 }
1187}
1188
1189impl<T, D> From<OFrame<T, D>> for OVector<T, D>
1190where
1191 T: RealField,
1192 D: DimNameSub<U1>,
1193 DefaultAllocator: Allocator<DimNameDiff<D, U1>> + Allocator<D>,
1194{
1195 #[inline]
1196 fn from(frame: OFrame<T, D>) -> Self {
1197 frame.velocity()
1198 }
1199}
1200
1201impl<T, D> Neg for OFrame<T, D>
1202where
1203 T: RealField,
1204 D: DimNameSub<U1>,
1205 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
1206{
1207 type Output = Self;
1208
1209 #[inline]
1210 fn neg(mut self) -> Self::Output {
1211 self.axis = -self.axis;
1212 self
1213 }
1214}
1215
1216impl<T, D> Add for OFrame<T, D>
1217where
1218 T: RealField,
1219 D: DimNameSub<U1>,
1220 DefaultAllocator: Allocator<DimNameDiff<D, U1>> + Allocator<D>,
1221{
1222 type Output = Self;
1223
1224 #[inline]
1225 fn add(self, rhs: Self) -> Self::Output {
1226 self.compose(&rhs)
1227 }
1228}
1229
1230impl<T, D> Sub for OFrame<T, D>
1231where
1232 T: RealField,
1233 D: DimNameSub<U1>,
1234 DefaultAllocator: Allocator<DimNameDiff<D, U1>> + Allocator<D>,
1235{
1236 type Output = Self;
1237
1238 #[inline]
1239 fn sub(self, rhs: Self) -> Self::Output {
1240 self.compose(&-rhs)
1241 }
1242}
1243
1244impl<T, D> Copy for OFrame<T, D>
1245where
1246 T: RealField + Copy,
1247 D: DimNameSub<U1>,
1248 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
1249 Owned<T, DimNameDiff<D, U1>>: Copy,
1250{
1251}
1252
1253#[repr(transparent)]
1275#[derive(Debug, PartialEq, Clone)]
1276pub struct OMomentum<T, D>
1277where
1278 T: Scalar,
1279 D: DimNameSub<U1>,
1280 DefaultAllocator: Allocator<D>,
1281{
1282 momentum: OVector<T, D>,
1283}
1284
1285impl<T, D> OMomentum<T, D>
1286where
1287 T: RealField,
1288 D: DimNameSub<U1>,
1289 DefaultAllocator: Allocator<D>,
1290{
1291 #[must_use]
1294 #[inline]
1295 pub fn from_split(energy: &T, momentum: &OVector<T, DimNameDiff<D, U1>>) -> Self
1296 where
1297 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
1298 <DefaultAllocator as Allocator<D, U1>>::Buffer<T>:
1299 StorageMut<T, D, U1, RStride = U1, CStride = D>,
1300 {
1301 Self {
1302 momentum: OVector::<T, D>::from_split(energy, momentum),
1303 }
1304 }
1305
1306 #[must_use]
1308 #[inline]
1309 pub fn from_mass_at_velocity(mass: T, velocity: OVector<T, D>) -> Self
1310 where
1311 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
1312 {
1313 Self {
1314 momentum: velocity * mass,
1315 }
1316 }
1317
1318 #[must_use]
1322 #[inline]
1323 pub fn from_mass_in_frame(mass: T, frame: &OFrame<T, D>) -> Self
1324 where
1325 DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
1326 {
1327 Self::from_mass_at_velocity(mass, frame.velocity())
1328 }
1329
1330 #[must_use]
1332 #[inline]
1333 pub fn from_mass_at_rest(mass: T) -> Self {
1334 let mut momentum = OVector::<T, D>::zeros();
1335 *momentum.temporal_mut() = mass;
1336 Self { momentum }
1337 }
1338
1339 #[must_use]
1341 #[inline]
1342 pub fn mass(&self) -> T {
1343 self.momentum.timelike_norm()
1344 }
1345
1346 #[must_use]
1348 #[inline]
1349 pub fn velocity(&self) -> OVector<T, D> {
1350 self.momentum.clone() / self.mass()
1351 }
1352
1353 #[must_use]
1355 #[inline]
1356 pub fn energy(&self) -> &T {
1357 self.momentum.temporal()
1358 }
1359
1360 #[must_use]
1362 #[inline]
1363 pub fn momentum(&self) -> VectorView<T, DimNameDiff<D, U1>, U1, D>
1364 where
1365 DefaultAllocator: Allocator<D, U1>,
1366 <DefaultAllocator as Allocator<D, U1>>::Buffer<T>:
1367 Storage<T, D, U1, RStride = U1, CStride = D>,
1368 {
1369 self.momentum.spatial()
1370 }
1371}
1372
1373impl<T, D> From<OVector<T, D>> for OMomentum<T, D>
1374where
1375 T: RealField,
1376 D: DimNameSub<U1>,
1377 DefaultAllocator: Allocator<D>,
1378{
1379 #[inline]
1380 fn from(momentum: OVector<T, D>) -> Self {
1381 Self { momentum }
1382 }
1383}
1384
1385impl<T, D> From<OMomentum<T, D>> for OVector<T, D>
1386where
1387 T: RealField,
1388 D: DimNameSub<U1>,
1389 DefaultAllocator: Allocator<D>,
1390{
1391 #[inline]
1392 fn from(momentum: OMomentum<T, D>) -> Self {
1393 momentum.momentum
1394 }
1395}
1396
1397impl<T, D> Neg for OMomentum<T, D>
1398where
1399 T: RealField,
1400 D: DimNameSub<U1>,
1401 DefaultAllocator: Allocator<D>,
1402{
1403 type Output = Self;
1404
1405 #[inline]
1406 fn neg(mut self) -> Self::Output {
1407 self.momentum = -self.momentum;
1408 self
1409 }
1410}
1411
1412impl<T, D> Add<Self> for OMomentum<T, D>
1413where
1414 T: RealField,
1415 D: DimNameSub<U1>,
1416 DefaultAllocator: Allocator<D>,
1417{
1418 type Output = Self;
1419
1420 #[inline]
1421 fn add(self, rhs: Self) -> Self::Output {
1422 Self {
1423 momentum: self.momentum + rhs.momentum,
1424 }
1425 }
1426}
1427
1428impl<T, D> Sub<Self> for OMomentum<T, D>
1429where
1430 T: RealField,
1431 D: DimNameSub<U1>,
1432 DefaultAllocator: Allocator<D>,
1433{
1434 type Output = Self;
1435
1436 #[inline]
1437 fn sub(self, rhs: Self) -> Self::Output {
1438 Self {
1439 momentum: self.momentum - rhs.momentum,
1440 }
1441 }
1442}
1443
1444impl<T, D> Copy for OMomentum<T, D>
1445where
1446 T: RealField + Copy,
1447 D: DimNameSub<U1>,
1448 DefaultAllocator: Allocator<D>,
1449 Owned<T, D>: Copy,
1450{
1451}
1452
1453pub type Momentum2<T> = OMomentum<T, U2>;
1455pub type Momentum3<T> = OMomentum<T, U3>;
1457pub type Momentum4<T> = OMomentum<T, U4>;
1459pub type Momentum5<T> = OMomentum<T, U5>;
1461pub type Momentum6<T> = OMomentum<T, U6>;