cartesian_tree/
pose.rs

1use crate::CartesianTreeError;
2use crate::frame::{Frame, FrameData};
3use crate::lazy_access::{LazyRotation, LazyTranslation};
4use crate::rotation::Rotation;
5use crate::tree::Walking;
6use nalgebra::{Isometry3, Translation3, Vector3};
7use std::cell::RefCell;
8use std::ops::{Add, Mul, Sub};
9use std::rc::Weak;
10
11/// Use [`Frame::add_pose`] to create a new pose.
12#[derive(Clone, Debug)]
13pub struct Pose {
14    /// Reference to the parent frame.
15    parent: Weak<RefCell<FrameData>>,
16    /// Transformation from this frame to its parent frame.
17    transform_to_parent: Isometry3<f64>,
18}
19
20impl Pose {
21    /// Creates a new pose relative to a frame.
22    ///
23    /// This function is intended for internal use. To create a pose associated with a frame,
24    /// use [`Frame::add_pose`], which handles the association safely.
25    pub(crate) fn new(
26        frame: Weak<RefCell<FrameData>>,
27        position: Vector3<f64>,
28        orientation: impl Into<Rotation>,
29    ) -> Self {
30        Self {
31            parent: frame,
32            transform_to_parent: Isometry3::from_parts(
33                Translation3::from(position),
34                orientation.into().as_quaternion(),
35            ),
36        }
37    }
38
39    /// Returns the parent frame of this pose.
40    ///
41    /// # Returns
42    /// `Some(Frame)` if the parent frame is still valid, or `None` if the frame
43    /// has been dropped or no longer exists.
44    ///
45    /// # Example
46    /// ```
47    /// use cartesian_tree::Frame;
48    /// use nalgebra::{Vector3, UnitQuaternion};
49    ///
50    /// let frame = Frame::new_origin("base");
51    /// let pose = frame.add_pose(Vector3::new(0.0, 0.0, 0.0), UnitQuaternion::identity());
52    /// assert_eq!(pose.frame().unwrap().name(), "base");
53    /// ```
54    #[must_use]
55    pub fn frame(&self) -> Option<Frame> {
56        self.parent.upgrade().map(|data| Frame { data })
57    }
58
59    /// Returns the transformation from this pose to its parent frame.
60    ///
61    /// # Returns
62    /// The transformation of the pose in its parent frame.
63    #[must_use]
64    pub const fn transformation(&self) -> Isometry3<f64> {
65        self.transform_to_parent
66    }
67
68    /// Returns the position of this pose relative to its parent frame.
69    /// # Returns
70    /// The position of the pose in its parent frame.
71    #[must_use]
72    pub const fn position(&self) -> Vector3<f64> {
73        self.transform_to_parent.translation.vector
74    }
75
76    /// Returns the orientation of this pose relative to its parent frame.
77    /// # Returns
78    /// The orientation of the pose in its parent frame.
79    #[must_use]
80    pub fn orientation(&self) -> Rotation {
81        self.transform_to_parent.rotation.into()
82    }
83
84    /// Sets the pose's transformation relative to its parent.
85    ///
86    /// # Arguments
87    /// - `position`: A 3D vector representing the new translational offset from the parent.
88    /// - `orientation`: An orientation convertible into a unit quaternion for new orientational offset from the parent.
89    ///
90    /// # Example
91    /// ```
92    /// use cartesian_tree::Frame;
93    /// use nalgebra::{Vector3, UnitQuaternion};
94    ///
95    /// let root = Frame::new_origin("root");
96    /// let mut pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
97    /// pose.set(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity());
98    /// ```
99    pub fn set(&mut self, position: Vector3<f64>, orientation: impl Into<Rotation>) {
100        self.transform_to_parent = Isometry3::from_parts(
101            Translation3::from(position),
102            orientation.into().as_quaternion(),
103        );
104    }
105
106    /// Applies the provided isometry interpreted in the parent frame to the pose.
107    ///
108    /// # Arguments
109    /// - `isometry`: The isometry (describing a motion in the parent frame coordinates) to apply to the current transformation.
110    ///
111    /// # Example
112    /// ```
113    /// use cartesian_tree::Frame;
114    /// use nalgebra::{Isometry3, Translation3, Vector3, UnitQuaternion};
115    ///
116    /// let root = Frame::new_origin("root");
117    /// let mut pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
118    /// pose.apply_in_parent_frame(&Isometry3::from_parts(Translation3::new(1.0, 0.0, 0.0), UnitQuaternion::identity()));
119    /// ```
120    pub fn apply_in_parent_frame(&mut self, isometry: &Isometry3<f64>) {
121        self.transform_to_parent = isometry * self.transform_to_parent;
122    }
123
124    /// Applies the provided isometry interpreted in the body frame to this pose.
125    ///
126    /// # Arguments
127    /// - `isometry`: The isometry (describing a motion in the body frame coordinates) to apply to the current transformation.
128    ///
129    /// # Example
130    /// ```
131    /// use cartesian_tree::Frame;
132    /// use nalgebra::{Isometry3, Translation3, Vector3, UnitQuaternion};
133    ///
134    /// let root = Frame::new_origin("root");
135    /// let mut pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
136    /// pose.apply_in_local_frame(&Isometry3::from_parts(Translation3::new(1.0, 0.0, 0.0), UnitQuaternion::identity()));
137    /// ```
138    pub fn apply_in_local_frame(&mut self, isometry: &Isometry3<f64>) {
139        self.transform_to_parent *= isometry;
140    }
141
142    /// Transforms this pose into the coordinate system of the given target frame.
143    ///
144    /// # Arguments
145    /// * `target` - The frame to express this pose in.
146    ///
147    /// # Returns
148    /// A new `Pose`, expressed in the `target` frame.
149    ///
150    /// # Errors
151    /// Returns a [`CartesianTreeError`] if:
152    /// - The frame hierarchy cannot be resolved (e.g., due to dropped frames).
153    /// - There is no common ancestor between `self` and `target`.
154    ///
155    /// # Example
156    /// ```
157    /// use cartesian_tree::Frame;
158    /// use nalgebra::{Vector3, UnitQuaternion};
159    ///
160    /// let root = Frame::new_origin("root");
161    /// let pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
162    /// let new_frame = root.add_child("child", Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity()).unwrap();
163    /// let pose_in_new_frame = pose.in_frame(&new_frame);
164    /// ```
165    pub fn in_frame(&self, target: &Frame) -> Result<Self, CartesianTreeError> {
166        let source_data = self
167            .parent
168            .upgrade()
169            .ok_or(CartesianTreeError::WeakUpgradeFailed())?;
170        let source = Frame { data: source_data };
171        let ancestor = source
172            .lca_with(target)
173            .ok_or_else(|| CartesianTreeError::NoCommonAncestor(source.name(), target.name()))?;
174
175        // Transformation from source frame up to ancestor
176        let tf_up = source.walk_up_and_transform(&ancestor)? * self.transform_to_parent;
177
178        // Transformation from target frame up to ancestor (to be inverted)
179        let tf_down = target.walk_up_and_transform(&ancestor)?;
180
181        Ok(Self {
182            parent: target.downgrade(),
183            transform_to_parent: tf_down.inverse() * tf_up,
184        })
185    }
186}
187
188impl Add<LazyTranslation> for &Pose {
189    type Output = Pose;
190
191    fn add(self, rhs: LazyTranslation) -> Self::Output {
192        let parent = self.frame().unwrap();
193        let mut new_pose = parent.add_pose(
194            self.transform_to_parent.translation.vector,
195            self.transform_to_parent.rotation,
196        );
197        new_pose.apply_in_parent_frame(&rhs.inner);
198        new_pose
199    }
200}
201
202impl Sub<LazyTranslation> for &Pose {
203    type Output = Pose;
204
205    fn sub(self, rhs: LazyTranslation) -> Self::Output {
206        let parent = self.frame().unwrap();
207        let mut new_pose = parent.add_pose(
208            self.transform_to_parent.translation.vector,
209            self.transform_to_parent.rotation,
210        );
211        new_pose.apply_in_parent_frame(&rhs.inner.inverse());
212        new_pose
213    }
214}
215
216impl Mul<LazyRotation> for &Pose {
217    type Output = Pose;
218
219    fn mul(self, rhs: LazyRotation) -> Self::Output {
220        let parent = self.frame().unwrap();
221        let mut new_pose = parent.add_pose(
222            self.transform_to_parent.translation.vector,
223            self.transform_to_parent.rotation,
224        );
225        new_pose.apply_in_local_frame(&rhs.inner);
226        new_pose
227    }
228}