1use crate::camera::camera::{Camera, CameraTarget};
4
5#[derive(Clone, Debug, Default)]
19pub struct CameraTrack {
20 keyframes: Vec<(f64, CameraTarget)>,
22}
23
24impl CameraTrack {
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn from_keyframes(mut keyframes: Vec<(f64, CameraTarget)>) -> Self {
35 keyframes.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
36 Self { keyframes }
37 }
38
39 pub fn push(&mut self, time: f64, target: CameraTarget) {
41 let pos = self
42 .keyframes
43 .partition_point(|(t, _)| *t <= time);
44 self.keyframes.insert(pos, (time, target));
45 }
46
47 pub fn keyframes(&self) -> &[(f64, CameraTarget)] {
49 &self.keyframes
50 }
51
52 pub fn duration(&self) -> f64 {
54 match (self.keyframes.first(), self.keyframes.last()) {
55 (Some(first), Some(last)) => (last.0 - first.0).max(0.0),
56 _ => 0.0,
57 }
58 }
59
60 pub fn is_empty(&self) -> bool {
62 self.keyframes.is_empty()
63 }
64
65 pub fn len(&self) -> usize {
67 self.keyframes.len()
68 }
69}
70
71pub fn interpolate_camera(track: &CameraTrack, t: f64) -> CameraTarget {
85 let kfs = track.keyframes();
86
87 match kfs.len() {
88 0 => CameraTarget {
89 center: glam::Vec3::ZERO,
90 distance: 5.0,
91 orientation: glam::Quat::IDENTITY,
92 },
93 1 => kfs[0].1,
94 _ => {
95 if t <= kfs[0].0 {
97 return kfs[0].1;
98 }
99 if t >= kfs[kfs.len() - 1].0 {
100 return kfs[kfs.len() - 1].1;
101 }
102
103 let i = kfs.partition_point(|(kt, _)| *kt <= t).saturating_sub(1);
105 let i = i.min(kfs.len() - 2);
106
107 let t0 = kfs[i].0;
108 let t1 = kfs[i + 1].0;
109 let s = if (t1 - t0).abs() < 1e-12 {
110 0.0_f32
111 } else {
112 ((t - t0) / (t1 - t0)) as f32
113 };
114
115 let p1 = kfs[i].1;
117 let p2 = kfs[i + 1].1;
118 let p0 = if i > 0 {
119 kfs[i - 1].1
120 } else {
121 CameraTarget {
123 center: p1.center * 2.0 - p2.center,
124 distance: p1.distance * 2.0 - p2.distance,
125 orientation: p1.orientation, }
127 };
128 let p3 = if i + 2 < kfs.len() {
129 kfs[i + 2].1
130 } else {
131 CameraTarget {
133 center: p2.center * 2.0 - p1.center,
134 distance: p2.distance * 2.0 - p1.distance,
135 orientation: p2.orientation,
136 }
137 };
138
139 CameraTarget {
140 center: catmull_rom_vec3(p0.center, p1.center, p2.center, p3.center, s),
141 distance: catmull_rom_f32(p0.distance, p1.distance, p2.distance, p3.distance, s)
142 .max(Camera::MIN_DISTANCE),
143 orientation: p1.orientation.slerp(p2.orientation, s).normalize(),
144 }
145 }
146 }
147}
148
149fn catmull_rom_f32(p0: f32, p1: f32, p2: f32, p3: f32, s: f32) -> f32 {
154 let s2 = s * s;
155 let s3 = s2 * s;
156 0.5 * (2.0 * p1
157 + (-p0 + p2) * s
158 + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * s2
159 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * s3)
160}
161
162fn catmull_rom_vec3(
163 p0: glam::Vec3,
164 p1: glam::Vec3,
165 p2: glam::Vec3,
166 p3: glam::Vec3,
167 s: f32,
168) -> glam::Vec3 {
169 glam::Vec3::new(
170 catmull_rom_f32(p0.x, p1.x, p2.x, p3.x, s),
171 catmull_rom_f32(p0.y, p1.y, p2.y, p3.y, s),
172 catmull_rom_f32(p0.z, p1.z, p2.z, p3.z, s),
173 )
174}
175
176#[cfg(test)]
181mod tests {
182 use super::*;
183
184 fn target(x: f32, d: f32) -> CameraTarget {
185 CameraTarget {
186 center: glam::Vec3::new(x, 0.0, 0.0),
187 distance: d,
188 orientation: glam::Quat::IDENTITY,
189 }
190 }
191
192 #[test]
193 fn test_empty_track_returns_default() {
194 let track = CameraTrack::new();
195 let t = interpolate_camera(&track, 0.0);
196 assert_eq!(t.distance, 5.0);
197 }
198
199 #[test]
200 fn test_single_keyframe() {
201 let mut track = CameraTrack::new();
202 track.push(0.0, target(3.0, 7.0));
203 let t = interpolate_camera(&track, 5.0);
204 assert!((t.center.x - 3.0).abs() < 1e-5);
205 assert!((t.distance - 7.0).abs() < 1e-5);
206 }
207
208 #[test]
209 fn test_clamp_before_start() {
210 let mut track = CameraTrack::new();
211 track.push(1.0, target(1.0, 1.0));
212 track.push(2.0, target(2.0, 2.0));
213 let t = interpolate_camera(&track, 0.0);
214 assert!((t.center.x - 1.0).abs() < 1e-5);
215 }
216
217 #[test]
218 fn test_clamp_after_end() {
219 let mut track = CameraTrack::new();
220 track.push(1.0, target(1.0, 1.0));
221 track.push(2.0, target(2.0, 2.0));
222 let t = interpolate_camera(&track, 5.0);
223 assert!((t.center.x - 2.0).abs() < 1e-5);
224 }
225
226 #[test]
227 fn test_midpoint_two_keyframes() {
228 let mut track = CameraTrack::new();
229 track.push(0.0, target(0.0, 4.0));
230 track.push(1.0, target(2.0, 8.0));
231 let t = interpolate_camera(&track, 0.5);
234 assert!((t.center.x - 1.0).abs() < 0.05, "center.x={}", t.center.x);
235 assert!((t.distance - 6.0).abs() < 0.1, "distance={}", t.distance);
236 }
237
238 #[test]
239 fn test_keyframe_hit_exact() {
240 let mut track = CameraTrack::new();
241 track.push(0.0, target(0.0, 1.0));
242 track.push(1.0, target(5.0, 3.0));
243 track.push(2.0, target(10.0, 5.0));
244 let t = interpolate_camera(&track, 1.0);
246 assert!((t.center.x - 5.0).abs() < 1e-4, "center.x={}", t.center.x);
247 assert!((t.distance - 3.0).abs() < 1e-4, "distance={}", t.distance);
248 }
249
250 #[test]
251 fn test_from_keyframes_sorts() {
252 let kfs = vec![
253 (2.0_f64, target(2.0, 2.0)),
254 (0.0_f64, target(0.0, 0.0)),
255 (1.0_f64, target(1.0, 1.0)),
256 ];
257 let track = CameraTrack::from_keyframes(kfs);
258 assert_eq!(track.keyframes()[0].0, 0.0);
259 assert_eq!(track.keyframes()[1].0, 1.0);
260 assert_eq!(track.keyframes()[2].0, 2.0);
261 }
262
263 #[test]
264 fn test_duration() {
265 let mut track = CameraTrack::new();
266 assert_eq!(track.duration(), 0.0);
267 track.push(1.0, target(0.0, 1.0));
268 track.push(4.0, target(1.0, 2.0));
269 assert!((track.duration() - 3.0).abs() < 1e-10);
270 }
271
272 #[test]
273 fn test_push_keeps_sorted() {
274 let mut track = CameraTrack::new();
275 track.push(3.0, target(3.0, 3.0));
276 track.push(1.0, target(1.0, 1.0));
277 track.push(2.0, target(2.0, 2.0));
278 assert_eq!(track.keyframes()[0].0, 1.0);
279 assert_eq!(track.keyframes()[1].0, 2.0);
280 assert_eq!(track.keyframes()[2].0, 3.0);
281 }
282}