Skip to main content

token_value_map/
traits.rs

1//! Core traits for generic data systems.
2//!
3//! This module defines the foundational traits that allow you to define your
4//! own data type systems. By implementing these traits, you can replace the
5//! built-in [`Data`](crate::Data) and [`AnimatedData`](crate::AnimatedData)
6//! types entirely, or extend them with custom types.
7//!
8//! # Overview
9//!
10//! The trait hierarchy is:
11//! - [`Interpolatable`] -- Types that can be interpolated over time.
12//! - [`DataSystem`] -- A complete enum of data variants (like [`Data`](crate::Data)).
13//! - [`AnimatedDataSystem`] -- Container for animated data (like [`AnimatedData`](crate::AnimatedData)).
14//!
15//! # Example
16//!
17//! ```ignore
18//! use token_value_map::{DataSystem, AnimatedDataSystem, GenericValue};
19//!
20//! // Define your own data enum.
21//! #[derive(Clone, Debug, PartialEq, Eq, Hash)]
22//! pub enum MyData {
23//!     Float(f64),
24//!     Quat(MyQuaternion),
25//! }
26//!
27//! // Implement the traits.
28//! impl DataSystem for MyData { /* ... */ }
29//!
30//! // Use with generic types.
31//! type MyValue = GenericValue<MyData>;
32//! ```
33
34use smallvec::SmallVec;
35use std::{
36    fmt::Debug,
37    hash::Hash,
38    ops::{Add, Div, Mul, Sub},
39};
40
41use crate::{Result, Time};
42
43/// Trait for converting data types to f32 for curve editing.
44///
45/// Implement this trait on your data enum to enable egui-keyframe integration
46/// with [`GenericValue`](crate::GenericValue). Return `None` for types that
47/// cannot be displayed as scalar curves (e.g., strings, colors, transforms).
48///
49/// # Example
50///
51/// ```ignore
52/// impl ToF32 for MyData {
53///     fn to_f32(&self) -> Option<f32> {
54///         match self {
55///             MyData::Float(f) => Some(*f),
56///             MyData::Vec3(_) => None, // Can't display as single curve.
57///         }
58///     }
59/// }
60/// ```
61pub trait ToF32 {
62    /// Convert the data value to f32 for curve display.
63    ///
64    /// Returns `None` for non-numeric types.
65    fn to_f32(&self) -> Option<f32>;
66}
67
68/// Marker trait for types that support time-based interpolation.
69///
70/// Types implementing this trait can be used with [`TimeDataMap`](crate::TimeDataMap)
71/// for automatic interpolation between keyframes.
72///
73/// # Required Bounds
74///
75/// - `Clone` -- Values must be cloneable for interpolation.
76/// - `Add`, `Sub` -- Vector arithmetic for blending.
77/// - `Mul<f32>`, `Mul<f64>` -- Scalar multiplication for weighting.
78/// - `Div<f32>`, `Div<f64>` -- Scalar division for normalization.
79/// - `PartialEq` -- Equality comparison for optimization.
80/// - `Send + Sync + 'static` -- Thread safety for parallel operations.
81pub trait Interpolatable:
82    Clone
83    + Add<Output = Self>
84    + Sub<Output = Self>
85    + Mul<f32, Output = Self>
86    + Mul<f64, Output = Self>
87    + Div<f32, Output = Self>
88    + Div<f64, Output = Self>
89    + PartialEq
90    + Send
91    + Sync
92    + 'static
93{
94}
95
96// Blanket implementation for any type meeting the bounds.
97impl<T> Interpolatable for T where
98    T: Clone
99        + Add<Output = T>
100        + Sub<Output = T>
101        + Mul<f32, Output = T>
102        + Mul<f64, Output = T>
103        + Div<f32, Output = T>
104        + Div<f64, Output = T>
105        + PartialEq
106        + Send
107        + Sync
108        + 'static
109{
110}
111
112/// A complete data type system.
113///
114/// This trait defines the contract for a "data enum" -- a type that can hold
115/// any of several different value types (scalars, vectors, matrices, etc.).
116///
117/// The built-in [`Data`](crate::Data) type implements this trait. You can
118/// define your own enums and implement this trait to create custom type systems.
119///
120/// # Associated Types
121///
122/// - `Animated` -- The corresponding animated data container type.
123/// - `DataType` -- A discriminant enum for identifying variants.
124///
125/// # Example
126///
127/// ```ignore
128/// impl DataSystem for MyData {
129///     type Animated = MyAnimatedData;
130///     type DataType = MyDataType;
131///
132///     fn data_type(&self) -> MyDataType {
133///         match self {
134///             MyData::Float(_) => MyDataType::Float,
135///             MyData::Quat(_) => MyDataType::Quat,
136///         }
137///     }
138///
139///     fn type_name(&self) -> &'static str {
140///         match self {
141///             MyData::Float(_) => "float",
142///             MyData::Quat(_) => "quat",
143///         }
144///     }
145/// }
146/// ```
147pub trait DataSystem: Clone + Debug + PartialEq + Eq + Hash + Send + Sync + 'static {
148    /// The animated data container type for this system.
149    type Animated: AnimatedDataSystem<Data = Self>;
150
151    /// The discriminant type for identifying variants.
152    type DataType: Clone + Copy + Debug + PartialEq + Eq + Hash + Send + Sync;
153
154    /// Returns the discriminant for this value.
155    ///
156    /// Named `discriminant()` to avoid conflict with [`DataTypeOps::data_type()`](crate::DataTypeOps::data_type).
157    fn discriminant(&self) -> Self::DataType;
158
159    /// Returns a human-readable type name for this value.
160    ///
161    /// Named `variant_name()` to avoid conflict with [`DataTypeOps::type_name()`](crate::DataTypeOps::type_name).
162    fn variant_name(&self) -> &'static str;
163
164    /// Returns the length if this is a vector type, `None` for scalars.
165    ///
166    /// Override this for data systems that support vector types.
167    fn try_len(&self) -> Option<usize> {
168        None
169    }
170
171    /// Pads a vector value to the target length.
172    ///
173    /// Override this for data systems that support vector types.
174    /// Does nothing by default.
175    fn pad_to_length(&mut self, _target_len: usize) {}
176}
177
178/// Animated data container for a [`DataSystem`].
179///
180/// This trait defines the contract for storing and interpolating time-varying
181/// data. The built-in [`AnimatedData`](crate::AnimatedData) type implements
182/// this trait.
183///
184/// # Associated Types
185///
186/// - `Data` -- The corresponding data system type.
187///
188/// # Implementation Notes
189///
190/// Implementations typically use [`TimeDataMap<T>`](crate::TimeDataMap) internally
191/// to store keyframes for each data type variant.
192pub trait AnimatedDataSystem:
193    Clone + Debug + PartialEq + Eq + Hash + Send + Sync + 'static
194{
195    /// The data system type that this animates.
196    type Data: DataSystem<Animated = Self>;
197
198    /// Returns the number of keyframes.
199    ///
200    /// Named `keyframe_count()` to avoid conflict with [`AnimatedDataOps::len()`](crate::AnimatedDataOps::len).
201    fn keyframe_count(&self) -> usize;
202
203    /// Returns `true` if there are no keyframes.
204    fn is_keyframes_empty(&self) -> bool {
205        self.keyframe_count() == 0
206    }
207
208    /// Returns `true` if there is more than one keyframe.
209    ///
210    /// Named `has_animation()` to avoid conflict with [`AnimatedDataOps::is_animated()`](crate::AnimatedDataOps::is_animated).
211    fn has_animation(&self) -> bool {
212        self.keyframe_count() > 1
213    }
214
215    /// Returns all keyframe times.
216    fn times(&self) -> SmallVec<[Time; 10]>;
217
218    /// Interpolates the value at the given time.
219    fn interpolate(&self, time: Time) -> Self::Data;
220
221    /// Returns the exact sample at a time, or `None` if no keyframe exists.
222    fn sample_at(&self, time: Time) -> Option<Self::Data>;
223
224    /// Inserts a value at the given time, checking type compatibility.
225    fn try_insert(&mut self, time: Time, value: Self::Data) -> Result<()>;
226
227    /// Removes the keyframe at the given time.
228    ///
229    /// Returns the removed value if it existed. Returns `None` if the key
230    /// was not found or if it was the last sample (the non-empty invariant
231    /// prevents removing it).
232    fn remove_at(&mut self, time: &Time) -> Option<Self::Data>;
233
234    /// Returns the data type discriminant for this animated data.
235    ///
236    /// Named `discriminant()` to avoid conflict with [`DataTypeOps::data_type()`](crate::DataTypeOps::data_type).
237    fn discriminant(&self) -> <Self::Data as DataSystem>::DataType;
238
239    /// Creates animated data from a single time-value pair.
240    fn from_single(time: Time, value: Self::Data) -> Self;
241
242    /// Returns the type name for this animated data.
243    ///
244    /// Named `variant_name()` to avoid conflict with [`DataTypeOps::type_name()`](crate::DataTypeOps::type_name).
245    fn variant_name(&self) -> &'static str;
246}