cartesian_tree/pose.rs
1use crate::CartesianTreeError;
2use crate::frame::{Frame, FrameKind, SharedTree, read_tree};
3use crate::lazy_access::{LazyRotation, LazyTranslation};
4use crate::rotation::Rotation;
5use nalgebra::{Isometry3, Translation3, Vector3};
6use std::ops::{Add, Mul, Sub};
7use std::sync::Arc;
8
9/// Use [`Frame::add_pose`] to create a new pose.
10///
11/// A pose shares ownership of the tree of the frame it lives in, so holding a pose
12/// keeps the tree alive. `Pose` is `Send + Sync`.
13#[derive(Clone)]
14pub struct Pose {
15 /// The tree of the frame this pose lives in.
16 pub(crate) tree: SharedTree,
17 /// The frame this pose lives in.
18 pub(crate) anchor: FrameKind,
19 /// Transformation from this pose to its parent frame.
20 transform_to_parent: Isometry3<f64>,
21}
22
23impl std::fmt::Debug for Pose {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 // Deliberately does not lock the tree, so Debug is safe in any context.
26 f.debug_struct("Pose")
27 .field("anchor", &self.anchor)
28 .field("transform_to_parent", &self.transform_to_parent)
29 .finish_non_exhaustive()
30 }
31}
32
33impl Pose {
34 /// Creates a new pose relative to a frame.
35 ///
36 /// This function is intended for internal use. To create a pose associated with a frame,
37 /// use [`Frame::add_pose`], which handles the association safely.
38 pub(crate) fn new(
39 tree: SharedTree,
40 anchor: FrameKind,
41 position: Vector3<f64>,
42 orientation: impl Into<Rotation>,
43 ) -> Self {
44 Self {
45 tree,
46 anchor,
47 transform_to_parent: Isometry3::from_parts(
48 Translation3::from(position),
49 orientation.into().as_quaternion(),
50 ),
51 }
52 }
53
54 /// Returns the parent frame of this pose.
55 ///
56 /// # Returns
57 /// `Some(Frame)` if the frame still exists in the tree, or `None` if it has been
58 /// removed.
59 ///
60 /// # Example
61 /// ```
62 /// use cartesian_tree::Frame;
63 /// use nalgebra::{Vector3, UnitQuaternion};
64 ///
65 /// let frame = Frame::new_origin("base");
66 /// let pose = frame.add_pose(Vector3::new(0.0, 0.0, 0.0), UnitQuaternion::identity());
67 /// assert_eq!(pose.frame().unwrap().name(), "base");
68 /// ```
69 #[must_use]
70 pub fn frame(&self) -> Option<Frame> {
71 let guard = read_tree(&self.tree);
72 guard.contains(self.anchor.anchor()).then(|| Frame {
73 tree: Arc::clone(&self.tree),
74 kind: self.anchor.clone(),
75 })
76 }
77
78 /// Returns the transformation from this pose to its parent frame.
79 ///
80 /// # Returns
81 /// The transformation of the pose in its parent frame.
82 #[must_use]
83 pub const fn transformation(&self) -> Isometry3<f64> {
84 self.transform_to_parent
85 }
86
87 /// Returns the position of this pose relative to its parent frame.
88 /// # Returns
89 /// The position of the pose in its parent frame.
90 #[must_use]
91 pub const fn position(&self) -> Vector3<f64> {
92 self.transform_to_parent.translation.vector
93 }
94
95 /// Returns the orientation of this pose relative to its parent frame.
96 /// # Returns
97 /// The orientation of the pose in its parent frame.
98 #[must_use]
99 pub fn orientation(&self) -> Rotation {
100 self.transform_to_parent.rotation.into()
101 }
102
103 /// Sets the pose's transformation relative to its parent.
104 ///
105 /// # Arguments
106 /// - `position`: A 3D vector representing the new translational offset from the parent.
107 /// - `orientation`: An orientation convertible into a unit quaternion for new orientational offset from the parent.
108 ///
109 /// # Example
110 /// ```
111 /// use cartesian_tree::Frame;
112 /// use nalgebra::{Vector3, UnitQuaternion};
113 ///
114 /// let root = Frame::new_origin("root");
115 /// let mut pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
116 /// pose.set(Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity());
117 /// ```
118 pub fn set(&mut self, position: Vector3<f64>, orientation: impl Into<Rotation>) {
119 self.transform_to_parent = Isometry3::from_parts(
120 Translation3::from(position),
121 orientation.into().as_quaternion(),
122 );
123 }
124
125 /// Applies the provided isometry interpreted in the parent frame to the pose.
126 ///
127 /// # Arguments
128 /// - `isometry`: The isometry (describing a motion in the parent frame coordinates) to apply to the current transformation.
129 ///
130 /// # Example
131 /// ```
132 /// use cartesian_tree::Frame;
133 /// use nalgebra::{Isometry3, Translation3, Vector3, UnitQuaternion};
134 ///
135 /// let root = Frame::new_origin("root");
136 /// let mut pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
137 /// pose.apply_in_parent_frame(&Isometry3::from_parts(Translation3::new(1.0, 0.0, 0.0), UnitQuaternion::identity()));
138 /// ```
139 pub fn apply_in_parent_frame(&mut self, isometry: &Isometry3<f64>) {
140 self.transform_to_parent = isometry * self.transform_to_parent;
141 }
142
143 /// Applies the provided isometry interpreted in the body frame to this pose.
144 ///
145 /// # Arguments
146 /// - `isometry`: The isometry (describing a motion in the body frame coordinates) to apply to the current transformation.
147 ///
148 /// # Example
149 /// ```
150 /// use cartesian_tree::Frame;
151 /// use nalgebra::{Isometry3, Translation3, Vector3, UnitQuaternion};
152 ///
153 /// let root = Frame::new_origin("root");
154 /// let mut pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
155 /// pose.apply_in_local_frame(&Isometry3::from_parts(Translation3::new(1.0, 0.0, 0.0), UnitQuaternion::identity()));
156 /// ```
157 pub fn apply_in_local_frame(&mut self, isometry: &Isometry3<f64>) {
158 self.transform_to_parent *= isometry;
159 }
160
161 /// Transforms this pose into the coordinate system of the given target frame.
162 ///
163 /// The computation runs under a single read lock, so it sees a consistent snapshot
164 /// of the tree even while other threads are updating transforms.
165 ///
166 /// # Arguments
167 /// * `target` - The frame to express this pose in.
168 ///
169 /// # Returns
170 /// A new `Pose`, expressed in the `target` frame.
171 ///
172 /// # Errors
173 /// Returns a [`CartesianTreeError`] if:
174 /// - The pose's frame or the target frame has been removed from the tree.
175 /// - The frames belong to different trees.
176 /// - There is no common ancestor between `self` and `target`.
177 ///
178 /// # Example
179 /// ```
180 /// use cartesian_tree::Frame;
181 /// use nalgebra::{Vector3, UnitQuaternion};
182 ///
183 /// let root = Frame::new_origin("root");
184 /// let pose = root.add_pose(Vector3::new(0.0, 0.0, 1.0), UnitQuaternion::identity());
185 /// let new_frame = root.add_child("child", Vector3::new(1.0, 0.0, 0.0), UnitQuaternion::identity()).unwrap();
186 /// let pose_in_new_frame = pose.in_frame(&new_frame);
187 /// ```
188 pub fn in_frame(&self, target: &Frame) -> Result<Self, CartesianTreeError> {
189 if !Arc::ptr_eq(&self.tree, &target.tree) {
190 let own_name = self
191 .frame()
192 .map_or_else(|| "<removed>".to_owned(), |frame| frame.name());
193 return Err(CartesianTreeError::DifferentTrees(own_name, target.name()));
194 }
195
196 let guard = read_tree(&self.tree);
197 let source_anchor = self.anchor.anchor();
198 let target_anchor = target.kind.anchor();
199
200 let ancestor = guard.lca(source_anchor, target_anchor)?.ok_or_else(|| {
201 CartesianTreeError::NoCommonAncestor(
202 guard.name_of(source_anchor),
203 guard.name_of(target_anchor),
204 )
205 })?;
206
207 // Transformation from the pose up to the ancestor.
208 let tf_up = guard.transform_up(
209 source_anchor,
210 self.anchor.offset() * self.transform_to_parent,
211 ancestor,
212 )?;
213
214 // Transformation from the target's anchor up to the ancestor (to be inverted).
215 let tf_down = guard.transform_up(target_anchor, Isometry3::identity(), ancestor)?;
216
217 Ok(Self {
218 tree: Arc::clone(&self.tree),
219 anchor: target.kind.clone(),
220 transform_to_parent: target.kind.offset().inverse() * (tf_down.inverse() * tf_up),
221 })
222 }
223}
224
225impl Add<LazyTranslation> for &Pose {
226 type Output = Pose;
227
228 fn add(self, rhs: LazyTranslation) -> Self::Output {
229 let mut new_pose = self.clone();
230 new_pose.apply_in_parent_frame(&rhs.inner);
231 new_pose
232 }
233}
234
235impl Sub<LazyTranslation> for &Pose {
236 type Output = Pose;
237
238 fn sub(self, rhs: LazyTranslation) -> Self::Output {
239 let mut new_pose = self.clone();
240 new_pose.apply_in_parent_frame(&rhs.inner.inverse());
241 new_pose
242 }
243}
244
245impl Mul<LazyRotation> for &Pose {
246 type Output = Pose;
247
248 fn mul(self, rhs: LazyRotation) -> Self::Output {
249 let mut new_pose = self.clone();
250 new_pose.apply_in_local_frame(&rhs.inner);
251 new_pose
252 }
253}