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}