1use crate::CartesianTreeError;
2use crate::Pose;
3use crate::lazy_access::LazyRotation;
4use crate::lazy_access::LazyTranslation;
5use crate::rotation::Rotation;
6use crate::tree::Walking;
7use crate::tree::{HasChildren, HasParent, NodeEquality};
8
9use nalgebra::UnitQuaternion;
10use nalgebra::{Isometry3, Translation3, Vector3};
11use std::cell::RefCell;
12use std::ops::Add;
13use std::ops::Mul;
14use std::ops::Sub;
15use std::rc::{Rc, Weak};
16
17use serde::{Deserialize, Serialize};
18use serde_json;
19use uuid::Uuid;
20
21#[derive(Clone, Debug)]
28pub struct Frame {
29 pub(crate) data: Rc<RefCell<FrameData>>,
30}
31
32#[derive(Debug)]
33pub(crate) struct FrameData {
34 pub(crate) name: String,
36 parent: Option<Weak<RefCell<FrameData>>>,
38 transform_to_parent: Isometry3<f64>,
40 children: Vec<Frame>,
42}
43
44#[derive(Serialize, Deserialize, Debug, Clone)]
45struct SerialFrame {
46 name: String,
47 position: Vector3<f64>,
48 orientation: UnitQuaternion<f64>,
49 children: Vec<SerialFrame>,
50}
51
52impl Frame {
53 pub fn new_origin(name: impl Into<String>) -> Self {
66 Self {
67 data: Rc::new(RefCell::new(FrameData {
68 name: name.into(),
69 parent: None,
70 children: Vec::new(),
71 transform_to_parent: Isometry3::identity(),
72 })),
73 }
74 }
75
76 pub(crate) fn borrow(&self) -> std::cell::Ref<'_, FrameData> {
77 self.data.borrow()
78 }
79
80 fn borrow_mut(&self) -> std::cell::RefMut<'_, FrameData> {
81 self.data.borrow_mut()
82 }
83
84 pub(crate) fn downgrade(&self) -> Weak<RefCell<FrameData>> {
85 Rc::downgrade(&self.data)
86 }
87
88 pub(crate) fn walk_up_and_transform(
89 &self,
90 target: &Self,
91 ) -> Result<Isometry3<f64>, CartesianTreeError> {
92 let mut transform = Isometry3::identity();
93 let mut current = self.clone();
94
95 while !current.is_same(target) {
96 let transform_to_its_parent = {
97 let current_data = current.borrow();
99
100 if current_data.parent.is_none() {
102 return Err(CartesianTreeError::IsNoAncestor(target.name(), self.name()));
103 }
104 current_data.transform_to_parent
105 };
106
107 transform = transform_to_its_parent * transform;
108
109 let parent_frame_opt = current.parent();
110 current = parent_frame_opt
111 .ok_or_else(|| CartesianTreeError::IsNoAncestor(target.name(), self.name()))?;
112 }
113
114 Ok(transform)
115 }
116
117 #[must_use]
119 pub fn name(&self) -> String {
120 self.borrow().name.clone()
121 }
122
123 pub fn transformation(&self) -> Result<Isometry3<f64>, CartesianTreeError> {
132 if self.parent().is_none() {
133 return Err(CartesianTreeError::RootHasNoParent(self.name()));
134 }
135 Ok(self.borrow().transform_to_parent)
136 }
137
138 #[must_use]
143 pub fn position(&self) -> Vector3<f64> {
144 self.borrow().transform_to_parent.translation.vector
145 }
146
147 #[must_use]
152 pub fn orientation(&self) -> Rotation {
153 self.borrow().transform_to_parent.rotation.into()
154 }
155
156 pub fn set(
185 &self,
186 position: Vector3<f64>,
187 orientation: impl Into<Rotation>,
188 ) -> Result<(), CartesianTreeError> {
189 if self.parent().is_none() {
190 return Err(CartesianTreeError::CannotUpdateRootTransform(self.name()));
191 }
192 self.borrow_mut().transform_to_parent = Isometry3::from_parts(
193 Translation3::from(position),
194 orientation.into().as_quaternion(),
195 );
196 Ok(())
197 }
198
199 pub fn apply_in_parent_frame(
228 &self,
229 isometry: &Isometry3<f64>,
230 ) -> Result<(), CartesianTreeError> {
231 if self.parent().is_none() {
232 return Err(CartesianTreeError::CannotUpdateRootTransform(self.name()));
233 }
234 let mut borrow = self.borrow_mut();
235 borrow.transform_to_parent = isometry * borrow.transform_to_parent;
236 Ok(())
237 }
238
239 pub fn apply_in_local_frame(
268 &self,
269 isometry: &Isometry3<f64>,
270 ) -> Result<(), CartesianTreeError> {
271 if self.parent().is_none() {
272 return Err(CartesianTreeError::CannotUpdateRootTransform(self.name()));
273 }
274 let mut borrow = self.borrow_mut();
275 borrow.transform_to_parent *= isometry;
276 Ok(())
277 }
278
279 pub fn add_child(
308 &self,
309 name: impl Into<String>,
310 position: Vector3<f64>,
311 orientation: impl Into<Rotation>,
312 ) -> Result<Self, CartesianTreeError> {
313 let child_name = name.into();
314 {
315 let frame = self.borrow();
316 if frame
317 .children
318 .iter()
319 .any(|child| child.borrow().name == child_name)
320 {
321 return Err(CartesianTreeError::ChildNameConflict(
322 child_name,
323 self.name(),
324 ));
325 }
326 }
327 let transform = Isometry3::from_parts(
328 Translation3::from(position),
329 orientation.into().as_quaternion(),
330 );
331
332 let child = Self {
333 data: Rc::new(RefCell::new(FrameData {
334 name: child_name,
335 parent: Some(Rc::downgrade(&self.data)),
336 children: Vec::new(),
337 transform_to_parent: transform,
338 })),
339 };
340
341 self.borrow_mut().children.push(child.clone());
342 Ok(child)
343 }
344
345 pub fn calibrate_child(
378 &self,
379 name: impl Into<String>,
380 desired_position: Vector3<f64>,
381 desired_orientation: impl Into<Rotation>,
382 reference_pose: &Pose,
383 ) -> Result<Self, CartesianTreeError> {
384 let reference_frame = reference_pose.frame().ok_or_else(|| {
385 CartesianTreeError::FrameDropped("Reference pose frame has been dropped".to_string())
386 })?;
387
388 let ancestor = self.lca_with(&reference_frame).ok_or_else(|| {
389 CartesianTreeError::NoCommonAncestor(self.name(), reference_frame.name())
390 })?;
391
392 let t_reference_to_ancestor = reference_frame.walk_up_and_transform(&ancestor)?;
393 let t_pose_to_reference = reference_pose.transformation();
394 let t_pose_to_ancestor = t_reference_to_ancestor * t_pose_to_reference;
395
396 let t_parent_to_ancestor = self.walk_up_and_transform(&ancestor)?;
397 let t_ancestor_to_parent = t_parent_to_ancestor.inverse();
398
399 let desired_pose = Isometry3::from_parts(
400 Translation3::from(desired_position),
401 desired_orientation.into().as_quaternion(),
402 );
403
404 let t_calibrated_to_parent =
405 t_pose_to_ancestor * desired_pose.inverse() * t_ancestor_to_parent;
406
407 self.add_child(
408 name,
409 t_calibrated_to_parent.translation.vector,
410 t_calibrated_to_parent.rotation,
411 )
412 }
413
414 pub fn add_pose(&self, position: Vector3<f64>, orientation: impl Into<Rotation>) -> Pose {
432 Pose::new(self.downgrade(), position, orientation)
433 }
434
435 pub fn to_json(&self) -> Result<String, CartesianTreeError> {
447 let serial = self.to_serial();
448 Ok(serde_json::to_string_pretty(&serial)?)
449 }
450
451 fn to_serial(&self) -> SerialFrame {
455 let (position, orientation) = if self.parent().is_some() {
456 let iso = self
457 .transformation()
458 .unwrap_or_else(|_| Isometry3::identity());
459 (iso.translation.vector, iso.rotation)
460 } else {
461 (Vector3::zeros(), UnitQuaternion::identity())
462 };
463
464 SerialFrame {
465 name: self.name(),
466 position,
467 orientation,
468 children: self.children().into_iter().map(|c| c.to_serial()).collect(),
469 }
470 }
471
472 pub fn apply_config(&self, json: &str) -> Result<(), CartesianTreeError> {
490 let serial: SerialFrame = serde_json::from_str(json)?;
491 self.apply_serial(&serial)
492 }
493
494 fn apply_serial(&self, serial: &SerialFrame) -> Result<(), CartesianTreeError> {
495 if self.name() != serial.name {
496 return Err(CartesianTreeError::Mismatch(format!(
497 "Frame names do not match: {} vs {}",
498 self.name(),
499 serial.name
500 )));
501 }
502
503 if self.parent().is_some() {
505 self.set(serial.position, serial.orientation)?;
506 }
507
508 for potential_child in &serial.children {
509 if let Some(child) = self
510 .children()
511 .into_iter()
512 .find(|c| c.name() == potential_child.name)
513 {
514 child.apply_serial(potential_child)?;
515 }
516 }
517
518 Ok(())
519 }
520}
521
522impl Add<LazyTranslation> for &Frame {
523 type Output = Frame;
524
525 fn add(self, rhs: LazyTranslation) -> Self::Output {
526 let auto_name = Uuid::new_v4().to_string();
527 let current_position = self.borrow().transform_to_parent.translation.vector;
528 let current_orientation = self.borrow().transform_to_parent.rotation;
529 let child = self
530 .add_child(auto_name, current_position, current_orientation)
531 .unwrap();
532 child.apply_in_parent_frame(&rhs.inner).unwrap();
533 child }
535}
536
537impl Sub<LazyTranslation> for &Frame {
538 type Output = Frame;
539
540 fn sub(self, rhs: LazyTranslation) -> Self::Output {
541 let auto_name = Uuid::new_v4().to_string();
542 let current_position = self.borrow().transform_to_parent.translation.vector;
543 let current_orientation = self.borrow().transform_to_parent.rotation;
544 let child = self
545 .add_child(auto_name, current_position, current_orientation)
546 .unwrap();
547 child.apply_in_parent_frame(&rhs.inner.inverse()).unwrap();
548 child }
550}
551
552impl Mul<LazyRotation> for &Frame {
553 type Output = Frame;
554
555 fn mul(self, rhs: LazyRotation) -> Self::Output {
556 let auto_name = Uuid::new_v4().to_string();
557 let current_position = self.borrow().transform_to_parent.translation.vector;
558 let current_orientation = self.borrow().transform_to_parent.rotation;
559 let child = self
560 .add_child(auto_name, current_position, current_orientation)
561 .unwrap();
562 child.apply_in_local_frame(&rhs.inner).unwrap();
563 child }
565}
566
567impl HasParent for Frame {
568 type Node = Self;
569
570 fn parent(&self) -> Option<Self::Node> {
571 self.borrow()
572 .parent
573 .clone()
574 .and_then(|data_weak| data_weak.upgrade().map(|data_rc| Self { data: data_rc }))
575 }
576}
577
578impl NodeEquality for Frame {
579 fn is_same(&self, other: &Self) -> bool {
580 Rc::ptr_eq(&self.data, &other.data)
581 }
582}
583
584impl HasChildren for Frame {
585 type Node = Self;
586 fn children(&self) -> Vec<Self> {
587 self.borrow().children.clone()
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 use crate::lazy_access::{rz, y, z};
594
595 use super::*;
596 use approx::assert_relative_eq;
597 use nalgebra::{UnitQuaternion, Vector3};
598
599 #[test]
600 fn create_origin_frame() {
601 let root = Frame::new_origin("world");
602 let root_borrow = root.borrow();
603 assert_eq!(root_borrow.name, "world");
604 assert!(root_borrow.parent.is_none());
605 assert_eq!(root_borrow.children.len(), 0);
606 }
607
608 #[test]
609 fn add_child_frame_with_quaternion() {
610 let root = Frame::new_origin("world");
611 let child = root
612 .add_child(
613 "dummy",
614 Vector3::new(1.0, 0.0, 0.0),
615 UnitQuaternion::identity(),
616 )
617 .unwrap();
618
619 let root_borrow = root.borrow();
620 assert_eq!(root_borrow.children.len(), 1);
621
622 let child_borrow = child.borrow();
623 assert_eq!(child_borrow.name, "dummy");
624 assert!(child_borrow.parent.is_some());
625
626 let parent_name = child_borrow
627 .parent
628 .as_ref()
629 .unwrap()
630 .upgrade()
631 .unwrap()
632 .borrow()
633 .name
634 .clone();
635 assert_eq!(parent_name, "world");
636 }
637
638 #[test]
639 fn add_child_frame_with_rpy() {
640 let root = Frame::new_origin("world");
641 let child = root
642 .add_child(
643 "dummy",
644 Vector3::new(0.0, 1.0, 0.0),
645 Rotation::from_rpy(0.0, 0.0, std::f64::consts::FRAC_PI_2),
646 )
647 .unwrap();
648
649 let child_borrow = child.borrow();
650 assert_eq!(child_borrow.name, "dummy");
651
652 let rotation = child_borrow.transform_to_parent.rotation;
653 let expected = UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2);
654 assert!((rotation.angle() - expected.angle()).abs() < 1e-10);
655 }
656
657 #[test]
658 fn test_child_frame_transform_to_parent() {
659 let root = Frame::new_origin("world");
660 let child = root
661 .add_child(
662 "dummy",
663 Vector3::new(0.0, 0.0, 1.0),
664 UnitQuaternion::identity(),
665 )
666 .unwrap();
667
668 let transform = child.transformation().unwrap();
669 assert_eq!(transform.translation.vector, Vector3::new(0.0, 0.0, 1.0));
670 assert_eq!(transform.rotation, UnitQuaternion::identity());
671
672 assert_eq!(child.position(), Vector3::new(0.0, 0.0, 1.0));
673 assert_eq!(
674 child.orientation().as_quaternion(),
675 UnitQuaternion::identity()
676 );
677 }
678
679 #[test]
680 fn multiple_child_frames() {
681 let root = Frame::new_origin("world");
682
683 let a = root
684 .add_child("a", Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity())
685 .unwrap();
686 let b = root
687 .add_child("b", Vector3::new(0.0, 1.0, 0.0), UnitQuaternion::identity())
688 .unwrap();
689
690 let root_borrow = root.borrow();
691 assert_eq!(root_borrow.children.len(), 2);
692
693 let a_borrow = a.borrow();
694 let b_borrow = b.borrow();
695
696 assert_eq!(
697 a_borrow
698 .parent
699 .as_ref()
700 .unwrap()
701 .upgrade()
702 .unwrap()
703 .borrow()
704 .name,
705 "world"
706 );
707 assert_eq!(
708 b_borrow
709 .parent
710 .as_ref()
711 .unwrap()
712 .upgrade()
713 .unwrap()
714 .borrow()
715 .name,
716 "world"
717 );
718 }
719
720 #[test]
721 fn reject_duplicate_child_name() {
722 let root = Frame::new_origin("world");
723
724 let _ = root
725 .add_child(
726 "duplicate",
727 Vector3::new(1.0, 0.0, 0.0),
728 UnitQuaternion::identity(),
729 )
730 .unwrap();
731
732 let result = root.add_child(
733 "duplicate",
734 Vector3::new(2.0, 0.0, 0.0),
735 UnitQuaternion::identity(),
736 );
737 assert!(result.is_err());
738 }
739
740 #[test]
741 #[should_panic(expected = "already borrowed")]
742 fn test_borrow_conflict() {
743 let frame = Frame::new_origin("root");
744 let _borrow = frame.borrow(); frame.borrow_mut(); }
747
748 #[test]
749 fn test_add_pose_to_frame() {
750 let frame = Frame::new_origin("dummy");
751 let pose = frame.add_pose(Vector3::new(1.0, 2.0, 3.0), UnitQuaternion::identity());
752
753 assert_eq!(pose.frame().unwrap().name(), "dummy");
754 }
755
756 #[test]
757 fn test_set_transform() {
758 let root = Frame::new_origin("root");
759 let child = root
760 .add_child(
761 "dummy",
762 Vector3::new(0.0, 0.0, 1.0),
763 UnitQuaternion::identity(),
764 )
765 .unwrap();
766 child
767 .set(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity())
768 .unwrap();
769 assert_eq!(
770 child.transformation().unwrap().translation.vector,
771 Vector3::new(1.0, 0.0, 0.0)
772 );
773
774 assert!(
776 root.set(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity())
777 .is_err()
778 );
779 }
780
781 #[test]
782 fn test_apply_in_parent_frame() {
783 let root = Frame::new_origin("root");
784 let child = root
785 .add_child(
786 "dummy",
787 Vector3::new(1.0, 0.0, 1.0),
788 UnitQuaternion::identity(),
789 )
790 .unwrap();
791 child
792 .apply_in_parent_frame(&Isometry3::from_parts(
793 Translation3::identity(),
794 UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2),
795 ))
796 .unwrap();
797
798 assert_relative_eq!(
799 child.transformation().unwrap().translation.vector,
800 Vector3::new(0.0, 1.0, 1.0),
801 epsilon = 1e-10
802 );
803
804 child
805 .apply_in_parent_frame(&Isometry3::from_parts(
806 Translation3::new(1.0, 0.0, 1.0),
807 UnitQuaternion::identity(),
808 ))
809 .unwrap();
810 assert_relative_eq!(
811 child.transformation().unwrap().translation.vector,
812 Vector3::new(1.0, 1.0, 2.0),
813 epsilon = 1e-10
814 );
815
816 assert!(
818 root.set(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity())
819 .is_err()
820 );
821 }
822
823 #[test]
824 fn test_apply_in_local_frame() {
825 let root = Frame::new_origin("root");
826 let child = root
827 .add_child(
828 "dummy",
829 Vector3::zeros(),
830 UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2),
831 )
832 .unwrap();
833
834 child
835 .apply_in_local_frame(&Isometry3::from_parts(
836 Translation3::new(1.0, 0.0, 0.0),
837 UnitQuaternion::identity(),
838 ))
839 .unwrap();
840
841 assert_relative_eq!(
842 child.transformation().unwrap().translation.vector,
843 Vector3::new(0.0, 1.0, 0.0),
844 epsilon = 1e-10
845 );
846
847 child
848 .apply_in_local_frame(&Isometry3::from_parts(
849 Translation3::identity(),
850 UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2),
851 ))
852 .unwrap();
853 assert_relative_eq!(
854 child.transformation().unwrap().translation.vector,
855 Vector3::new(0.0, 1.0, 0.0),
856 epsilon = 1e-10
857 );
858
859 let (roll, pitch, yaw) = child.transformation().unwrap().rotation.euler_angles();
860 assert_relative_eq!(roll, 0.0, epsilon = 1e-10);
861 assert_relative_eq!(pitch, 0.0, epsilon = 1e-10);
862 assert_relative_eq!(yaw, std::f64::consts::PI, epsilon = 1e-10);
863
864 assert!(
866 root.set(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity())
867 .is_err()
868 );
869 }
870
871 #[test]
872 fn test_pose_apply_in_parent_frame() {
873 let root = Frame::new_origin("root");
874 let mut pose = root.add_pose(Vector3::new(1.0, 0.0, 1.0), UnitQuaternion::identity());
875
876 pose.apply_in_parent_frame(&Isometry3::from_parts(
877 Translation3::identity(),
878 UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2),
879 ));
880
881 assert_relative_eq!(
882 pose.transformation().translation.vector,
883 Vector3::new(0.0, 1.0, 1.0),
884 epsilon = 1e-10
885 );
886
887 pose.apply_in_parent_frame(&Isometry3::from_parts(
888 Translation3::new(1.0, 0.0, 1.0),
889 UnitQuaternion::identity(),
890 ));
891 assert_relative_eq!(
892 pose.transformation().translation.vector,
893 Vector3::new(1.0, 1.0, 2.0),
894 epsilon = 1e-10
895 );
896 }
897
898 #[test]
899 fn test_pose_apply_in_local_frame() {
900 let root = Frame::new_origin("root");
901 let mut pose = root.add_pose(
902 Vector3::zeros(),
903 UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2),
904 );
905
906 pose.apply_in_local_frame(&Isometry3::from_parts(
907 Translation3::new(1.0, 0.0, 0.0),
908 UnitQuaternion::identity(),
909 ));
910
911 assert_relative_eq!(
912 pose.transformation().translation.vector,
913 Vector3::new(0.0, 1.0, 0.0),
914 epsilon = 1e-10
915 );
916
917 pose.apply_in_local_frame(&Isometry3::from_parts(
918 Translation3::identity(),
919 UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2),
920 ));
921 assert_relative_eq!(
922 pose.transformation().translation.vector,
923 Vector3::new(0.0, 1.0, 0.0),
924 epsilon = 1e-10
925 );
926
927 let (roll, pitch, yaw) = pose.transformation().rotation.euler_angles();
928 assert_relative_eq!(roll, 0.0, epsilon = 1e-10);
929 assert_relative_eq!(pitch, 0.0, epsilon = 1e-10);
930 assert_relative_eq!(yaw, std::f64::consts::PI, epsilon = 1e-10);
931
932 assert!(
934 root.set(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity())
935 .is_err()
936 );
937 }
938
939 #[test]
940 fn test_pose_transform_to_parent() {
941 let root = Frame::new_origin("root");
942 let pose = root.add_pose(Vector3::new(1.0, 2.0, 3.0), UnitQuaternion::identity());
943
944 let transformation = pose.transformation();
945 assert_eq!(
946 transformation.translation.vector,
947 Vector3::new(1.0, 2.0, 3.0)
948 );
949 assert_eq!(transformation.rotation, UnitQuaternion::identity());
950
951 assert_eq!(pose.position(), Vector3::new(1.0, 2.0, 3.0));
952 assert_eq!(
953 pose.orientation().as_quaternion(),
954 UnitQuaternion::identity()
955 );
956 }
957
958 #[test]
959 fn test_pose_transformation_between_frames() {
960 let root = Frame::new_origin("root");
961
962 let f1 = root
963 .add_child(
964 "f1",
965 Vector3::new(1.0, 0.0, 0.0),
966 UnitQuaternion::identity(),
967 )
968 .unwrap();
969
970 let f2 = f1
971 .add_child(
972 "f2",
973 Vector3::new(0.0, 2.0, 0.0),
974 UnitQuaternion::identity(),
975 )
976 .unwrap();
977
978 let pose_in_f2 = f2.add_pose(Vector3::new(1.0, 1.0, 0.0), UnitQuaternion::identity());
979
980 let pose_in_root = pose_in_f2.in_frame(&root).unwrap();
981 let pos = pose_in_root.transformation().translation.vector;
982
983 assert!((pos - Vector3::new(2.0, 3.0, 0.0)).norm() < 1e-6);
985 }
986
987 #[test]
988 fn test_calibrate_child() {
989 let root = Frame::new_origin("root");
990
991 let reference_pose = root.add_pose(
992 Vector3::new(1.0, 2.0, 3.0),
993 UnitQuaternion::from_euler_angles(0.0, 0.0, std::f64::consts::FRAC_PI_2),
994 );
995
996 let calibrated_frame = root
998 .calibrate_child(
999 "calibrated",
1000 Vector3::zeros(),
1001 UnitQuaternion::identity(),
1002 &reference_pose,
1003 )
1004 .unwrap();
1005
1006 let pose_in_calibrated = reference_pose.in_frame(&calibrated_frame).unwrap();
1007 let transformation = pose_in_calibrated.transformation();
1008
1009 assert!((transformation.translation.vector - Vector3::zeros()).norm() < 1e-6);
1010 assert!((transformation.rotation.angle() - 0.0).abs() < 1e-6);
1011
1012 let calibrated_transformation = calibrated_frame.transformation().unwrap();
1014 assert!(
1015 (calibrated_transformation.translation.vector - Vector3::new(1.0, 2.0, 3.0)).norm()
1016 < 1e-6
1017 );
1018 assert!(
1019 (calibrated_transformation.rotation.angle() - std::f64::consts::FRAC_PI_2).abs() < 1e-6
1020 );
1021 }
1022
1023 #[test]
1024 fn test_to_json_and_apply_config() {
1025 let root = Frame::new_origin("root");
1026 let _ = root
1027 .add_child(
1028 "child",
1029 Vector3::new(1.0, 2.0, 3.0),
1030 UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3),
1031 )
1032 .unwrap();
1033
1034 let json = root.to_json().unwrap();
1035 assert!(json.contains(r#""name": "root""#));
1037 assert!(json.contains(r#""name": "child""#));
1038
1039 let default_root = Frame::new_origin("root");
1041 default_root
1042 .add_child(
1043 "child",
1044 Vector3::new(0.0, 0.0, 0.0),
1045 UnitQuaternion::identity(),
1046 )
1047 .unwrap();
1048
1049 default_root.apply_config(&json).unwrap();
1051
1052 let updated_child = default_root
1054 .children()
1055 .into_iter()
1056 .find(|c| c.name() == "child")
1057 .unwrap();
1058 let iso = updated_child.transformation().unwrap();
1059 assert_eq!(iso.translation.vector, Vector3::new(1.0, 2.0, 3.0));
1060 let (r, p, y) = iso.rotation.euler_angles();
1061 assert!((r - 0.1).abs() < 1e-6);
1062 assert!((p - 0.2).abs() < 1e-6);
1063 assert!((y - 0.3).abs() < 1e-6);
1064
1065 let partial_json = r#"
1067 {
1068 "name": "root",
1069 "position": [0.0, 0.0, 0.0],
1070 "orientation": [0.0, 0.0, 0.0, 1.0],
1071 "children": [
1072 {
1073 "name": "child",
1074 "position": [4.0, 5.0, 6.0],
1075 "orientation": [0.0, 0.0, 0.0, 1.0],
1076 "children": []
1077 },
1078 {
1079 "name": "extra",
1080 "position": [0.0, 0.0, 0.0],
1081 "orientation": [0.0, 0.0, 0.0, 1.0],
1082 "children": []
1083 }
1084 ]
1085 }
1086 "#;
1087 default_root.apply_config(partial_json).unwrap();
1088 let updated_child = default_root
1089 .children()
1090 .into_iter()
1091 .find(|c| c.name() == "child")
1092 .unwrap();
1093 assert_eq!(
1094 updated_child.transformation().unwrap().translation.vector,
1095 Vector3::new(4.0, 5.0, 6.0)
1096 );
1097
1098 let mismatch_json = r#"
1100 {
1101 "name": "wrong_root",
1102 "position": [0.0, 0.0, 0.0],
1103 "orientation": [0.0, 0.0, 0.0, 1.0],
1104 "children": []
1105 }
1106 "#;
1107 assert!(default_root.apply_config(mismatch_json).is_err());
1108 }
1109
1110 #[test]
1111 fn test_lazy_translation_frame() {
1112 use nalgebra::UnitQuaternion;
1113
1114 let root = Frame::new_origin("root");
1115 let child = root
1116 .add_child(
1117 "child",
1118 Vector3::new(0.0, 0.0, 0.0),
1119 UnitQuaternion::identity(),
1120 )
1121 .unwrap();
1122
1123 let result = &child + z(5.0);
1124 assert_relative_eq!(
1125 result.transformation().unwrap().translation.vector,
1126 Vector3::new(0.0, 0.0, 5.0),
1127 epsilon = 1e-10
1128 );
1129 assert_relative_eq!(
1130 child.transformation().unwrap().translation.vector,
1131 Vector3::new(0.0, 0.0, 0.0),
1132 epsilon = 1e-10
1133 );
1134
1135 let result = &result - y(3.0);
1136 assert_relative_eq!(
1137 result.transformation().unwrap().translation.vector,
1138 Vector3::new(0.0, -3.0, 5.0),
1139 epsilon = 1e-10
1140 );
1141
1142 let (roll, pitch, yaw) = result.transformation().unwrap().rotation.euler_angles();
1143 assert_relative_eq!(
1144 Vector3::new(roll, pitch, yaw),
1145 Vector3::new(0.0, 0.0, 0.0),
1146 epsilon = 1e-10
1147 );
1148 }
1149
1150 #[test]
1151 fn test_lazy_rotation_frame() {
1152 use nalgebra::UnitQuaternion;
1153 let root = Frame::new_origin("root");
1154 let child = root
1155 .add_child(
1156 "child",
1157 Vector3::new(0.0, 0.0, 0.0),
1158 UnitQuaternion::identity(),
1159 )
1160 .unwrap();
1161 let result = &child * rz(std::f64::consts::FRAC_PI_4);
1162
1163 let (roll, pitch, yaw) = result.transformation().unwrap().rotation.euler_angles();
1164 assert_relative_eq!(
1165 Vector3::new(roll, pitch, yaw),
1166 Vector3::new(0.0, 0.0, std::f64::consts::FRAC_PI_4),
1167 epsilon = 1e-10
1168 );
1169 assert_relative_eq!(
1170 result.transformation().unwrap().translation.vector,
1171 Vector3::new(0.0, 0.0, 0.0),
1172 epsilon = 1e-10
1173 );
1174 let (roll, pitch, yaw) = child.transformation().unwrap().rotation.euler_angles();
1175 assert_relative_eq!(
1176 Vector3::new(roll, pitch, yaw),
1177 Vector3::new(0.0, 0.0, 0.0),
1178 epsilon = 1e-10
1179 );
1180 }
1181
1182 #[test]
1183 fn test_lazy_translation_pose() {
1184 use nalgebra::UnitQuaternion;
1185
1186 let root = Frame::new_origin("root");
1187 let pose = root.add_pose(Vector3::new(0.0, 0.0, 0.0), UnitQuaternion::identity());
1188
1189 let result = &pose + z(5.0);
1190 assert_relative_eq!(
1191 result.transformation().translation.vector,
1192 Vector3::new(0.0, 0.0, 5.0),
1193 epsilon = 1e-10
1194 );
1195 assert_relative_eq!(
1196 pose.transformation().translation.vector,
1197 Vector3::new(0.0, 0.0, 0.0),
1198 epsilon = 1e-10
1199 );
1200
1201 let result = &result - y(3.0);
1202 assert_relative_eq!(
1203 result.transformation().translation.vector,
1204 Vector3::new(0.0, -3.0, 5.0),
1205 epsilon = 1e-10
1206 );
1207
1208 let (roll, pitch, yaw) = result.transformation().rotation.euler_angles();
1209 assert_relative_eq!(
1210 Vector3::new(roll, pitch, yaw),
1211 Vector3::new(0.0, 0.0, 0.0),
1212 epsilon = 1e-10
1213 );
1214 }
1215
1216 #[test]
1217 fn test_lazy_rotation_pose() {
1218 use nalgebra::UnitQuaternion;
1219 let root = Frame::new_origin("root");
1220 let pose = root.add_pose(Vector3::new(0.0, 0.0, 0.0), UnitQuaternion::identity());
1221 let result = &pose * rz(std::f64::consts::FRAC_PI_4);
1222
1223 let (roll, pitch, yaw) = result.transformation().rotation.euler_angles();
1224 assert_relative_eq!(
1225 Vector3::new(roll, pitch, yaw),
1226 Vector3::new(0.0, 0.0, std::f64::consts::FRAC_PI_4),
1227 epsilon = 1e-10
1228 );
1229 assert_relative_eq!(
1230 result.transformation().translation.vector,
1231 Vector3::new(0.0, 0.0, 0.0),
1232 epsilon = 1e-10
1233 );
1234 let (roll, pitch, yaw) = pose.transformation().rotation.euler_angles();
1235 assert_relative_eq!(
1236 Vector3::new(roll, pitch, yaw),
1237 Vector3::new(0.0, 0.0, 0.0),
1238 epsilon = 1e-10
1239 );
1240 }
1241}