Skip to main content

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