ecoord_core/
transform_edge.rs1use crate::Error::NoTransforms;
2use crate::{
3 Error, ExtrapolationMethod, FrameId, InterpolationMethod, TimedTransform, Transform,
4 TransformId,
5};
6use chrono::{DateTime, Utc};
7
8#[derive(Debug, Clone, PartialEq)]
9pub enum TransformEdge {
10 Static(StaticTransform),
11 Dynamic(DynamicTransform),
12}
13
14impl TransformEdge {
15 pub fn at_time(&self, timestamp: DateTime<Utc>) -> Transform {
16 match self {
17 TransformEdge::Static(s) => s.transform,
18 TransformEdge::Dynamic(d) => d.interpolate(timestamp),
19 }
20 }
21
22 pub fn parent_frame_id(&self) -> &FrameId {
23 match self {
24 TransformEdge::Static(s) => &s.parent_frame_id,
25 TransformEdge::Dynamic(d) => &d.parent_frame_id,
26 }
27 }
28
29 pub fn child_frame_id(&self) -> &FrameId {
30 match self {
31 TransformEdge::Static(s) => &s.child_frame_id,
32 TransformEdge::Dynamic(d) => &d.child_frame_id,
33 }
34 }
35
36 pub fn transform_id(&self) -> TransformId {
37 match self {
38 TransformEdge::Static(s) => s.transform_id(),
39 TransformEdge::Dynamic(d) => d.transform_id(),
40 }
41 }
42}
43
44#[derive(Debug, Clone, PartialEq)]
45pub struct StaticTransform {
46 parent_frame_id: FrameId,
47 child_frame_id: FrameId,
48 pub transform: Transform,
49}
50
51impl StaticTransform {
52 pub fn new(parent_frame_id: FrameId, child_frame_id: FrameId, transform: Transform) -> Self {
53 Self {
54 parent_frame_id,
55 child_frame_id,
56 transform,
57 }
58 }
59
60 pub fn parent_frame_id(&self) -> &FrameId {
61 &self.parent_frame_id
62 }
63
64 pub fn child_frame_id(&self) -> &FrameId {
65 &self.child_frame_id
66 }
67
68 pub fn transform_id(&self) -> TransformId {
69 TransformId::new(self.parent_frame_id.clone(), self.child_frame_id.clone())
70 }
71}
72
73#[derive(Debug, Clone, PartialEq)]
74pub struct DynamicTransform {
75 parent_frame_id: FrameId,
76 child_frame_id: FrameId,
77 pub interpolation: Option<InterpolationMethod>,
78 pub extrapolation: Option<ExtrapolationMethod>,
79 pub samples: Vec<TimedTransform>,
80}
81
82impl DynamicTransform {
83 pub fn new(
84 parent_frame_id: FrameId,
85 child_frame_id: FrameId,
86 interpolation: Option<InterpolationMethod>,
87 extrapolation: Option<ExtrapolationMethod>,
88 mut samples: Vec<TimedTransform>,
89 ) -> Result<Self, Error> {
90 if samples.is_empty() {
91 return Err(NoTransforms());
92 }
93 samples.sort_by_key(|s| s.timestamp);
94
95 for window in samples.windows(2) {
96 if window[0].timestamp == window[1].timestamp {
97 return Err(Error::DuplicateTimestamp(window[1].timestamp));
98 }
99 }
100
101 Ok(Self {
102 parent_frame_id,
103 child_frame_id,
104 interpolation,
105 extrapolation,
106 samples,
107 })
108 }
109
110 pub fn parent_frame_id(&self) -> &FrameId {
111 &self.parent_frame_id
112 }
113
114 pub fn child_frame_id(&self) -> &FrameId {
115 &self.child_frame_id
116 }
117
118 pub fn transform_id(&self) -> TransformId {
119 TransformId::new(self.parent_frame_id.clone(), self.child_frame_id.clone())
120 }
121
122 pub fn sample_timestamps(&self) -> Vec<DateTime<Utc>> {
123 self.samples.iter().map(|x| x.timestamp).collect()
124 }
125
126 pub fn first_sample_time(&self) -> DateTime<Utc> {
127 self.samples
128 .first()
129 .expect("must at least have one sample")
130 .timestamp
131 }
132
133 pub fn last_sample_time(&self) -> DateTime<Utc> {
134 self.samples
135 .last()
136 .expect("must at least have one sample")
137 .timestamp
138 }
139}
140
141impl DynamicTransform {
142 pub fn interpolate(&self, timestamp: DateTime<Utc>) -> Transform {
143 debug_assert!(
144 self.samples.is_sorted_by_key(|t| t.timestamp),
145 "transforms must be sorted by timestamp"
146 );
147 debug_assert!(
148 self.samples
149 .windows(2)
150 .all(|t| t[0].timestamp != t[1].timestamp),
151 "transforms must not contain two samples with same timestamps"
152 );
153
154 if timestamp < self.first_sample_time() || self.last_sample_time() <= timestamp {
155 return match self.extrapolation.unwrap_or_default() {
156 ExtrapolationMethod::Constant => {
157 crate::utils::transforms_interpolation::extrapolate_constant(
158 &self.samples,
159 ×tamp,
160 )
161 }
162
163 ExtrapolationMethod::Linear => {
164 crate::utils::transforms_interpolation::extrapolate_linear(
165 &self.samples,
166 ×tamp,
167 )
168 }
169 };
170 }
171
172 match self.interpolation.unwrap_or_default() {
173 InterpolationMethod::Step => {
174 crate::utils::transforms_interpolation::interpolate_step_function(
175 &self.samples,
176 ×tamp,
177 )
178 }
179 InterpolationMethod::Linear => {
180 crate::utils::transforms_interpolation::interpolate_linearly(
181 &self.samples,
182 ×tamp,
183 )
184 }
185 }
186 }
187
188 pub fn filter_samples_by_time(
189 &mut self,
190 start_time: Option<DateTime<Utc>>,
191 end_time: Option<DateTime<Utc>>,
192 ) -> Result<(), Error> {
193 let filtered_samples: Vec<TimedTransform> = self
194 .samples
195 .iter()
196 .filter(|t| start_time.is_none_or(|x| x <= t.timestamp))
197 .filter(|t| end_time.is_none_or(|x| t.timestamp < x))
198 .copied()
199 .collect();
200 if filtered_samples.is_empty() {
201 return Err(NoTransforms());
202 }
203
204 self.samples = filtered_samples;
205 Ok(())
206 }
207}