cartesian_tree/pose.rs
1use crate::CartesianTreeError;
2use crate::frame::{Frame, FrameData};
3use crate::orientation::IntoOrientation;
4use crate::tree::Walking;
5use nalgebra::{Isometry3, Translation3, Vector3};
6use std::cell::RefCell;
7use std::rc::Weak;
8
9/// Use [`Frame::add_pose`] to create a new pose.
10#[derive(Clone, Debug)]
11pub struct Pose {
12 /// Reference to the parent frame.
13 parent: Weak<RefCell<FrameData>>,
14 /// Transformation from this frame to its parent frame.
15 transform_to_parent: Isometry3<f64>,
16}
17
18impl Pose {
19 /// Creates a new pose relative to a frame.
20 ///
21 /// This function is intended for internal use. To create a pose associated with a frame,
22 /// use [`Frame::add_pose`], which handles the association safely.
23 pub(crate) fn new<O>(
24 frame: Weak<RefCell<FrameData>>,
25 position: Vector3<f64>,
26 orientation: O,
27 ) -> Self
28 where
29 O: IntoOrientation,
30 {
31 Self {
32 parent: frame,
33 transform_to_parent: Isometry3::from_parts(
34 Translation3::from(position),
35 orientation.into_orientation(),
36 ),
37 }
38 }
39
40 /// Returns the parent frame of this pose.
41 ///
42 /// # Returns
43 /// `Some(Frame)` if the parent frame is still valid, or `None` if the frame
44 /// has been dropped or no longer exists.
45 ///
46 /// # Example
47 /// ```
48 /// use cartesian_tree::Frame;
49 /// use nalgebra::{Vector3, UnitQuaternion};
50 ///
51 /// let frame = Frame::new_origin("base");
52 /// let pose = frame.add_pose(Vector3::new(0.0, 0.0, 0.0), UnitQuaternion::identity());
53 /// assert_eq!(pose.frame().unwrap().name(), "base");
54 /// ```
55 #[must_use]
56 pub fn frame(&self) -> Option<Frame> {
57 self.parent.upgrade().map(|data| Frame { data })
58 }
59
60 /// Returns the transformation from this pose to its parent frame.
61 ///
62 /// # Returns
63 /// The transformation of the pose in its parent frame.
64 #[must_use]
65 pub const fn transformation(&self) -> Isometry3<f64> {
66 self.transform_to_parent
67 }
68
69 /// Updates the pose's transformation relative to its parent.
70 ///
71 /// # Arguments
72 /// - `position`: A 3D vector representing the new translational offset from the parent.
73 /// - `orientation`: An orientation convertible into a unit quaternion for new orientational offset from the parent.
74 ///
75 /// # Example
76 /// ```
77 /// use cartesian_tree::Frame;
78 /// use nalgebra::{Vector3, UnitQuaternion};
79 ///
80 /// let root = Frame::new_origin("root");
81 /// let mut pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
82 /// pose.update(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity());
83 /// ```
84 pub fn update<O>(&mut self, position: Vector3<f64>, orientation: O)
85 where
86 O: IntoOrientation,
87 {
88 self.transform_to_parent =
89 Isometry3::from_parts(Translation3::from(position), orientation.into_orientation());
90 }
91
92 /// Transforms this pose into the coordinate system of the given target frame.
93 ///
94 /// # Arguments
95 /// * `target` - The frame to express this pose in.
96 ///
97 /// # Returns
98 /// A new `Pose`, expressed in the `target` frame.
99 ///
100 /// # Errors
101 /// Returns a [`CartesianTreeError`] if:
102 /// - The frame hierarchy cannot be resolved (e.g., due to dropped frames).
103 /// - There is no common ancestor between `self` and `target`.
104 ///
105 /// # Example
106 /// ```
107 /// use cartesian_tree::Frame;
108 /// use nalgebra::{Vector3, UnitQuaternion};
109 ///
110 /// let root = Frame::new_origin("root");
111 /// let pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
112 /// let new_frame = root.add_child("child", Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity()).unwrap();
113 /// let pose_in_new_frame = pose.in_frame(&new_frame);
114 /// ```
115 pub fn in_frame(&self, target: &Frame) -> Result<Self, CartesianTreeError> {
116 let source_data = self
117 .parent
118 .upgrade()
119 .ok_or(CartesianTreeError::WeakUpgradeFailed())?;
120 let source = Frame { data: source_data };
121 let ancestor = source
122 .lca_with(target)
123 .ok_or_else(|| CartesianTreeError::NoCommonAncestor(source.name(), target.name()))?;
124
125 // Transformation from source frame up to ancestor
126 let tf_up = source.walk_up_and_transform(&ancestor)? * self.transform_to_parent;
127
128 // Transformation from target frame up to ancestor (to be inverted)
129 let tf_down = target.walk_up_and_transform(&ancestor)?;
130
131 Ok(Self {
132 parent: target.downgrade(),
133 transform_to_parent: tf_down.inverse() * tf_up,
134 })
135 }
136}