1#![allow(non_snake_case)]
5
6use serde::Deserialize;
7
8use na::core::dimension::{U1, U2, U3, U4};
9use na::core::{Matrix3, Matrix4, OMatrix, Vector3, Vector5};
10use na::geometry::{Point2, Point3, Rotation3, UnitQuaternion};
11use na::{DefaultAllocator, RealField, allocator::Allocator};
12use nalgebra as na;
13use num_traits::{One, Zero};
14
15use opencv_ros_camera::UndistortedPixels;
16
17use crate::pymvg_support::PymvgCamera;
18use crate::{
19 DistortedPixel, Distortion, ExtrinsicParameters, MvgError, PointWorldFrame, Result,
20 RosOpenCvIntrinsics, UndistortedPixel,
21};
22
23#[derive(Clone, PartialEq)]
24pub struct Camera<R: RealField> {
73 pub(crate) width: usize,
74 pub(crate) height: usize,
75 pub(crate) inner: cam_geom::Camera<R, RosOpenCvIntrinsics<R>>,
76 pub(crate) cache: CameraCache<R>,
77}
78
79impl<R: RealField + Copy> Camera<R> {
80 pub fn new(
116 width: usize,
117 height: usize,
118 extrinsics: ExtrinsicParameters<R>,
119 intrinsics: RosOpenCvIntrinsics<R>,
120 ) -> Result<Self> {
121 let inner = cam_geom::Camera::new(intrinsics, extrinsics);
122 Self::new_from_cam_geom(width, height, inner)
123 }
124
125 pub fn new_from_cam_geom(
153 width: usize,
154 height: usize,
155 inner: cam_geom::Camera<R, RosOpenCvIntrinsics<R>>,
156 ) -> Result<Self> {
157 let intrinsics = inner.intrinsics();
158 let extrinsics = inner.extrinsics();
159 let m = {
160 let p33 = intrinsics.p.fixed_view::<3, 3>(0, 0);
161 p33 * extrinsics.matrix()
162 };
163
164 let m = if m[(0, 0)] < na::convert(0.0) { -m } else { m };
166
167 let m = m / m[(2, 3)]; let pinv = my_pinv(&m)?;
170 let cache = CameraCache { m, pinv };
171 Ok(Self {
172 width,
173 height,
174 inner,
175 cache,
176 })
177 }
178
179 pub fn from_pmat(width: usize, height: usize, pmat: &OMatrix<R, U3, U4>) -> Result<Self> {
225 let distortion = Distortion::zero();
226 Self::from_pmat_with_distortion(width, height, pmat, distortion)
227 }
228
229 fn from_pmat_with_distortion(
230 width: usize,
231 height: usize,
232 pmat: &OMatrix<R, U3, U4>,
233 distortion: Distortion<R>,
234 ) -> Result<Self> {
235 let m = (*pmat).remove_column(3);
236 let (rquat, k) = rq_decomposition(m)?;
237
238 let k22: R = k[(2, 2)];
239
240 let one: R = One::one();
241
242 let k = k * (one / k22); let fx = k[(0, 0)];
244 let skew = k[(0, 1)];
245 let fy = k[(1, 1)];
246 let cx = k[(0, 2)];
247 let cy = k[(1, 2)];
248
249 let intrinsics =
250 RosOpenCvIntrinsics::from_params_with_distortion(fx, skew, fy, cx, cy, distortion);
251 let camcenter = pmat2cam_center(pmat);
252 let extrinsics = ExtrinsicParameters::from_rotation_and_camcenter(rquat, camcenter);
253
254 Camera::new(width, height, extrinsics, intrinsics)
255 }
256
257 pub fn as_pmat(&self) -> Option<&OMatrix<R, U3, U4>> {
259 let d = &self.intrinsics().distortion;
260 if d.is_linear() {
261 Some(&self.cache.m)
262 } else {
263 None
264 }
265 }
266
267 pub fn linear_part_as_pmat(&self) -> &OMatrix<R, U3, U4> {
291 &self.cache.m
292 }
293
294 pub fn linearize_to_cam_geom(
300 &self,
301 ) -> cam_geom::Camera<R, cam_geom::IntrinsicParametersPerspective<R>> {
302 let fx = self.intrinsics().k[(0, 0)];
303 let skew = self.intrinsics().k[(0, 1)];
304 let fy = self.intrinsics().k[(1, 1)];
305 let cx = self.intrinsics().k[(0, 2)];
306 let cy = self.intrinsics().k[(1, 2)];
307
308 let intrinsics =
309 cam_geom::IntrinsicParametersPerspective::from(cam_geom::PerspectiveParams {
310 fx,
311 fy,
312 skew,
313 cx,
314 cy,
315 });
316
317 let pose = self.extrinsics().clone();
318 cam_geom::Camera::new(intrinsics, pose)
319 }
320
321 pub fn align(&self, s: R, rot: Matrix3<R>, t: Vector3<R>) -> Result<Self> {
372 let m = build_xform(s, rot, t);
373 let mi = my_pinv_4x4(&m)?;
374
375 let pmat = &self.cache.m;
376 let aligned_pmat = pmat * mi;
377
378 Self::from_pmat_with_distortion(
379 self.width,
380 self.height,
381 &aligned_pmat,
382 self.intrinsics().distortion.clone(),
383 )
384 }
385
386 pub fn flip(&self) -> Option<Camera<R>> {
391 use crate::intrinsics::{MirrorAxis::LeftRight, mirror};
392 if !self.intrinsics().rect.is_identity(na::convert(1.0e-7)) {
393 return None;
394 }
395
396 let cc = self.extrinsics().camcenter();
397
398 let lv = self.extrinsics().forward();
399 let lv2 = -lv;
400 let la2 = cc.coords + lv2.as_ref();
401
402 let up = self.extrinsics().up();
403 let up2 = -up;
404
405 let extrinsics2 = crate::ExtrinsicParameters::from_view(&cc.coords, &la2, &up2);
406 let mut intinsics2 = mirror(self.intrinsics(), LeftRight)?;
407
408 intinsics2.p[(0, 1)] = -intinsics2.p[(0, 1)];
409 intinsics2.k[(0, 1)] = -intinsics2.k[(0, 1)];
410
411 let mut d = intinsics2.distortion.clone();
412 *d.tangential2_mut() = -d.tangential2();
413
414 Some(Camera::new(self.width(), self.height(), extrinsics2, intinsics2).unwrap())
415 }
416
417 #[inline]
419 pub fn intrinsics(&self) -> &RosOpenCvIntrinsics<R> {
420 self.inner.intrinsics()
421 }
422
423 #[inline]
425 pub fn extrinsics(&self) -> &ExtrinsicParameters<R> {
426 self.inner.extrinsics()
427 }
428
429 pub fn to_pymvg(&self, name: &str) -> PymvgCamera<R> {
454 let d = &self.intrinsics().distortion;
455 let dvec = Vector5::new(
456 d.radial1(),
457 d.radial2(),
458 d.tangential1(),
459 d.tangential2(),
460 d.radial3(),
461 );
462 PymvgCamera {
463 name: name.to_string(),
464 width: self.width,
465 height: self.height,
466 P: self.intrinsics().p,
467 K: self.intrinsics().k,
468 D: dvec,
469 R: self.intrinsics().rect,
470 Q: *self.extrinsics().rotation().matrix(),
471 translation: *self.extrinsics().translation(),
472 }
473 }
474
475 pub(crate) fn from_pymvg(cam: &PymvgCamera<R>) -> Result<(String, Self)> {
476 let name = cam.name.clone();
477
478 let rquat = right_handed_rotation_quat_new(&cam.Q)?;
479 let extrinsics = crate::extrinsics::from_rquat_translation(rquat, cam.translation);
480 let distortion = Distortion::from_opencv_vec(cam.D);
481 let intrinsics = RosOpenCvIntrinsics::from_components(cam.P, cam.K, distortion, cam.R)?;
482 let cam = Self::new(cam.width, cam.height, extrinsics, intrinsics)?;
483 Ok((name, cam))
484 }
485
486 #[inline]
488 pub fn width(&self) -> usize {
489 self.width
490 }
491
492 #[inline]
494 pub fn height(&self) -> usize {
495 self.height
496 }
497
498 pub fn project_3d_to_pixel(&self, pt3d: &PointWorldFrame<R>) -> UndistortedPixel<R> {
505 let coords: Point3<R> = pt3d.coords;
506
507 let cc = self.cache.m * coords.to_homogeneous();
508 UndistortedPixel {
509 coords: Point2::new(cc[0] / cc[2], cc[1] / cc[2]),
510 }
511 }
512
513 pub fn project_3d_to_distorted_pixel(&self, pt3d: &PointWorldFrame<R>) -> DistortedPixel<R> {
541 let undistorted = self.project_3d_to_pixel(pt3d);
542 let ud = UndistortedPixels {
543 data: OMatrix::<R, U1, U2>::new(undistorted.coords[0], undistorted.coords[1]),
544 };
545 self.intrinsics().distort(&ud).into()
546 }
547
548 pub fn project_pixel_to_3d_with_dist(
577 &self,
578 pt2d: &UndistortedPixel<R>,
579 dist: R,
580 ) -> PointWorldFrame<R>
581 where
582 DefaultAllocator: Allocator<U1, U2>,
583 DefaultAllocator: Allocator<U1, U3>,
584 {
585 let ray_cam = self.intrinsics().undistorted_pixel_to_camera(&pt2d.into());
586 let pt_cam = ray_cam.point_on_ray_at_distance(dist);
587 self.extrinsics().camera_to_world(&pt_cam).into()
588 }
589
590 pub fn project_distorted_pixel_to_3d_with_dist(
618 &self,
619 pt2d: &DistortedPixel<R>,
620 dist: R,
621 ) -> PointWorldFrame<R> {
622 use cam_geom::IntrinsicParameters;
623 let ray_cam = self.intrinsics().pixel_to_camera(&pt2d.into());
624 let pt_cam = ray_cam.point_on_ray_at_distance(dist);
625 self.extrinsics().camera_to_world(&pt_cam).into()
626 }
627}
628
629impl<R: RealField + Copy> std::default::Default for Camera<R> {
630 fn default() -> Camera<R> {
631 let extrinsics = crate::extrinsics::make_default_extrinsics();
632 let intrinsics = crate::make_default_intrinsics();
633 Camera::new(640, 480, extrinsics, intrinsics).unwrap()
634 }
635}
636
637impl<R: RealField + Copy> std::fmt::Debug for Camera<R> {
638 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
639 f.debug_struct("Camera")
640 .field("width", &self.width)
641 .field("height", &self.height)
642 .field("inner", &self.inner)
643 .finish()
644 }
645}
646
647impl<R: RealField + Copy> AsRef<cam_geom::Camera<R, RosOpenCvIntrinsics<R>>> for Camera<R> {
648 #[inline]
649 fn as_ref(&self) -> &cam_geom::Camera<R, RosOpenCvIntrinsics<R>> {
650 &self.inner
651 }
652}
653
654impl<R: RealField + serde::Serialize + Copy> serde::Serialize for Camera<R> {
655 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
656 where
657 S: serde::Serializer,
658 {
659 use serde::ser::SerializeStruct;
660
661 let mut state = serializer.serialize_struct("Camera", 5)?;
663 state.serialize_field("width", &self.width)?;
664 state.serialize_field("height", &self.height)?;
665 state.serialize_field("extrinsics", &self.extrinsics())?;
666 state.serialize_field("intrinsics", &self.intrinsics())?;
667 state.end()
668 }
669}
670
671impl<'de, R: RealField + serde::Deserialize<'de> + Copy> serde::Deserialize<'de> for Camera<R> {
672 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
673 where
674 D: serde::Deserializer<'de>,
675 {
676 use serde::de;
677 use std::fmt;
678
679 #[derive(Deserialize)]
680 #[serde(field_identifier, rename_all = "lowercase")]
681 enum Field {
682 Width,
683 Height,
684 Extrinsics,
685 Intrinsics,
686 }
687
688 struct CameraVisitor<'de, R2: RealField + serde::Deserialize<'de>>(
689 std::marker::PhantomData<&'de R2>,
690 );
691
692 impl<'de, R2: RealField + serde::Deserialize<'de> + Copy> serde::de::Visitor<'de>
693 for CameraVisitor<'de, R2>
694 {
695 type Value = Camera<R2>;
696
697 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
698 formatter.write_str("struct Camera")
699 }
700
701 fn visit_seq<V>(self, mut seq: V) -> std::result::Result<Camera<R2>, V::Error>
702 where
703 V: serde::de::SeqAccess<'de>,
704 {
705 let width = seq
706 .next_element()?
707 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
708 let height = seq
709 .next_element()?
710 .ok_or_else(|| de::Error::invalid_length(1, &self))?;
711 let extrinsics = seq
712 .next_element()?
713 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
714 let intrinsics = seq
715 .next_element()?
716 .ok_or_else(|| de::Error::invalid_length(1, &self))?;
717 Camera::new(width, height, extrinsics, intrinsics)
718 .map_err(|e| de::Error::custom(format!("failed creating Camera: {e}")))
719 }
720
721 fn visit_map<V>(self, mut map: V) -> std::result::Result<Camera<R2>, V::Error>
722 where
723 V: serde::de::MapAccess<'de>,
724 {
725 let mut width = None;
726 let mut height = None;
727 let mut extrinsics = None;
728 let mut intrinsics = None;
729 while let Some(key) = map.next_key()? {
730 match key {
731 Field::Width => {
732 if width.is_some() {
733 return Err(de::Error::duplicate_field("width"));
734 }
735 width = Some(map.next_value()?);
736 }
737 Field::Height => {
738 if height.is_some() {
739 return Err(de::Error::duplicate_field("height"));
740 }
741 height = Some(map.next_value()?);
742 }
743 Field::Extrinsics => {
744 if extrinsics.is_some() {
745 return Err(de::Error::duplicate_field("extrinsics"));
746 }
747 extrinsics = Some(map.next_value()?);
748 }
749 Field::Intrinsics => {
750 if intrinsics.is_some() {
751 return Err(de::Error::duplicate_field("intrinsics"));
752 }
753 intrinsics = Some(map.next_value()?);
754 }
755 }
756 }
757 let width = width.ok_or_else(|| de::Error::missing_field("width"))?;
758 let height = height.ok_or_else(|| de::Error::missing_field("height"))?;
759 let extrinsics =
760 extrinsics.ok_or_else(|| de::Error::missing_field("extrinsics"))?;
761 let intrinsics =
762 intrinsics.ok_or_else(|| de::Error::missing_field("intrinsics"))?;
763 Camera::new(width, height, extrinsics, intrinsics)
764 .map_err(|e| de::Error::custom(format!("failed creating Camera: {e}")))
765 }
766 }
767
768 const FIELDS: &[&str] = &["width", "height", "extrinsics", "intrinsics"];
769 deserializer.deserialize_struct("Camera", FIELDS, CameraVisitor(std::marker::PhantomData))
770 }
771}
772
773fn _test_camera_is_serialize() {
774 fn implements<T: serde::Serialize>() {}
776 implements::<Camera<f64>>();
777}
778
779fn _test_camera_is_deserialize() {
780 fn implements<'de, T: serde::Deserialize<'de>>() {}
782 implements::<Camera<f64>>();
783}
784
785#[derive(Clone, PartialEq)]
786pub(crate) struct CameraCache<R: RealField> {
787 pub(crate) m: OMatrix<R, U3, U4>,
788 pub(crate) pinv: OMatrix<R, U4, U3>,
789}
790
791const SVD_MAX_ITERATIONS: usize = 1_000_000;
792
793fn my_pinv<R: RealField + Copy>(m: &OMatrix<R, U3, U4>) -> Result<OMatrix<R, U4, U3>> {
794 na::linalg::SVD::try_new(*m, true, true, na::convert(1e-7), SVD_MAX_ITERATIONS)
795 .ok_or(MvgError::SvdFailed)?
796 .pseudo_inverse(na::convert(1.0e-7))
797 .map_err(|e| MvgError::PinvError {
798 error: format!("inverse failed {e}"),
799 })
800}
801
802fn my_pinv_4x4<R: RealField + Copy>(m: &OMatrix<R, U4, U4>) -> Result<OMatrix<R, U4, U4>> {
803 na::linalg::SVD::try_new(*m, true, true, na::convert(1e-7), SVD_MAX_ITERATIONS)
804 .ok_or(MvgError::SvdFailed)?
805 .pseudo_inverse(na::convert(1.0e-7))
806 .map_err(|e| MvgError::PinvError {
807 error: format!("inverse failed {e}"),
808 })
809}
810
811fn build_xform<R: RealField + Copy>(s: R, rot: Matrix3<R>, t: Vector3<R>) -> Matrix4<R> {
812 let mut m1 = Matrix4::zero();
813 for i in 0..3 {
814 for j in 0..3 {
815 m1[(i, j)] = rot[(i, j)];
816 }
817 }
818 let mut m2 = m1 * s;
819 for i in 0..3 {
820 m2[(i, 3)] = t[i];
821 }
822 m2[(3, 3)] = R::one();
823 m2
824}
825
826#[expect(clippy::many_single_char_names)]
827fn pmat2cam_center<R: RealField + Copy>(p: &OMatrix<R, U3, U4>) -> Point3<R> {
828 let x = (*p).remove_column(0).determinant();
829 let y = -(*p).remove_column(1).determinant();
830 let z = (*p).remove_column(2).determinant();
831 let w = -(*p).remove_column(3).determinant();
832 Point3::from(Vector3::new(x / w, y / w, z / w))
833}
834
835fn my_quat_angle<R: RealField + Copy>(quat: &na::UnitQuaternion<R>) -> R {
840 let w = quat.quaternion().scalar().abs();
841
842 if w >= R::one() {
844 R::zero()
845 } else {
846 w.acos() * na::convert(2.0f64)
847 }
848}
849
850fn right_handed_rotation_quat_new<R: RealField + Copy>(
852 orig: &Matrix3<R>,
853) -> Result<UnitQuaternion<R>> {
854 let r1 = *orig;
855 let rotmat = Rotation3::from_matrix_unchecked(r1);
856 let rquat = UnitQuaternion::from_rotation_matrix(&rotmat);
857 {
858 let rotmat2 = rquat.to_rotation_matrix();
862 let rquat2 = UnitQuaternion::from_rotation_matrix(&rotmat2);
863 let delta = rquat.rotation_to(&rquat2);
864 let angle = my_quat_angle(&delta);
865 let epsilon = na::convert(1.0e-7);
866 if angle.abs() > epsilon {
867 return Err(MvgError::InvalidRotationMatrix);
868 }
869 }
870 Ok(rquat)
871}
872
873fn rq<R: RealField + Copy>(A: Matrix3<R>) -> (Matrix3<R>, Matrix3<R>) {
874 let zero: R = Zero::zero();
875 let one: R = One::one();
876
877 let P = Matrix3::<R>::new(zero, zero, one, zero, one, zero, one, zero, zero);
879 let Atilde = P * A;
880
881 let (Qtilde, Rtilde) = {
882 let qrm = na::linalg::QR::new(Atilde.transpose());
883 (qrm.q(), qrm.r())
884 };
885 let Q = P * Qtilde.transpose();
886 let R = P * Rtilde.transpose() * P;
887 (R, Q)
888}
889
890pub fn rq_decomposition<R: RealField + Copy>(
892 orig: Matrix3<R>,
893) -> Result<(UnitQuaternion<R>, Matrix3<R>)> {
894 let (mut intrin, mut q) = rq(orig);
895 let zero: R = Zero::zero();
896 for i in 0..3 {
897 if intrin[(i, i)] < zero {
898 for j in 0..3 {
899 intrin[(j, i)] = -intrin[(j, i)];
900 q[(i, j)] = -q[(i, j)];
901 }
902 }
903 }
904
905 match right_handed_rotation_quat_new(&q) {
906 Ok(rquat) => Ok((rquat, intrin)),
907 Err(error) => {
908 match error {
909 MvgError::InvalidRotationMatrix => {
910 let q = -q;
912 let intrin = -intrin;
913 let rquat = right_handed_rotation_quat_new(&q)?;
914 Ok((rquat, intrin))
915 }
916 e => Err(e),
917 }
918 }
919 }
920}
921
922#[cfg(test)]
923mod tests {
924 use crate::{DistortedPixel, PointWorldFrame};
925 use na::core::dimension::{U3, U4};
926 use na::core::{OMatrix, Vector4};
927 use na::geometry::{Point2, Point3};
928 use nalgebra as na;
929
930 fn is_pmat_same(cam: &crate::Camera<f64>, pmat: &OMatrix<f64, U3, U4>) -> bool {
931 let world_pts = [
932 PointWorldFrame {
933 coords: Point3::new(1.23, 4.56, 7.89),
934 },
935 PointWorldFrame {
936 coords: Point3::new(1.0, 2.0, 3.0),
937 },
938 ];
939
940 let pts1: Vec<DistortedPixel<_>> = world_pts
941 .iter()
942 .map(|world| cam.project_3d_to_distorted_pixel(world))
943 .collect();
944
945 let pts2: Vec<DistortedPixel<_>> = world_pts
946 .iter()
947 .map(|world| {
948 let world_h = Vector4::new(world.coords.x, world.coords.y, world.coords.z, 1.0);
949 let rst = pmat * world_h;
950 DistortedPixel {
951 coords: Point2::new(rst[0] / rst[2], rst[1] / rst[2]),
952 }
953 })
954 .collect();
955
956 let epsilon = 1e-10;
957
958 for (im1, im2) in pts1.iter().zip(pts2) {
959 println!("im1: {im1:?}");
960 println!("im2: {im2:?}");
961 let diff = im1.coords - im2.coords;
962 let dist_squared = diff.dot(&diff);
963 if dist_squared.is_nan() {
964 continue;
965 }
966 println!("dist_squared: {dist_squared:?}");
967 if dist_squared > epsilon {
968 return false;
969 }
970 }
971 true
972 }
973
974 fn is_similar(cam1: &crate::Camera<f64>, cam2: &crate::Camera<f64>) -> bool {
975 let world_pts = [
976 PointWorldFrame {
977 coords: Point3::new(1.23, 4.56, 7.89),
978 },
979 PointWorldFrame {
980 coords: Point3::new(1.0, 2.0, 3.0),
981 },
982 ];
983
984 let pts1: Vec<DistortedPixel<_>> = world_pts
985 .iter()
986 .map(|world| cam1.project_3d_to_distorted_pixel(world))
987 .collect();
988
989 let pts2: Vec<DistortedPixel<_>> = world_pts
990 .iter()
991 .map(|world| cam2.project_3d_to_distorted_pixel(world))
992 .collect();
993
994 let epsilon = 1e-10;
995
996 for (im1, im2) in pts1.iter().zip(pts2) {
997 let diff = im1.coords - im2.coords;
998 let dist_squared = diff.dot(&diff);
999 if dist_squared.is_nan() {
1000 continue;
1001 }
1002 if dist_squared > epsilon {
1003 return false;
1004 }
1005 }
1006 true
1007 }
1008
1009 #[test]
1010 fn test_to_from_pmat() {
1011 for (name, cam1) in crate::tests::get_test_cameras().iter() {
1012 println!("\n\n\ntesting camera {name}");
1013 let pmat = match cam1.as_pmat() {
1014 Some(pmat) => pmat,
1015 None => {
1016 println!("skipping camera {name}: no pmat");
1017 continue;
1018 }
1019 };
1020 assert!(is_pmat_same(cam1, pmat));
1021 let cam2 = crate::Camera::from_pmat(cam1.width(), cam1.height(), pmat).unwrap();
1022 assert!(is_similar(cam1, &cam2));
1023 }
1024 }
1025
1026 #[test]
1027 fn test_flipped_camera() {
1028 for (name, cam1) in crate::tests::get_test_cameras().iter() {
1029 println!("testing camera {name}");
1030 let cam2 = cam1.flip().expect("flip cam");
1031 if !is_similar(cam1, &cam2) {
1032 panic!("results not similar for cam {name}");
1033 }
1034 }
1035 }
1036
1037 #[test]
1038 fn test_rq() {
1039 let a = na::Matrix3::new(1.2, 3.4, 5.6, 7.8, 9.8, 7.6, 5.4, 3.2, 1.0);
1040 let (r, q) = crate::camera::rq(a);
1041 println!("r {r:?}");
1042 println!("q {q:?}");
1043
1044 let a2 = r * q;
1046 println!("a {a:?}");
1047 println!("a2 {a2:?}");
1048
1049 approx::assert_abs_diff_eq!(a, a2, epsilon = 1e-10);
1050
1051 let actual = q * q.transpose();
1053 let expected = na::Matrix3::identity();
1054 approx::assert_abs_diff_eq!(actual, expected, epsilon = 1e-10);
1055
1056 approx::assert_abs_diff_eq!(r[(1, 0)], 0.0, epsilon = 1e-10);
1058 approx::assert_abs_diff_eq!(r[(2, 0)], 0.0, epsilon = 1e-10);
1059 approx::assert_abs_diff_eq!(r[(2, 1)], 0.0, epsilon = 1e-10);
1060 }
1061
1062 #[test]
1063 fn test_rotation_matrices_and_quaternions() {
1064 use na::geometry::{Rotation3, UnitQuaternion};
1065
1066 #[rustfmt::skip]
1067 let r1 = na::Matrix3::from_column_slice(
1068 &[-0.9999999999999998, -0.00000000000000042632564145606005, -0.0000000000000002220446049250313,
1069 0.0000000000000004263256414560601, -1.0, 0.0,
1070 -0.0000000000000002220446049250313, -0.00000000000000000000000000000004930380657631324, -0.9999999999999998]);
1071
1072 let rotmat = Rotation3::from_matrix_unchecked(r1);
1073
1074 let rquat = UnitQuaternion::from_rotation_matrix(&rotmat);
1075
1076 let rotmat2 = rquat.to_rotation_matrix();
1077
1078 let rquat2 = UnitQuaternion::from_rotation_matrix(&rotmat2);
1079
1080 let angle = rquat.angle_to(&rquat2);
1081 let delta = rquat.rotation_to(&rquat2);
1082 let my_angle = crate::camera::my_quat_angle(&delta);
1083
1084 println!("r1 {r1:?}");
1085 println!("rotmat {rotmat:?}");
1086 println!("rquat {rquat:?}");
1087 println!("rotmat2 {rotmat2:?}");
1088 println!("rquat2 {rquat2:?}");
1089 println!("angle: {angle:?}");
1090 println!("delta {delta:?}");
1091 println!("my_angle: {my_angle:?}");
1092
1093 let q = na::Quaternion::new(
1094 -0.000000000000000000000000000000002756166576353432,
1095 0.000000000000000024825341532472726,
1096 -0.00000000000000004766465574234759,
1097 0.5590169943749475,
1098 );
1099 let uq = UnitQuaternion::from_quaternion(q); println!("q: {q:?}");
1101 println!("uq: {uq:?}");
1102 println!("uq.angle(): {:?}", uq.angle());
1103 }
1104
1105 #[test]
1106 fn test_serde() {
1107 let expected = crate::Camera::<f64>::default();
1108 let buf = serde_json::to_string(&expected).unwrap();
1109 let actual: crate::Camera<f64> = serde_json::from_str(&buf).unwrap();
1110 assert!(expected == actual);
1111 }
1112}