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    pub fn sample_at(&self, time: Time) -> Option<Data> {
361        match self {
362            AnimatedData::Boolean(map) => map.get(&time).map(|v| Data::Boolean(v.clone())),
363            AnimatedData::Integer(map) => map.get(&time).map(|v| Data::Integer(v.clone())),
364            AnimatedData::Real(map) => map.get(&time).map(|v| Data::Real(v.clone())),
365            AnimatedData::String(map) => map.get(&time).map(|v| Data::String(v.clone())),
366            AnimatedData::Color(map) => map.get(&time).map(|v| Data::Color(v.clone())),
367            #[cfg(feature = "vector2")]
368            AnimatedData::Vector2(map) => map.get(&time).map(|v| Data::Vector2(v.clone())),
369            #[cfg(feature = "vector3")]
370            AnimatedData::Vector3(map) => map.get(&time).map(|v| Data::Vector3(v.clone())),
371            #[cfg(feature = "matrix3")]
372            AnimatedData::Matrix3(map) => map.get(&time).map(|v| Data::Matrix3(v.clone())),
373            #[cfg(feature = "normal3")]
374            AnimatedData::Normal3(map) => map.get(&time).map(|v| Data::Normal3(v.clone())),
375            #[cfg(feature = "point3")]
376            AnimatedData::Point3(map) => map.get(&time).map(|v| Data::Point3(v.clone())),
377            #[cfg(feature = "matrix4")]
378            AnimatedData::Matrix4(map) => map.get(&time).map(|v| Data::Matrix4(v.clone())),
379            AnimatedData::BooleanVec(map) => map.get(&time).map(|v| Data::BooleanVec(v.clone())),
380            AnimatedData::IntegerVec(map) => map.get(&time).map(|v| Data::IntegerVec(v.clone())),
381            AnimatedData::RealVec(map) => map.get(&time).map(|v| Data::RealVec(v.clone())),
382            AnimatedData::ColorVec(map) => map.get(&time).map(|v| Data::ColorVec(v.clone())),
383            AnimatedData::StringVec(map) => map.get(&time).map(|v| Data::StringVec(v.clone())),
384            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
385            AnimatedData::Vector2Vec(map) => map.get(&time).map(|v| Data::Vector2Vec(v.clone())),
386            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
387            AnimatedData::Vector3Vec(map) => map.get(&time).map(|v| Data::Vector3Vec(v.clone())),
388            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
389            AnimatedData::Matrix3Vec(map) => map.get(&time).map(|v| Data::Matrix3Vec(v.clone())),
390            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
391            AnimatedData::Normal3Vec(map) => map.get(&time).map(|v| Data::Normal3Vec(v.clone())),
392            #[cfg(all(feature = "point3", feature = "vec_variants"))]
393            AnimatedData::Point3Vec(map) => map.get(&time).map(|v| Data::Point3Vec(v.clone())),
394            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
395            AnimatedData::Matrix4Vec(map) => map.get(&time).map(|v| Data::Matrix4Vec(v.clone())),
396        }
397    }
398
399    pub fn interpolate(&self, time: Time) -> Data {
400        match self {
401            AnimatedData::Boolean(map) => Data::Boolean(map.closest_sample(time).clone()),
402            AnimatedData::Integer(map) => {
403                if TimeDataMapControl::is_animated(map) {
404                    Data::Integer(map.interpolate(time))
405                } else {
406                    Data::Integer(map.iter().next().unwrap().1.clone())
407                }
408            }
409            AnimatedData::Real(map) => {
410                if TimeDataMapControl::is_animated(map) {
411                    Data::Real(map.interpolate(time))
412                } else {
413                    Data::Real(map.iter().next().unwrap().1.clone())
414                }
415            }
416            AnimatedData::String(map) => Data::String(map.closest_sample(time).clone()),
417            AnimatedData::Color(map) => {
418                if TimeDataMapControl::is_animated(map) {
419                    Data::Color(map.interpolate(time))
420                } else {
421                    Data::Color(map.iter().next().unwrap().1.clone())
422                }
423            }
424            #[cfg(feature = "vector2")]
425            AnimatedData::Vector2(map) => {
426                if TimeDataMapControl::is_animated(map) {
427                    Data::Vector2(map.interpolate(time))
428                } else {
429                    Data::Vector2(map.iter().next().unwrap().1.clone())
430                }
431            }
432            #[cfg(feature = "vector3")]
433            AnimatedData::Vector3(map) => {
434                if TimeDataMapControl::is_animated(map) {
435                    Data::Vector3(map.interpolate(time))
436                } else {
437                    Data::Vector3(map.iter().next().unwrap().1.clone())
438                }
439            }
440            #[cfg(feature = "matrix3")]
441            AnimatedData::Matrix3(map) => {
442                if TimeDataMapControl::is_animated(map) {
443                    Data::Matrix3(map.interpolate(time))
444                } else {
445                    Data::Matrix3(map.iter().next().unwrap().1.clone())
446                }
447            }
448            #[cfg(feature = "normal3")]
449            AnimatedData::Normal3(map) => {
450                if TimeDataMapControl::is_animated(map) {
451                    Data::Normal3(map.interpolate(time))
452                } else {
453                    Data::Normal3(map.iter().next().unwrap().1.clone())
454                }
455            }
456            #[cfg(feature = "point3")]
457            AnimatedData::Point3(map) => {
458                if TimeDataMapControl::is_animated(map) {
459                    Data::Point3(map.interpolate(time))
460                } else {
461                    Data::Point3(map.iter().next().unwrap().1.clone())
462                }
463            }
464            #[cfg(feature = "matrix4")]
465            AnimatedData::Matrix4(map) => {
466                if TimeDataMapControl::is_animated(map) {
467                    Data::Matrix4(map.interpolate(time))
468                } else {
469                    Data::Matrix4(map.iter().next().unwrap().1.clone())
470                }
471            }
472            AnimatedData::BooleanVec(map) => Data::BooleanVec(map.closest_sample(time).clone()),
473            AnimatedData::IntegerVec(map) => {
474                if TimeDataMapControl::is_animated(map) {
475                    Data::IntegerVec(map.interpolate(time))
476                } else {
477                    Data::IntegerVec(map.iter().next().unwrap().1.clone())
478                }
479            }
480            AnimatedData::RealVec(map) => {
481                if TimeDataMapControl::is_animated(map) {
482                    Data::RealVec(map.interpolate(time))
483                } else {
484                    Data::RealVec(map.iter().next().unwrap().1.clone())
485                }
486            }
487            AnimatedData::ColorVec(map) => {
488                if TimeDataMapControl::is_animated(map) {
489                    Data::ColorVec(map.interpolate(time))
490                } else {
491                    Data::ColorVec(map.iter().next().unwrap().1.clone())
492                }
493            }
494            AnimatedData::StringVec(map) => Data::StringVec(map.closest_sample(time).clone()),
495            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
496            AnimatedData::Vector2Vec(map) => {
497                if TimeDataMapControl::is_animated(map) {
498                    Data::Vector2Vec(map.interpolate(time))
499                } else {
500                    Data::Vector2Vec(map.iter().next().unwrap().1.clone())
501                }
502            }
503            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
504            AnimatedData::Vector3Vec(map) => {
505                if TimeDataMapControl::is_animated(map) {
506                    Data::Vector3Vec(map.interpolate(time))
507                } else {
508                    Data::Vector3Vec(map.iter().next().unwrap().1.clone())
509                }
510            }
511            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
512            AnimatedData::Matrix3Vec(map) => {
513                if TimeDataMapControl::is_animated(map) {
514                    Data::Matrix3Vec(map.interpolate(time))
515                } else {
516                    Data::Matrix3Vec(map.iter().next().unwrap().1.clone())
517                }
518            }
519            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
520            AnimatedData::Normal3Vec(map) => {
521                if TimeDataMapControl::is_animated(map) {
522                    Data::Normal3Vec(map.interpolate(time))
523                } else {
524                    Data::Normal3Vec(map.iter().next().unwrap().1.clone())
525                }
526            }
527            #[cfg(all(feature = "point3", feature = "vec_variants"))]
528            AnimatedData::Point3Vec(map) => {
529                if TimeDataMapControl::is_animated(map) {
530                    Data::Point3Vec(map.interpolate(time))
531                } else {
532                    Data::Point3Vec(map.iter().next().unwrap().1.clone())
533                }
534            }
535            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
536            AnimatedData::Matrix4Vec(map) => {
537                if TimeDataMapControl::is_animated(map) {
538                    Data::Matrix4Vec(map.interpolate(time))
539                } else {
540                    Data::Matrix4Vec(map.iter().next().unwrap().1.clone())
541                }
542            }
543        }
544    }
545}
546
547impl Hash for AnimatedData {
548    fn hash<H: Hasher>(&self, state: &mut H) {
549        std::mem::discriminant(self).hash(state);
550        match self {
551            AnimatedData::Boolean(map) => {
552                map.len().hash(state);
553                #[cfg(not(feature = "interpolation"))]
554                for (time, value) in &map.values {
555                    time.hash(state);
556                    value.0.hash(state);
557                }
558                #[cfg(feature = "interpolation")]
559                for (time, (value, spec)) in &map.values {
560                    time.hash(state);
561                    value.0.hash(state);
562                    spec.hash(state);
563                }
564            }
565            AnimatedData::Integer(map) => {
566                map.len().hash(state);
567                #[cfg(not(feature = "interpolation"))]
568                for (time, value) in &map.values {
569                    time.hash(state);
570                    value.0.hash(state);
571                }
572                #[cfg(feature = "interpolation")]
573                for (time, (value, spec)) in &map.values {
574                    time.hash(state);
575                    value.0.hash(state);
576                    spec.hash(state);
577                }
578            }
579            AnimatedData::Real(map) => {
580                map.len().hash(state);
581                #[cfg(not(feature = "interpolation"))]
582                for (time, value) in &map.values {
583                    time.hash(state);
584                    value.0.to_bits().hash(state);
585                }
586                #[cfg(feature = "interpolation")]
587                for (time, (value, spec)) in &map.values {
588                    time.hash(state);
589                    value.0.to_bits().hash(state);
590                    spec.hash(state);
591                }
592            }
593            AnimatedData::String(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::Color(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.iter().for_each(|v| v.to_bits().hash(state));
613                }
614                #[cfg(feature = "interpolation")]
615                for (time, (value, spec)) in &map.values {
616                    time.hash(state);
617                    value.0.iter().for_each(|v| v.to_bits().hash(state));
618                    spec.hash(state);
619                }
620            }
621            #[cfg(feature = "vector2")]
622            AnimatedData::Vector2(map) => {
623                map.len().hash(state);
624                #[cfg(not(feature = "interpolation"))]
625                for (time, value) in &map.values {
626                    time.hash(state);
627                    value.0.iter().for_each(|v| v.to_bits().hash(state));
628                }
629                #[cfg(feature = "interpolation")]
630                for (time, (value, spec)) in &map.values {
631                    time.hash(state);
632                    value.0.iter().for_each(|v| v.to_bits().hash(state));
633                    spec.hash(state);
634                }
635            }
636            #[cfg(feature = "vector3")]
637            AnimatedData::Vector3(map) => {
638                map.len().hash(state);
639                #[cfg(not(feature = "interpolation"))]
640                for (time, value) in &map.values {
641                    time.hash(state);
642                    value.0.iter().for_each(|v| v.to_bits().hash(state));
643                }
644                #[cfg(feature = "interpolation")]
645                for (time, (value, spec)) in &map.values {
646                    time.hash(state);
647                    value.0.iter().for_each(|v| v.to_bits().hash(state));
648                    spec.hash(state);
649                }
650            }
651            #[cfg(feature = "matrix3")]
652            AnimatedData::Matrix3(map) => {
653                map.len().hash(state);
654                #[cfg(not(feature = "interpolation"))]
655                for (time, value) in &map.values {
656                    time.hash(state);
657                    value.0.iter().for_each(|v| v.to_bits().hash(state));
658                }
659                #[cfg(feature = "interpolation")]
660                for (time, (value, spec)) in &map.values {
661                    time.hash(state);
662                    value.0.iter().for_each(|v| v.to_bits().hash(state));
663                    spec.hash(state);
664                }
665            }
666            #[cfg(feature = "normal3")]
667            AnimatedData::Normal3(map) => {
668                map.len().hash(state);
669                #[cfg(not(feature = "interpolation"))]
670                for (time, value) in &map.values {
671                    time.hash(state);
672                    value.0.iter().for_each(|v| v.to_bits().hash(state));
673                }
674                #[cfg(feature = "interpolation")]
675                for (time, (value, spec)) in &map.values {
676                    time.hash(state);
677                    value.0.iter().for_each(|v| v.to_bits().hash(state));
678                    spec.hash(state);
679                }
680            }
681            #[cfg(feature = "point3")]
682            AnimatedData::Point3(map) => {
683                map.len().hash(state);
684                #[cfg(not(feature = "interpolation"))]
685                for (time, value) in &map.values {
686                    time.hash(state);
687                    value.0.coords.iter().for_each(|v| v.to_bits().hash(state));
688                }
689                #[cfg(feature = "interpolation")]
690                for (time, (value, spec)) in &map.values {
691                    time.hash(state);
692                    value.0.coords.iter().for_each(|v| v.to_bits().hash(state));
693                    spec.hash(state);
694                }
695            }
696            #[cfg(feature = "matrix4")]
697            AnimatedData::Matrix4(map) => {
698                map.len().hash(state);
699                #[cfg(not(feature = "interpolation"))]
700                for (time, value) in &map.values {
701                    time.hash(state);
702                    value.0.iter().for_each(|v| v.to_bits().hash(state));
703                }
704                #[cfg(feature = "interpolation")]
705                for (time, (value, spec)) in &map.values {
706                    time.hash(state);
707                    value.0.iter().for_each(|v| v.to_bits().hash(state));
708                    spec.hash(state);
709                }
710            }
711            AnimatedData::BooleanVec(map) => {
712                map.len().hash(state);
713                #[cfg(not(feature = "interpolation"))]
714                for (time, value) in &map.values {
715                    time.hash(state);
716                    value.0.hash(state);
717                }
718                #[cfg(feature = "interpolation")]
719                for (time, (value, spec)) in &map.values {
720                    time.hash(state);
721                    value.0.hash(state);
722                    spec.hash(state);
723                }
724            }
725            AnimatedData::IntegerVec(map) => {
726                map.len().hash(state);
727                #[cfg(not(feature = "interpolation"))]
728                for (time, value) in &map.values {
729                    time.hash(state);
730                    value.0.hash(state);
731                }
732                #[cfg(feature = "interpolation")]
733                for (time, (value, spec)) in &map.values {
734                    time.hash(state);
735                    value.0.hash(state);
736                    spec.hash(state);
737                }
738            }
739            AnimatedData::RealVec(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.len().hash(state);
745                    value.0.iter().for_each(|v| v.to_bits().hash(state));
746                }
747                #[cfg(feature = "interpolation")]
748                for (time, (value, spec)) in &map.values {
749                    time.hash(state);
750                    value.0.len().hash(state);
751                    value.0.iter().for_each(|v| v.to_bits().hash(state));
752                    spec.hash(state);
753                }
754            }
755            AnimatedData::ColorVec(map) => {
756                map.len().hash(state);
757                #[cfg(not(feature = "interpolation"))]
758                for (time, value) in &map.values {
759                    time.hash(state);
760                    value.0.len().hash(state);
761                    value.0.iter().for_each(|c| {
762                        c.iter().for_each(|v| v.to_bits().hash(state));
763                    });
764                }
765                #[cfg(feature = "interpolation")]
766                for (time, (value, spec)) in &map.values {
767                    time.hash(state);
768                    value.0.len().hash(state);
769                    value.0.iter().for_each(|c| {
770                        c.iter().for_each(|v| v.to_bits().hash(state));
771                    });
772                    spec.hash(state);
773                }
774            }
775            AnimatedData::StringVec(map) => {
776                map.len().hash(state);
777                #[cfg(not(feature = "interpolation"))]
778                for (time, value) in &map.values {
779                    time.hash(state);
780                    value.0.hash(state);
781                }
782                #[cfg(feature = "interpolation")]
783                for (time, (value, spec)) in &map.values {
784                    time.hash(state);
785                    value.0.hash(state);
786                    spec.hash(state);
787                }
788            }
789            #[cfg(all(feature = "vector2", feature = "vec_variants"))]
790            AnimatedData::Vector2Vec(map) => {
791                map.len().hash(state);
792                #[cfg(not(feature = "interpolation"))]
793                for (time, value) in &map.values {
794                    time.hash(state);
795                    value.0.len().hash(state);
796                    value.0.iter().for_each(|v| {
797                        v.iter().for_each(|f| f.to_bits().hash(state));
798                    });
799                }
800                #[cfg(feature = "interpolation")]
801                for (time, (value, spec)) in &map.values {
802                    time.hash(state);
803                    value.0.len().hash(state);
804                    value.0.iter().for_each(|v| {
805                        v.iter().for_each(|f| f.to_bits().hash(state));
806                    });
807                    spec.hash(state);
808                }
809            }
810            #[cfg(all(feature = "vector3", feature = "vec_variants"))]
811            AnimatedData::Vector3Vec(map) => {
812                map.len().hash(state);
813                #[cfg(not(feature = "interpolation"))]
814                for (time, value) in &map.values {
815                    time.hash(state);
816                    value.0.len().hash(state);
817                    value.0.iter().for_each(|v| {
818                        v.iter().for_each(|f| f.to_bits().hash(state));
819                    });
820                }
821                #[cfg(feature = "interpolation")]
822                for (time, (value, spec)) in &map.values {
823                    time.hash(state);
824                    value.0.len().hash(state);
825                    value.0.iter().for_each(|v| {
826                        v.iter().for_each(|f| f.to_bits().hash(state));
827                    });
828                    spec.hash(state);
829                }
830            }
831            #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
832            AnimatedData::Matrix3Vec(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(|m| {
839                        m.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(|m| {
847                        m.iter().for_each(|f| f.to_bits().hash(state));
848                    });
849                    spec.hash(state);
850                }
851            }
852            #[cfg(all(feature = "normal3", feature = "vec_variants"))]
853            AnimatedData::Normal3Vec(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 = "point3", feature = "vec_variants"))]
874            AnimatedData::Point3Vec(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(|p| {
881                        p.coords.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(|p| {
889                        p.coords.iter().for_each(|f| f.to_bits().hash(state));
890                    });
891                    spec.hash(state);
892                }
893            }
894            #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
895            AnimatedData::Matrix4Vec(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(|m| {
902                        m.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(|m| {
910                        m.iter().for_each(|f| f.to_bits().hash(state));
911                    });
912                    spec.hash(state);
913                }
914            }
915        }
916    }
917}
918
919// Sample trait implementations for AnimatedData
920impl_sample_for_animated_data!(
921    Real, Real;
922    Integer, Integer;
923    Color, Color;
924);
925
926impl_sample_for_animated_data!(
927    Vector2, Vector2, "vector2";
928    Vector3, Vector3, "vector3";
929    Matrix3, Matrix3, "matrix3";
930    Normal3, Normal3, "normal3";
931    Point3, Point3, "point3";
932    Matrix4, Matrix4, "matrix4";
933);
934
935impl AnimatedData {
936    /// Hash the animated data with shutter context for better cache coherency.
937    ///
938    /// Samples the animation at standardized points within the shutter range
939    /// and hashes the interpolated values. If all samples are identical,
940    /// only one value is hashed for efficiency.
941    pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
942        use smallvec::SmallVec;
943
944        // Sample at 5 standardized points within the shutter.
945        const SAMPLE_POSITIONS: [f32; 5] = [0.0, 0.25, 0.5, 0.75, 1.0];
946
947        // Collect interpolated samples using SmallVec to avoid heap allocation.
948        let samples: SmallVec<[Data; 5]> = SAMPLE_POSITIONS
949            .iter()
950            .map(|&pos| {
951                let time = shutter.evaluate(pos);
952                self.interpolate(time)
953            })
954            .collect();
955
956        // Check if all samples are identical.
957        let all_same = samples.windows(2).all(|w| w[0] == w[1]);
958
959        // Hash the data type discriminant.
960        std::mem::discriminant(self).hash(state);
961
962        if all_same {
963            // If all samples are the same, just hash one.
964            1usize.hash(state); // Indicate single sample.
965            samples[0].hash(state);
966        } else {
967            // Hash all samples.
968            samples.len().hash(state); // Indicate multiple samples.
969            for sample in &samples {
970                sample.hash(state);
971            }
972        }
973    }
974}