token_value_map/
animated_data.rs

1use crate::{
2    macros::{impl_animated_data_insert, impl_data_type_ops, impl_sample_for_animated_data},
3    time_data_map::TimeDataMapControl,
4    *,
5};
6use anyhow::Result;
7use core::num::NonZeroU16;
8use enum_dispatch::enum_dispatch;
9use smallvec::SmallVec;
10use std::hash::Hasher;
11
12/// Time-indexed data with interpolation support.
13///
14/// [`AnimatedData`] `enum` stores a collection of time-value pairs for a
15/// specific data type and provides interpolation between keyframes. Each
16/// variant contains a [`TimeDataMap`] for the corresponding data type.
17#[enum_dispatch(AnimatedDataOps)]
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20#[cfg_attr(feature = "facet", derive(Facet))]
21#[cfg_attr(feature = "facet", facet(opaque))]
22#[cfg_attr(feature = "facet", repr(u8))]
23pub enum AnimatedData {
24    /// Animated boolean values.
25    Boolean(TimeDataMap<Boolean>),
26    /// Animated integer values.
27    Integer(TimeDataMap<Integer>),
28    /// Animated real values.
29    Real(TimeDataMap<Real>),
30    /// Animated string values.
31    String(TimeDataMap<String>),
32    /// Animated color values.
33    Color(TimeDataMap<Color>),
34    /// Animated 2D vectors.
35    #[cfg(feature = "vector2")]
36    Vector2(TimeDataMap<Vector2>),
37    /// Animated 3D vectors.
38    #[cfg(feature = "vector3")]
39    Vector3(TimeDataMap<Vector3>),
40    /// Animated transformation matrices.
41    #[cfg(feature = "matrix3")]
42    Matrix3(TimeDataMap<Matrix3>),
43    /// Animated 3D normal vectors.
44    #[cfg(feature = "normal3")]
45    Normal3(TimeDataMap<Normal3>),
46    /// Animated 3D points.
47    #[cfg(feature = "point3")]
48    Point3(TimeDataMap<Point3>),
49    /// Animated 4×4 transformation matrices.
50    #[cfg(feature = "matrix4")]
51    Matrix4(TimeDataMap<Matrix4>),
52    /// Animated boolean vectors.
53    BooleanVec(TimeDataMap<BooleanVec>),
54    /// Animated integer vectors.
55    IntegerVec(TimeDataMap<IntegerVec>),
56    /// Animated real vectors.
57    RealVec(TimeDataMap<RealVec>),
58    /// Animated color vectors.
59    ColorVec(TimeDataMap<ColorVec>),
60    /// Animated string vectors.
61    StringVec(TimeDataMap<StringVec>),
62    /// Animated 2D vector arrays.
63    #[cfg(all(feature = "vector2", feature = "vec_variants"))]
64    Vector2Vec(TimeDataMap<Vector2Vec>),
65    /// Animated 3D vector arrays.
66    #[cfg(all(feature = "vector3", feature = "vec_variants"))]
67    Vector3Vec(TimeDataMap<Vector3Vec>),
68    /// Animated matrix arrays.
69    #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
70    Matrix3Vec(TimeDataMap<Matrix3Vec>),
71    /// Animated 3D normal arrays.
72    #[cfg(all(feature = "normal3", feature = "vec_variants"))]
73    Normal3Vec(TimeDataMap<Normal3Vec>),
74    /// Animated 3D point arrays.
75    #[cfg(all(feature = "point3", feature = "vec_variants"))]
76    Point3Vec(TimeDataMap<Point3Vec>),
77    /// Animated 4×4 matrix arrays.
78    #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
79    Matrix4Vec(TimeDataMap<Matrix4Vec>),
80}
81
82/// Common operations `trait` for animated data types.
83#[enum_dispatch]
84pub trait AnimatedDataOps {
85    /// Returns the number of time samples.
86    fn len(&self) -> usize;
87    /// Returns `true` if there are no time samples.
88    fn is_empty(&self) -> bool;
89    /// Returns `true` if there is more than one time sample.
90    fn is_animated(&self) -> bool;
91}
92
93impl<T> AnimatedDataOps for TimeDataMap<T> {
94    fn len(&self) -> usize {
95        self.values.len()
96    }
97
98    fn is_empty(&self) -> bool {
99        self.values.is_empty()
100    }
101
102    fn is_animated(&self) -> bool {
103        self.values.len() > 1
104    }
105}
106
107impl_data_type_ops!(AnimatedData);
108
109impl AnimatedData {
110    /// Get all time samples from this animated data.
111    pub fn times(&self) -> SmallVec<[Time; 10]> {
112        match self {
113            AnimatedData::Boolean(map) => map.iter().map(|(t, _)| *t).collect(),
114            AnimatedData::Integer(map) => map.iter().map(|(t, _)| *t).collect(),
115            AnimatedData::Real(map) => map.iter().map(|(t, _)| *t).collect(),
116            AnimatedData::String(map) => map.iter().map(|(t, _)| *t).collect(),
117            AnimatedData::Color(map) => map.iter().map(|(t, _)| *t).collect(),
118            #[cfg(feature = "vector2")]
119            AnimatedData::Vector2(map) => map.iter().map(|(t, _)| *t).collect(),
120            #[cfg(feature = "vector3")]
121            AnimatedData::Vector3(map) => map.iter().map(|(t, _)| *t).collect(),
122            #[cfg(feature = "matrix3")]
123            AnimatedData::Matrix3(map) => map.iter().map(|(t, _)| *t).collect(),
124            #[cfg(feature = "normal3")]
125            AnimatedData::Normal3(map) => map.iter().map(|(t, _)| *t).collect(),
126            #[cfg(feature = "point3")]
127            AnimatedData::Point3(map) => map.iter().map(|(t, _)| *t).collect(),
128            #[cfg(feature = "matrix4")]
129            AnimatedData::Matrix4(map) => map.iter().map(|(t, _)| *t).collect(),
130            AnimatedData::BooleanVec(map) => map.iter().map(|(t, _)| *t).collect(),
131            AnimatedData::IntegerVec(map) => map.iter().map(|(t, _)| *t).collect(),
132            AnimatedData::RealVec(map) => map.iter().map(|(t, _)| *t).collect(),
133            AnimatedData::ColorVec(map) => map.iter().map(|(t, _)| *t).collect(),
134            AnimatedData::StringVec(map) => map.iter().map(|(t, _)| *t).collect(),
135            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
136            AnimatedData::Vector2Vec(map) => map.iter().map(|(t, _)| *t).collect(),
137            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
138            AnimatedData::Vector3Vec(map) => map.iter().map(|(t, _)| *t).collect(),
139            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
140            AnimatedData::Matrix3Vec(map) => map.iter().map(|(t, _)| *t).collect(),
141            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
142            AnimatedData::Normal3Vec(map) => map.iter().map(|(t, _)| *t).collect(),
143            #[cfg(all(feature = "point3", feature = "vec_variants"))]
144            AnimatedData::Point3Vec(map) => map.iter().map(|(t, _)| *t).collect(),
145            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
146            AnimatedData::Matrix4Vec(map) => map.iter().map(|(t, _)| *t).collect(),
147        }
148    }
149}
150
151impl From<(Time, Value)> for AnimatedData {
152    fn from((time, value): (Time, Value)) -> Self {
153        match value {
154            Value::Uniform(data) => AnimatedData::from((time, data)),
155            Value::Animated(animated_data) => {
156                // If the value is already animated, we need to insert this time
157                // sample For simplicity, we'll just return the
158                // animated data as-is In a more sophisticated
159                // implementation, we might want to merge or replace samples
160                animated_data
161            }
162        }
163    }
164}
165
166impl From<(Time, Data)> for AnimatedData {
167    fn from((time, data): (Time, Data)) -> Self {
168        match data {
169            Data::Boolean(v) => AnimatedData::Boolean(TimeDataMap::from((time, v))),
170            Data::Integer(v) => AnimatedData::Integer(TimeDataMap::from((time, v))),
171            Data::Real(v) => AnimatedData::Real(TimeDataMap::from((time, v))),
172            Data::String(v) => AnimatedData::String(TimeDataMap::from((time, v))),
173            Data::Color(v) => AnimatedData::Color(TimeDataMap::from((time, v))),
174            #[cfg(feature = "vector2")]
175            Data::Vector2(v) => AnimatedData::Vector2(TimeDataMap::from((time, v))),
176            #[cfg(feature = "vector3")]
177            Data::Vector3(v) => AnimatedData::Vector3(TimeDataMap::from((time, v))),
178            #[cfg(feature = "matrix3")]
179            Data::Matrix3(v) => AnimatedData::Matrix3(TimeDataMap::from((time, v))),
180            #[cfg(feature = "normal3")]
181            Data::Normal3(v) => AnimatedData::Normal3(TimeDataMap::from((time, v))),
182            #[cfg(feature = "point3")]
183            Data::Point3(v) => AnimatedData::Point3(TimeDataMap::from((time, v))),
184            #[cfg(feature = "matrix4")]
185            Data::Matrix4(v) => AnimatedData::Matrix4(TimeDataMap::from((time, v))),
186            Data::BooleanVec(v) => AnimatedData::BooleanVec(TimeDataMap::from((time, v))),
187            Data::IntegerVec(v) => AnimatedData::IntegerVec(TimeDataMap::from((time, v))),
188            Data::RealVec(v) => AnimatedData::RealVec(TimeDataMap::from((time, v))),
189            Data::ColorVec(v) => AnimatedData::ColorVec(TimeDataMap::from((time, v))),
190            Data::StringVec(v) => AnimatedData::StringVec(TimeDataMap::from((time, v))),
191            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
192            Data::Vector2Vec(v) => AnimatedData::Vector2Vec(TimeDataMap::from((time, v))),
193            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
194            Data::Vector3Vec(v) => AnimatedData::Vector3Vec(TimeDataMap::from((time, v))),
195            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
196            Data::Matrix3Vec(v) => AnimatedData::Matrix3Vec(TimeDataMap::from((time, v))),
197            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
198            Data::Normal3Vec(v) => AnimatedData::Normal3Vec(TimeDataMap::from((time, v))),
199            #[cfg(all(feature = "point3", feature = "vec_variants"))]
200            Data::Point3Vec(v) => AnimatedData::Point3Vec(TimeDataMap::from((time, v))),
201            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
202            Data::Matrix4Vec(v) => AnimatedData::Matrix4Vec(TimeDataMap::from((time, v))),
203        }
204    }
205}
206
207impl_animated_data_insert!(
208    insert_boolean, Boolean, Boolean;
209    insert_integer, Integer, Integer;
210    insert_real, Real, Real;
211    insert_string, String, String;
212    insert_color, Color, Color;
213);
214
215#[cfg(feature = "vector2")]
216impl_animated_data_insert!(
217    insert_vector2, Vector2, Vector2;
218);
219
220#[cfg(feature = "vector3")]
221impl_animated_data_insert!(
222    insert_vector3, Vector3, Vector3;
223);
224
225#[cfg(feature = "matrix3")]
226impl_animated_data_insert!(
227    insert_matrix3, Matrix3, Matrix3;
228);
229
230#[cfg(feature = "normal3")]
231impl_animated_data_insert!(
232    insert_normal3, Normal3, Normal3;
233);
234
235#[cfg(feature = "point3")]
236impl_animated_data_insert!(
237    insert_point3, Point3, Point3;
238);
239
240#[cfg(feature = "matrix4")]
241impl_animated_data_insert!(
242    insert_matrix4, Matrix4, Matrix4;
243);
244
245impl AnimatedData {
246    /// Generic insert method that takes `Data` and matches the type to the
247    /// `AnimatedData` variant.
248    #[named]
249    pub fn try_insert(&mut self, time: Time, value: Data) -> Result<()> {
250        match (self, value) {
251            (AnimatedData::Boolean(map), Data::Boolean(v)) => {
252                map.insert(time, v);
253                Ok(())
254            }
255            (AnimatedData::Integer(map), Data::Integer(v)) => {
256                map.insert(time, v);
257                Ok(())
258            }
259            (AnimatedData::Real(map), Data::Real(v)) => {
260                map.insert(time, v);
261                Ok(())
262            }
263            (AnimatedData::String(map), Data::String(v)) => {
264                map.insert(time, v);
265                Ok(())
266            }
267            (AnimatedData::Color(map), Data::Color(v)) => {
268                map.insert(time, v);
269                Ok(())
270            }
271            #[cfg(feature = "vector2")]
272            (AnimatedData::Vector2(map), Data::Vector2(v)) => {
273                map.insert(time, v);
274                Ok(())
275            }
276            #[cfg(feature = "vector3")]
277            (AnimatedData::Vector3(map), Data::Vector3(v)) => {
278                map.insert(time, v);
279                Ok(())
280            }
281            #[cfg(feature = "matrix3")]
282            (AnimatedData::Matrix3(map), Data::Matrix3(v)) => {
283                map.insert(time, v);
284                Ok(())
285            }
286            #[cfg(feature = "normal3")]
287            (AnimatedData::Normal3(map), Data::Normal3(v)) => {
288                map.insert(time, v);
289                Ok(())
290            }
291            #[cfg(feature = "point3")]
292            (AnimatedData::Point3(map), Data::Point3(v)) => {
293                map.insert(time, v);
294                Ok(())
295            }
296            #[cfg(feature = "matrix4")]
297            (AnimatedData::Matrix4(map), Data::Matrix4(v)) => {
298                map.insert(time, v);
299                Ok(())
300            }
301            (AnimatedData::BooleanVec(map), Data::BooleanVec(v)) => {
302                map.insert(time, v);
303                Ok(())
304            }
305            (AnimatedData::IntegerVec(map), Data::IntegerVec(v)) => {
306                map.insert(time, v);
307                Ok(())
308            }
309            (AnimatedData::RealVec(map), Data::RealVec(v)) => {
310                map.insert(time, v);
311                Ok(())
312            }
313            (AnimatedData::ColorVec(map), Data::ColorVec(v)) => {
314                map.insert(time, v);
315                Ok(())
316            }
317            (AnimatedData::StringVec(map), Data::StringVec(v)) => {
318                map.insert(time, v);
319                Ok(())
320            }
321            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
322            (AnimatedData::Vector2Vec(map), Data::Vector2Vec(v)) => {
323                map.insert(time, v);
324                Ok(())
325            }
326            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
327            (AnimatedData::Vector3Vec(map), Data::Vector3Vec(v)) => {
328                map.insert(time, v);
329                Ok(())
330            }
331            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
332            (AnimatedData::Matrix3Vec(map), Data::Matrix3Vec(v)) => {
333                map.insert(time, v);
334                Ok(())
335            }
336            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
337            (AnimatedData::Normal3Vec(map), Data::Normal3Vec(v)) => {
338                map.insert(time, v);
339                Ok(())
340            }
341            #[cfg(all(feature = "point3", feature = "vec_variants"))]
342            (AnimatedData::Point3Vec(map), Data::Point3Vec(v)) => {
343                map.insert(time, v);
344                Ok(())
345            }
346            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
347            (AnimatedData::Matrix4Vec(map), Data::Matrix4Vec(v)) => {
348                map.insert(time, v);
349                Ok(())
350            }
351            (s, v) => Err(anyhow!(
352                "{}: type mismatch: {:?} variant does not match {:?} type",
353                function_name!(),
354                s.data_type(),
355                v.data_type()
356            )),
357        }
358    }
359
360    /// Remove a sample at the given time.
361    ///
362    /// Returns the removed value as `Data` if it existed.
363    pub fn remove_at(&mut self, time: &Time) -> Option<Data> {
364        match self {
365            AnimatedData::Boolean(map) => map.remove(time).map(Data::Boolean),
366            AnimatedData::Integer(map) => map.remove(time).map(Data::Integer),
367            AnimatedData::Real(map) => map.remove(time).map(Data::Real),
368            AnimatedData::String(map) => map.remove(time).map(Data::String),
369            AnimatedData::Color(map) => map.remove(time).map(Data::Color),
370            #[cfg(feature = "vector2")]
371            AnimatedData::Vector2(map) => map.remove(time).map(Data::Vector2),
372            #[cfg(feature = "vector3")]
373            AnimatedData::Vector3(map) => map.remove(time).map(Data::Vector3),
374            #[cfg(feature = "matrix3")]
375            AnimatedData::Matrix3(map) => map.remove(time).map(Data::Matrix3),
376            #[cfg(feature = "normal3")]
377            AnimatedData::Normal3(map) => map.remove(time).map(Data::Normal3),
378            #[cfg(feature = "point3")]
379            AnimatedData::Point3(map) => map.remove(time).map(Data::Point3),
380            #[cfg(feature = "matrix4")]
381            AnimatedData::Matrix4(map) => map.remove(time).map(Data::Matrix4),
382            AnimatedData::BooleanVec(map) => map.remove(time).map(Data::BooleanVec),
383            AnimatedData::IntegerVec(map) => map.remove(time).map(Data::IntegerVec),
384            AnimatedData::RealVec(map) => map.remove(time).map(Data::RealVec),
385            AnimatedData::ColorVec(map) => map.remove(time).map(Data::ColorVec),
386            AnimatedData::StringVec(map) => map.remove(time).map(Data::StringVec),
387            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
388            AnimatedData::Vector2Vec(map) => map.remove(time).map(Data::Vector2Vec),
389            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
390            AnimatedData::Vector3Vec(map) => map.remove(time).map(Data::Vector3Vec),
391            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
392            AnimatedData::Matrix3Vec(map) => map.remove(time).map(Data::Matrix3Vec),
393            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
394            AnimatedData::Normal3Vec(map) => map.remove(time).map(Data::Normal3Vec),
395            #[cfg(all(feature = "point3", feature = "vec_variants"))]
396            AnimatedData::Point3Vec(map) => map.remove(time).map(Data::Point3Vec),
397            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
398            AnimatedData::Matrix4Vec(map) => map.remove(time).map(Data::Matrix4Vec),
399        }
400    }
401
402    pub fn sample_at(&self, time: Time) -> Option<Data> {
403        match self {
404            AnimatedData::Boolean(map) => map.get(&time).map(|v| Data::Boolean(v.clone())),
405            AnimatedData::Integer(map) => map.get(&time).map(|v| Data::Integer(v.clone())),
406            AnimatedData::Real(map) => map.get(&time).map(|v| Data::Real(v.clone())),
407            AnimatedData::String(map) => map.get(&time).map(|v| Data::String(v.clone())),
408            AnimatedData::Color(map) => map.get(&time).map(|v| Data::Color(v.clone())),
409            #[cfg(feature = "vector2")]
410            AnimatedData::Vector2(map) => map.get(&time).map(|v| Data::Vector2(v.clone())),
411            #[cfg(feature = "vector3")]
412            AnimatedData::Vector3(map) => map.get(&time).map(|v| Data::Vector3(v.clone())),
413            #[cfg(feature = "matrix3")]
414            AnimatedData::Matrix3(map) => map.get(&time).map(|v| Data::Matrix3(v.clone())),
415            #[cfg(feature = "normal3")]
416            AnimatedData::Normal3(map) => map.get(&time).map(|v| Data::Normal3(v.clone())),
417            #[cfg(feature = "point3")]
418            AnimatedData::Point3(map) => map.get(&time).map(|v| Data::Point3(v.clone())),
419            #[cfg(feature = "matrix4")]
420            AnimatedData::Matrix4(map) => map.get(&time).map(|v| Data::Matrix4(v.clone())),
421            AnimatedData::BooleanVec(map) => map.get(&time).map(|v| Data::BooleanVec(v.clone())),
422            AnimatedData::IntegerVec(map) => map.get(&time).map(|v| Data::IntegerVec(v.clone())),
423            AnimatedData::RealVec(map) => map.get(&time).map(|v| Data::RealVec(v.clone())),
424            AnimatedData::ColorVec(map) => map.get(&time).map(|v| Data::ColorVec(v.clone())),
425            AnimatedData::StringVec(map) => map.get(&time).map(|v| Data::StringVec(v.clone())),
426            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
427            AnimatedData::Vector2Vec(map) => map.get(&time).map(|v| Data::Vector2Vec(v.clone())),
428            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
429            AnimatedData::Vector3Vec(map) => map.get(&time).map(|v| Data::Vector3Vec(v.clone())),
430            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
431            AnimatedData::Matrix3Vec(map) => map.get(&time).map(|v| Data::Matrix3Vec(v.clone())),
432            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
433            AnimatedData::Normal3Vec(map) => map.get(&time).map(|v| Data::Normal3Vec(v.clone())),
434            #[cfg(all(feature = "point3", feature = "vec_variants"))]
435            AnimatedData::Point3Vec(map) => map.get(&time).map(|v| Data::Point3Vec(v.clone())),
436            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
437            AnimatedData::Matrix4Vec(map) => map.get(&time).map(|v| Data::Matrix4Vec(v.clone())),
438        }
439    }
440
441    pub fn interpolate(&self, time: Time) -> Data {
442        match self {
443            AnimatedData::Boolean(map) => Data::Boolean(map.closest_sample(time).clone()),
444            AnimatedData::Integer(map) => {
445                if TimeDataMapControl::is_animated(map) {
446                    Data::Integer(map.interpolate(time))
447                } else {
448                    Data::Integer(map.iter().next().unwrap().1.clone())
449                }
450            }
451            AnimatedData::Real(map) => {
452                if TimeDataMapControl::is_animated(map) {
453                    Data::Real(map.interpolate(time))
454                } else {
455                    Data::Real(map.iter().next().unwrap().1.clone())
456                }
457            }
458            AnimatedData::String(map) => Data::String(map.closest_sample(time).clone()),
459            AnimatedData::Color(map) => {
460                if TimeDataMapControl::is_animated(map) {
461                    Data::Color(map.interpolate(time))
462                } else {
463                    Data::Color(map.iter().next().unwrap().1.clone())
464                }
465            }
466            #[cfg(feature = "vector2")]
467            AnimatedData::Vector2(map) => {
468                if TimeDataMapControl::is_animated(map) {
469                    Data::Vector2(map.interpolate(time))
470                } else {
471                    Data::Vector2(map.iter().next().unwrap().1.clone())
472                }
473            }
474            #[cfg(feature = "vector3")]
475            AnimatedData::Vector3(map) => {
476                if TimeDataMapControl::is_animated(map) {
477                    Data::Vector3(map.interpolate(time))
478                } else {
479                    Data::Vector3(map.iter().next().unwrap().1.clone())
480                }
481            }
482            #[cfg(feature = "matrix3")]
483            AnimatedData::Matrix3(map) => {
484                if TimeDataMapControl::is_animated(map) {
485                    Data::Matrix3(map.interpolate(time))
486                } else {
487                    Data::Matrix3(map.iter().next().unwrap().1.clone())
488                }
489            }
490            #[cfg(feature = "normal3")]
491            AnimatedData::Normal3(map) => {
492                if TimeDataMapControl::is_animated(map) {
493                    Data::Normal3(map.interpolate(time))
494                } else {
495                    Data::Normal3(map.iter().next().unwrap().1.clone())
496                }
497            }
498            #[cfg(feature = "point3")]
499            AnimatedData::Point3(map) => {
500                if TimeDataMapControl::is_animated(map) {
501                    Data::Point3(map.interpolate(time))
502                } else {
503                    Data::Point3(map.iter().next().unwrap().1.clone())
504                }
505            }
506            #[cfg(feature = "matrix4")]
507            AnimatedData::Matrix4(map) => {
508                if TimeDataMapControl::is_animated(map) {
509                    Data::Matrix4(map.interpolate(time))
510                } else {
511                    Data::Matrix4(map.iter().next().unwrap().1.clone())
512                }
513            }
514            AnimatedData::BooleanVec(map) => Data::BooleanVec(map.closest_sample(time).clone()),
515            AnimatedData::IntegerVec(map) => {
516                if TimeDataMapControl::is_animated(map) {
517                    Data::IntegerVec(map.interpolate(time))
518                } else {
519                    Data::IntegerVec(map.iter().next().unwrap().1.clone())
520                }
521            }
522            AnimatedData::RealVec(map) => {
523                if TimeDataMapControl::is_animated(map) {
524                    Data::RealVec(map.interpolate(time))
525                } else {
526                    Data::RealVec(map.iter().next().unwrap().1.clone())
527                }
528            }
529            AnimatedData::ColorVec(map) => {
530                if TimeDataMapControl::is_animated(map) {
531                    Data::ColorVec(map.interpolate(time))
532                } else {
533                    Data::ColorVec(map.iter().next().unwrap().1.clone())
534                }
535            }
536            AnimatedData::StringVec(map) => Data::StringVec(map.closest_sample(time).clone()),
537            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
538            AnimatedData::Vector2Vec(map) => {
539                if TimeDataMapControl::is_animated(map) {
540                    Data::Vector2Vec(map.interpolate(time))
541                } else {
542                    Data::Vector2Vec(map.iter().next().unwrap().1.clone())
543                }
544            }
545            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
546            AnimatedData::Vector3Vec(map) => {
547                if TimeDataMapControl::is_animated(map) {
548                    Data::Vector3Vec(map.interpolate(time))
549                } else {
550                    Data::Vector3Vec(map.iter().next().unwrap().1.clone())
551                }
552            }
553            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
554            AnimatedData::Matrix3Vec(map) => {
555                if TimeDataMapControl::is_animated(map) {
556                    Data::Matrix3Vec(map.interpolate(time))
557                } else {
558                    Data::Matrix3Vec(map.iter().next().unwrap().1.clone())
559                }
560            }
561            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
562            AnimatedData::Normal3Vec(map) => {
563                if TimeDataMapControl::is_animated(map) {
564                    Data::Normal3Vec(map.interpolate(time))
565                } else {
566                    Data::Normal3Vec(map.iter().next().unwrap().1.clone())
567                }
568            }
569            #[cfg(all(feature = "point3", feature = "vec_variants"))]
570            AnimatedData::Point3Vec(map) => {
571                if TimeDataMapControl::is_animated(map) {
572                    Data::Point3Vec(map.interpolate(time))
573                } else {
574                    Data::Point3Vec(map.iter().next().unwrap().1.clone())
575                }
576            }
577            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
578            AnimatedData::Matrix4Vec(map) => {
579                if TimeDataMapControl::is_animated(map) {
580                    Data::Matrix4Vec(map.interpolate(time))
581                } else {
582                    Data::Matrix4Vec(map.iter().next().unwrap().1.clone())
583                }
584            }
585        }
586    }
587}
588
589impl Hash for AnimatedData {
590    fn hash<H: Hasher>(&self, state: &mut H) {
591        std::mem::discriminant(self).hash(state);
592        match self {
593            AnimatedData::Boolean(map) => {
594                map.len().hash(state);
595                #[cfg(not(feature = "interpolation"))]
596                for (time, value) in &map.values {
597                    time.hash(state);
598                    value.0.hash(state);
599                }
600                #[cfg(feature = "interpolation")]
601                for (time, (value, spec)) in &map.values {
602                    time.hash(state);
603                    value.0.hash(state);
604                    spec.hash(state);
605                }
606            }
607            AnimatedData::Integer(map) => {
608                map.len().hash(state);
609                #[cfg(not(feature = "interpolation"))]
610                for (time, value) in &map.values {
611                    time.hash(state);
612                    value.0.hash(state);
613                }
614                #[cfg(feature = "interpolation")]
615                for (time, (value, spec)) in &map.values {
616                    time.hash(state);
617                    value.0.hash(state);
618                    spec.hash(state);
619                }
620            }
621            AnimatedData::Real(map) => {
622                map.len().hash(state);
623                #[cfg(not(feature = "interpolation"))]
624                for (time, value) in &map.values {
625                    time.hash(state);
626                    value.0.to_bits().hash(state);
627                }
628                #[cfg(feature = "interpolation")]
629                for (time, (value, spec)) in &map.values {
630                    time.hash(state);
631                    value.0.to_bits().hash(state);
632                    spec.hash(state);
633                }
634            }
635            AnimatedData::String(map) => {
636                map.len().hash(state);
637                #[cfg(not(feature = "interpolation"))]
638                for (time, value) in &map.values {
639                    time.hash(state);
640                    value.0.hash(state);
641                }
642                #[cfg(feature = "interpolation")]
643                for (time, (value, spec)) in &map.values {
644                    time.hash(state);
645                    value.0.hash(state);
646                    spec.hash(state);
647                }
648            }
649            AnimatedData::Color(map) => {
650                map.len().hash(state);
651                #[cfg(not(feature = "interpolation"))]
652                for (time, value) in &map.values {
653                    time.hash(state);
654                    value.0.iter().for_each(|v| v.to_bits().hash(state));
655                }
656                #[cfg(feature = "interpolation")]
657                for (time, (value, spec)) in &map.values {
658                    time.hash(state);
659                    value.0.iter().for_each(|v| v.to_bits().hash(state));
660                    spec.hash(state);
661                }
662            }
663            #[cfg(feature = "vector2")]
664            AnimatedData::Vector2(map) => {
665                map.len().hash(state);
666                #[cfg(not(feature = "interpolation"))]
667                for (time, value) in &map.values {
668                    time.hash(state);
669                    value.0.iter().for_each(|v| v.to_bits().hash(state));
670                }
671                #[cfg(feature = "interpolation")]
672                for (time, (value, spec)) in &map.values {
673                    time.hash(state);
674                    value.0.iter().for_each(|v| v.to_bits().hash(state));
675                    spec.hash(state);
676                }
677            }
678            #[cfg(feature = "vector3")]
679            AnimatedData::Vector3(map) => {
680                map.len().hash(state);
681                #[cfg(not(feature = "interpolation"))]
682                for (time, value) in &map.values {
683                    time.hash(state);
684                    value.0.iter().for_each(|v| v.to_bits().hash(state));
685                }
686                #[cfg(feature = "interpolation")]
687                for (time, (value, spec)) in &map.values {
688                    time.hash(state);
689                    value.0.iter().for_each(|v| v.to_bits().hash(state));
690                    spec.hash(state);
691                }
692            }
693            #[cfg(feature = "matrix3")]
694            AnimatedData::Matrix3(map) => {
695                map.len().hash(state);
696                #[cfg(not(feature = "interpolation"))]
697                for (time, value) in &map.values {
698                    time.hash(state);
699                    value.0.iter().for_each(|v| v.to_bits().hash(state));
700                }
701                #[cfg(feature = "interpolation")]
702                for (time, (value, spec)) in &map.values {
703                    time.hash(state);
704                    value.0.iter().for_each(|v| v.to_bits().hash(state));
705                    spec.hash(state);
706                }
707            }
708            #[cfg(feature = "normal3")]
709            AnimatedData::Normal3(map) => {
710                map.len().hash(state);
711                #[cfg(not(feature = "interpolation"))]
712                for (time, value) in &map.values {
713                    time.hash(state);
714                    value.0.iter().for_each(|v| v.to_bits().hash(state));
715                }
716                #[cfg(feature = "interpolation")]
717                for (time, (value, spec)) in &map.values {
718                    time.hash(state);
719                    value.0.iter().for_each(|v| v.to_bits().hash(state));
720                    spec.hash(state);
721                }
722            }
723            #[cfg(feature = "point3")]
724            AnimatedData::Point3(map) => {
725                map.len().hash(state);
726                #[cfg(not(feature = "interpolation"))]
727                for (time, value) in &map.values {
728                    time.hash(state);
729                    value.0.coords.iter().for_each(|v| v.to_bits().hash(state));
730                }
731                #[cfg(feature = "interpolation")]
732                for (time, (value, spec)) in &map.values {
733                    time.hash(state);
734                    value.0.coords.iter().for_each(|v| v.to_bits().hash(state));
735                    spec.hash(state);
736                }
737            }
738            #[cfg(feature = "matrix4")]
739            AnimatedData::Matrix4(map) => {
740                map.len().hash(state);
741                #[cfg(not(feature = "interpolation"))]
742                for (time, value) in &map.values {
743                    time.hash(state);
744                    value.0.iter().for_each(|v| v.to_bits().hash(state));
745                }
746                #[cfg(feature = "interpolation")]
747                for (time, (value, spec)) in &map.values {
748                    time.hash(state);
749                    value.0.iter().for_each(|v| v.to_bits().hash(state));
750                    spec.hash(state);
751                }
752            }
753            AnimatedData::BooleanVec(map) => {
754                map.len().hash(state);
755                #[cfg(not(feature = "interpolation"))]
756                for (time, value) in &map.values {
757                    time.hash(state);
758                    value.0.hash(state);
759                }
760                #[cfg(feature = "interpolation")]
761                for (time, (value, spec)) in &map.values {
762                    time.hash(state);
763                    value.0.hash(state);
764                    spec.hash(state);
765                }
766            }
767            AnimatedData::IntegerVec(map) => {
768                map.len().hash(state);
769                #[cfg(not(feature = "interpolation"))]
770                for (time, value) in &map.values {
771                    time.hash(state);
772                    value.0.hash(state);
773                }
774                #[cfg(feature = "interpolation")]
775                for (time, (value, spec)) in &map.values {
776                    time.hash(state);
777                    value.0.hash(state);
778                    spec.hash(state);
779                }
780            }
781            AnimatedData::RealVec(map) => {
782                map.len().hash(state);
783                #[cfg(not(feature = "interpolation"))]
784                for (time, value) in &map.values {
785                    time.hash(state);
786                    value.0.len().hash(state);
787                    value.0.iter().for_each(|v| v.to_bits().hash(state));
788                }
789                #[cfg(feature = "interpolation")]
790                for (time, (value, spec)) in &map.values {
791                    time.hash(state);
792                    value.0.len().hash(state);
793                    value.0.iter().for_each(|v| v.to_bits().hash(state));
794                    spec.hash(state);
795                }
796            }
797            AnimatedData::ColorVec(map) => {
798                map.len().hash(state);
799                #[cfg(not(feature = "interpolation"))]
800                for (time, value) in &map.values {
801                    time.hash(state);
802                    value.0.len().hash(state);
803                    value.0.iter().for_each(|c| {
804                        c.iter().for_each(|v| v.to_bits().hash(state));
805                    });
806                }
807                #[cfg(feature = "interpolation")]
808                for (time, (value, spec)) in &map.values {
809                    time.hash(state);
810                    value.0.len().hash(state);
811                    value.0.iter().for_each(|c| {
812                        c.iter().for_each(|v| v.to_bits().hash(state));
813                    });
814                    spec.hash(state);
815                }
816            }
817            AnimatedData::StringVec(map) => {
818                map.len().hash(state);
819                #[cfg(not(feature = "interpolation"))]
820                for (time, value) in &map.values {
821                    time.hash(state);
822                    value.0.hash(state);
823                }
824                #[cfg(feature = "interpolation")]
825                for (time, (value, spec)) in &map.values {
826                    time.hash(state);
827                    value.0.hash(state);
828                    spec.hash(state);
829                }
830            }
831            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
832            AnimatedData::Vector2Vec(map) => {
833                map.len().hash(state);
834                #[cfg(not(feature = "interpolation"))]
835                for (time, value) in &map.values {
836                    time.hash(state);
837                    value.0.len().hash(state);
838                    value.0.iter().for_each(|v| {
839                        v.iter().for_each(|f| f.to_bits().hash(state));
840                    });
841                }
842                #[cfg(feature = "interpolation")]
843                for (time, (value, spec)) in &map.values {
844                    time.hash(state);
845                    value.0.len().hash(state);
846                    value.0.iter().for_each(|v| {
847                        v.iter().for_each(|f| f.to_bits().hash(state));
848                    });
849                    spec.hash(state);
850                }
851            }
852            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
853            AnimatedData::Vector3Vec(map) => {
854                map.len().hash(state);
855                #[cfg(not(feature = "interpolation"))]
856                for (time, value) in &map.values {
857                    time.hash(state);
858                    value.0.len().hash(state);
859                    value.0.iter().for_each(|v| {
860                        v.iter().for_each(|f| f.to_bits().hash(state));
861                    });
862                }
863                #[cfg(feature = "interpolation")]
864                for (time, (value, spec)) in &map.values {
865                    time.hash(state);
866                    value.0.len().hash(state);
867                    value.0.iter().for_each(|v| {
868                        v.iter().for_each(|f| f.to_bits().hash(state));
869                    });
870                    spec.hash(state);
871                }
872            }
873            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
874            AnimatedData::Matrix3Vec(map) => {
875                map.len().hash(state);
876                #[cfg(not(feature = "interpolation"))]
877                for (time, value) in &map.values {
878                    time.hash(state);
879                    value.0.len().hash(state);
880                    value.0.iter().for_each(|m| {
881                        m.iter().for_each(|f| f.to_bits().hash(state));
882                    });
883                }
884                #[cfg(feature = "interpolation")]
885                for (time, (value, spec)) in &map.values {
886                    time.hash(state);
887                    value.0.len().hash(state);
888                    value.0.iter().for_each(|m| {
889                        m.iter().for_each(|f| f.to_bits().hash(state));
890                    });
891                    spec.hash(state);
892                }
893            }
894            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
895            AnimatedData::Normal3Vec(map) => {
896                map.len().hash(state);
897                #[cfg(not(feature = "interpolation"))]
898                for (time, value) in &map.values {
899                    time.hash(state);
900                    value.0.len().hash(state);
901                    value.0.iter().for_each(|v| {
902                        v.iter().for_each(|f| f.to_bits().hash(state));
903                    });
904                }
905                #[cfg(feature = "interpolation")]
906                for (time, (value, spec)) in &map.values {
907                    time.hash(state);
908                    value.0.len().hash(state);
909                    value.0.iter().for_each(|v| {
910                        v.iter().for_each(|f| f.to_bits().hash(state));
911                    });
912                    spec.hash(state);
913                }
914            }
915            #[cfg(all(feature = "point3", feature = "vec_variants"))]
916            AnimatedData::Point3Vec(map) => {
917                map.len().hash(state);
918                #[cfg(not(feature = "interpolation"))]
919                for (time, value) in &map.values {
920                    time.hash(state);
921                    value.0.len().hash(state);
922                    value.0.iter().for_each(|p| {
923                        p.coords.iter().for_each(|f| f.to_bits().hash(state));
924                    });
925                }
926                #[cfg(feature = "interpolation")]
927                for (time, (value, spec)) in &map.values {
928                    time.hash(state);
929                    value.0.len().hash(state);
930                    value.0.iter().for_each(|p| {
931                        p.coords.iter().for_each(|f| f.to_bits().hash(state));
932                    });
933                    spec.hash(state);
934                }
935            }
936            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
937            AnimatedData::Matrix4Vec(map) => {
938                map.len().hash(state);
939                #[cfg(not(feature = "interpolation"))]
940                for (time, value) in &map.values {
941                    time.hash(state);
942                    value.0.len().hash(state);
943                    value.0.iter().for_each(|m| {
944                        m.iter().for_each(|f| f.to_bits().hash(state));
945                    });
946                }
947                #[cfg(feature = "interpolation")]
948                for (time, (value, spec)) in &map.values {
949                    time.hash(state);
950                    value.0.len().hash(state);
951                    value.0.iter().for_each(|m| {
952                        m.iter().for_each(|f| f.to_bits().hash(state));
953                    });
954                    spec.hash(state);
955                }
956            }
957        }
958    }
959}
960
961// Sample trait implementations for AnimatedData
962impl_sample_for_animated_data!(
963    Real, Real;
964    Integer, Integer;
965    Color, Color;
966);
967
968impl_sample_for_animated_data!(
969    Vector2, Vector2, "vector2";
970    Vector3, Vector3, "vector3";
971    Matrix3, Matrix3, "matrix3";
972    Normal3, Normal3, "normal3";
973    Point3, Point3, "point3";
974    Matrix4, Matrix4, "matrix4";
975);
976
977impl AnimatedData {
978    /// Hash the animated data with shutter context for better cache coherency.
979    ///
980    /// Samples the animation at standardized points within the shutter range
981    /// and hashes the interpolated values. If all samples are identical,
982    /// only one value is hashed for efficiency.
983    pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
984        use smallvec::SmallVec;
985
986        // Sample at 5 standardized points within the shutter.
987        const SAMPLE_POSITIONS: [f32; 5] = [0.0, 0.25, 0.5, 0.75, 1.0];
988
989        // Collect interpolated samples using SmallVec to avoid heap allocation.
990        let samples: SmallVec<[Data; 5]> = SAMPLE_POSITIONS
991            .iter()
992            .map(|&pos| {
993                let time = shutter.evaluate(pos);
994                self.interpolate(time)
995            })
996            .collect();
997
998        // Check if all samples are identical.
999        let all_same = samples.windows(2).all(|w| w[0] == w[1]);
1000
1001        // Hash the data type discriminant.
1002        std::mem::discriminant(self).hash(state);
1003
1004        if all_same {
1005            // If all samples are the same, just hash one.
1006            1usize.hash(state); // Indicate single sample.
1007            samples[0].hash(state);
1008        } else {
1009            // Hash all samples.
1010            samples.len().hash(state); // Indicate multiple samples.
1011            for sample in &samples {
1012                sample.hash(state);
1013            }
1014        }
1015    }
1016}