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