1use core::ops::{Add, Mul};
2use std::vec::Vec;
3
4pub mod hermite;
5use hermite::{h_5, h_5p, h_5pp};
6
7pub mod vec;
8
9#[derive(Debug, PartialEq)]
10pub struct Pose<V> {
11 pub position: V,
12 pub velocity: V,
13 pub acceleration: V,
14}
15
16#[derive(Debug, PartialEq)]
17pub struct Segment<'a, V>(f64, &'a Pose<V>, &'a Pose<V>);
18
19pub trait Trajectory<V> {
20 fn get_segment(&self, t: f64) -> Option<Segment<V>>;
21 fn position_at(&self, t: f64) -> Option<V>;
22 fn velocity_at(&self, t: f64) -> Option<V>;
23 fn acceleration_at(&self, t: f64) -> Option<V>;
24}
25
26impl<V> Trajectory<V> for Vec<Pose<V>>
27where
28 V: Add<V, Output = V> + Copy + Mul<f64, Output = V>,
29{
30 fn get_segment(&self, t: f64) -> Option<Segment<V>> {
31 let length = self.len();
32
33 if let 0 = length {
35 return None;
36 }
37
38 let prec_idx = t.floor() as usize;
41 let succ_idx = t.ceil() as usize;
42
43 let prec: &Pose<V> = &self[prec_idx];
44 let succ: &Pose<V> = &self[succ_idx];
45
46 let t = t.fract();
47
48 assert!(0.0_f64 <= t && t <= 1.0_f64, "{} not in [0., 1.]", t);
49
50 Some(Segment(t, prec, succ))
51 }
52
53 fn position_at(&self, t: f64) -> Option<V> {
54 let anchors = self.get_segment(t);
55
56 if let Some(Segment(t, prec, succ)) = anchors {
57 let p0 = &prec.position;
58 let v0 = &prec.velocity;
59 let a0 = &prec.acceleration;
60
61 let p1 = &succ.position;
62 let v1 = &succ.velocity;
63 let a1 = &succ.acceleration;
64
65 let h05 = h_5(t, 0);
66 let h15 = h_5(t, 1);
67 let h25 = h_5(t, 2);
68 let h35 = h_5(t, 3);
69 let h45 = h_5(t, 4);
70 let h55 = h_5(t, 5);
71
72 return Some(
73 (*p0 * h05) + (*v0 * h15) + (*a0 * h25) + (*a1 * h35) + (*v1 * h45) + (*p1 * h55),
74 );
75 }
76
77 None
78 }
79
80 fn velocity_at(&self, t: f64) -> Option<V> {
81 let anchors = self.get_segment(t);
82
83 if let Some(Segment(t, prec, succ)) = anchors {
84 let p0 = &prec.position;
85 let v0 = &prec.velocity;
86 let a0 = &prec.acceleration;
87
88 let p1 = &succ.position;
89 let v1 = &succ.velocity;
90 let a1 = &succ.acceleration;
91
92 let h05p = h_5p(t, 0);
93 let h15p = h_5p(t, 1);
94 let h25p = h_5p(t, 2);
95 let h35p = h_5p(t, 3);
96 let h45p = h_5p(t, 4);
97 let h55p = h_5p(t, 5);
98
99 return Some(
100 (*p0 * h05p) + (*v0 * h15p) + (*a0 * h25p) + (*a1 * h35p) + (*v1 * h45p) + (*p1 * h55p),
101 );
102 }
103
104 None
105 }
106
107 fn acceleration_at(&self, t: f64) -> Option<V> {
108 let anchors = self.get_segment(t);
109
110 if let Some(Segment(t, prec, succ)) = anchors {
111 let p0 = &prec.position;
112 let v0 = &prec.velocity;
113 let a0 = &prec.acceleration;
114
115 let p1 = &succ.position;
116 let v1 = &succ.velocity;
117 let a1 = &succ.acceleration;
118
119 let h05pp = h_5pp(t, 0);
120 let h15pp = h_5pp(t, 1);
121 let h25pp = h_5pp(t, 2);
122 let h35pp = h_5pp(t, 3);
123 let h45pp = h_5pp(t, 4);
124 let h55pp = h_5pp(t, 5);
125
126 return Some(
127 (*p0 * h05pp)
128 + (*v0 * h15pp)
129 + (*a0 * h25pp)
130 + (*a1 * h35pp)
131 + (*v1 * h45pp)
132 + (*p1 * h55pp),
133 );
134 }
135
136 None
137 }
138}
139
140#[cfg(test)]
141mod tests;
142
143#[cfg(test)]
144#[macro_export]
145macro_rules! assert_f64_roughly_eq {
146 ($left:expr, $right:expr) => {
147 assert!(($right - $left).abs() < f64::EPSILON)
148 };
149}