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}