1use crate::*;
2use core::num::NonZeroU16;
3use std::hash::{Hash, Hasher};
4
5type BracketSample = (Option<(Time, Data)>, Option<(Time, Data)>);
7
8#[derive(Clone, Debug, PartialEq, Hash)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(feature = "facet", derive(Facet))]
15#[cfg_attr(feature = "facet", facet(opaque))]
16#[cfg_attr(feature = "facet", repr(u8))]
17#[cfg_attr(feature = "rkyv", derive(Archive, RkyvSerialize, RkyvDeserialize))]
18pub enum Value {
19 Uniform(Data),
21 Animated(AnimatedData),
23}
24
25impl Value {
26 pub fn uniform<V: Into<Data>>(value: V) -> Self {
28 Value::Uniform(value.into())
29 }
30
31 pub fn animated<I, V>(samples: I) -> Result<Self>
36 where
37 I: IntoIterator<Item = (Time, V)>,
38 V: Into<Data>,
39 {
40 let mut samples_vec: Vec<(Time, Data)> =
41 samples.into_iter().map(|(t, v)| (t, v.into())).collect();
42
43 if samples_vec.is_empty() {
44 return Err(Error::EmptySamples);
45 }
46
47 let data_type = samples_vec[0].1.data_type();
49
50 let mut expected_len: Option<usize> = None;
52 for (time, value) in &mut samples_vec {
53 if value.data_type() != data_type {
54 return Err(Error::AnimatedTypeMismatch {
55 expected: data_type,
56 got: value.data_type(),
57 time: *time,
58 });
59 }
60
61 if let Some(vec_len) = value.try_len() {
63 match expected_len {
64 None => expected_len = Some(vec_len),
65 Some(expected) => {
66 if vec_len > expected {
67 return Err(Error::VectorLengthExceeded {
68 actual: vec_len,
69 expected,
70 time: *time,
71 });
72 } else if vec_len < expected {
73 value.pad_to_length(expected);
75 }
76 }
77 }
78 }
79 }
80
81 let animated_data = match data_type {
85 DataType::Boolean => {
86 let typed_samples: Vec<(Time, Boolean)> = samples_vec
87 .into_iter()
88 .map(|(t, data)| match data {
89 Data::Boolean(v) => (t, v),
90 _ => unreachable!("Type validation should have caught this"),
91 })
92 .collect();
93 AnimatedData::Boolean(TimeDataMap::from_iter(typed_samples))
94 }
95 DataType::Integer => {
96 let typed_samples: Vec<(Time, Integer)> = samples_vec
97 .into_iter()
98 .map(|(t, data)| match data {
99 Data::Integer(v) => (t, v),
100 _ => unreachable!("Type validation should have caught this"),
101 })
102 .collect();
103 AnimatedData::Integer(TimeDataMap::from_iter(typed_samples))
104 }
105 DataType::Real => {
106 let typed_samples: Vec<(Time, Real)> = samples_vec
107 .into_iter()
108 .map(|(t, data)| match data {
109 Data::Real(v) => (t, v),
110 _ => unreachable!("Type validation should have caught this"),
111 })
112 .collect();
113 AnimatedData::Real(TimeDataMap::from_iter(typed_samples))
114 }
115 DataType::String => {
116 let typed_samples: Vec<(Time, String)> = samples_vec
117 .into_iter()
118 .map(|(t, data)| match data {
119 Data::String(v) => (t, v),
120 _ => unreachable!("Type validation should have caught this"),
121 })
122 .collect();
123 AnimatedData::String(TimeDataMap::from_iter(typed_samples))
124 }
125 DataType::Color => {
126 let typed_samples: Vec<(Time, Color)> = samples_vec
127 .into_iter()
128 .map(|(t, data)| match data {
129 Data::Color(v) => (t, v),
130 _ => unreachable!("Type validation should have caught this"),
131 })
132 .collect();
133 AnimatedData::Color(TimeDataMap::from_iter(typed_samples))
134 }
135 #[cfg(feature = "vector2")]
136 DataType::Vector2 => {
137 let typed_samples: Vec<(Time, Vector2)> = samples_vec
138 .into_iter()
139 .map(|(t, data)| match data {
140 Data::Vector2(v) => (t, v),
141 _ => unreachable!("Type validation should have caught this"),
142 })
143 .collect();
144 AnimatedData::Vector2(TimeDataMap::from_iter(typed_samples))
145 }
146 #[cfg(feature = "vector3")]
147 DataType::Vector3 => {
148 let typed_samples: Vec<(Time, Vector3)> = samples_vec
149 .into_iter()
150 .map(|(t, data)| match data {
151 Data::Vector3(v) => (t, v),
152 _ => unreachable!("Type validation should have caught this"),
153 })
154 .collect();
155 AnimatedData::Vector3(TimeDataMap::from_iter(typed_samples))
156 }
157 #[cfg(feature = "matrix3")]
158 DataType::Matrix3 => {
159 let typed_samples: Vec<(Time, Matrix3)> = samples_vec
160 .into_iter()
161 .map(|(t, data)| match data {
162 Data::Matrix3(v) => (t, v),
163 _ => unreachable!("Type validation should have caught this"),
164 })
165 .collect();
166 AnimatedData::Matrix3(TimeDataMap::from_iter(typed_samples))
167 }
168 #[cfg(feature = "normal3")]
169 DataType::Normal3 => {
170 let typed_samples: Vec<(Time, Normal3)> = samples_vec
171 .into_iter()
172 .map(|(t, data)| match data {
173 Data::Normal3(v) => (t, v),
174 _ => unreachable!("Type validation should have caught this"),
175 })
176 .collect();
177 AnimatedData::Normal3(TimeDataMap::from_iter(typed_samples))
178 }
179 #[cfg(feature = "point3")]
180 DataType::Point3 => {
181 let typed_samples: Vec<(Time, Point3)> = samples_vec
182 .into_iter()
183 .map(|(t, data)| match data {
184 Data::Point3(v) => (t, v),
185 _ => unreachable!("Type validation should have caught this"),
186 })
187 .collect();
188 AnimatedData::Point3(TimeDataMap::from_iter(typed_samples))
189 }
190 #[cfg(feature = "matrix4")]
191 DataType::Matrix4 => {
192 let typed_samples: Vec<(Time, Matrix4)> = samples_vec
193 .into_iter()
194 .map(|(t, data)| match data {
195 Data::Matrix4(v) => (t, v),
196 _ => unreachable!("Type validation should have caught this"),
197 })
198 .collect();
199 AnimatedData::Matrix4(TimeDataMap::from_iter(typed_samples))
200 }
201 DataType::BooleanVec => {
202 let typed_samples: Vec<(Time, BooleanVec)> = samples_vec
203 .into_iter()
204 .map(|(t, data)| match data {
205 Data::BooleanVec(v) => (t, v),
206 _ => unreachable!("Type validation should have caught this"),
207 })
208 .collect();
209 AnimatedData::BooleanVec(TimeDataMap::from_iter(typed_samples))
210 }
211 DataType::IntegerVec => {
212 let typed_samples: Vec<(Time, IntegerVec)> = samples_vec
213 .into_iter()
214 .map(|(t, data)| match data {
215 Data::IntegerVec(v) => (t, v),
216 _ => unreachable!("Type validation should have caught this"),
217 })
218 .collect();
219 AnimatedData::IntegerVec(TimeDataMap::from_iter(typed_samples))
220 }
221 DataType::RealVec => {
222 let typed_samples: Vec<(Time, RealVec)> = samples_vec
223 .into_iter()
224 .map(|(t, data)| match data {
225 Data::RealVec(v) => (t, v),
226 _ => unreachable!("Type validation should have caught this"),
227 })
228 .collect();
229 AnimatedData::RealVec(TimeDataMap::from_iter(typed_samples))
230 }
231 DataType::ColorVec => {
232 let typed_samples: Vec<(Time, ColorVec)> = samples_vec
233 .into_iter()
234 .map(|(t, data)| match data {
235 Data::ColorVec(v) => (t, v),
236 _ => unreachable!("Type validation should have caught this"),
237 })
238 .collect();
239 AnimatedData::ColorVec(TimeDataMap::from_iter(typed_samples))
240 }
241 DataType::StringVec => {
242 let typed_samples: Vec<(Time, StringVec)> = samples_vec
243 .into_iter()
244 .map(|(t, data)| match data {
245 Data::StringVec(v) => (t, v),
246 _ => unreachable!("Type validation should have caught this"),
247 })
248 .collect();
249 AnimatedData::StringVec(TimeDataMap::from_iter(typed_samples))
250 }
251 #[cfg(all(feature = "vector2", feature = "vec_variants"))]
252 DataType::Vector2Vec => {
253 let typed_samples: Vec<(Time, Vector2Vec)> = samples_vec
254 .into_iter()
255 .map(|(t, data)| match data {
256 Data::Vector2Vec(v) => (t, v),
257 _ => unreachable!("Type validation should have caught this"),
258 })
259 .collect();
260 AnimatedData::Vector2Vec(TimeDataMap::from_iter(typed_samples))
261 }
262 #[cfg(all(feature = "vector3", feature = "vec_variants"))]
263 DataType::Vector3Vec => {
264 let typed_samples: Vec<(Time, Vector3Vec)> = samples_vec
265 .into_iter()
266 .map(|(t, data)| match data {
267 Data::Vector3Vec(v) => (t, v),
268 _ => unreachable!("Type validation should have caught this"),
269 })
270 .collect();
271 AnimatedData::Vector3Vec(TimeDataMap::from_iter(typed_samples))
272 }
273 #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
274 DataType::Matrix3Vec => {
275 let typed_samples: Vec<(Time, Matrix3Vec)> = samples_vec
276 .into_iter()
277 .map(|(t, data)| match data {
278 Data::Matrix3Vec(v) => (t, v),
279 _ => unreachable!("Type validation should have caught this"),
280 })
281 .collect();
282 AnimatedData::Matrix3Vec(TimeDataMap::from_iter(typed_samples))
283 }
284 #[cfg(all(feature = "normal3", feature = "vec_variants"))]
285 DataType::Normal3Vec => {
286 let typed_samples: Vec<(Time, Normal3Vec)> = samples_vec
287 .into_iter()
288 .map(|(t, data)| match data {
289 Data::Normal3Vec(v) => (t, v),
290 _ => unreachable!("Type validation should have caught this"),
291 })
292 .collect();
293 AnimatedData::Normal3Vec(TimeDataMap::from_iter(typed_samples))
294 }
295 #[cfg(all(feature = "point3", feature = "vec_variants"))]
296 DataType::Point3Vec => {
297 let typed_samples: Vec<(Time, Point3Vec)> = samples_vec
298 .into_iter()
299 .map(|(t, data)| match data {
300 Data::Point3Vec(v) => (t, v),
301 _ => unreachable!("Type validation should have caught this"),
302 })
303 .collect();
304 AnimatedData::Point3Vec(TimeDataMap::from_iter(typed_samples))
305 }
306 #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
307 DataType::Matrix4Vec => {
308 let typed_samples: Vec<(Time, Matrix4Vec)> = samples_vec
309 .into_iter()
310 .map(|(t, data)| match data {
311 Data::Matrix4Vec(v) => (t, v),
312 _ => unreachable!("Type validation should have caught this"),
313 })
314 .collect();
315 AnimatedData::Matrix4Vec(TimeDataMap::from_iter(typed_samples))
316 }
317 };
318
319 Ok(Value::Animated(animated_data))
320 }
321
322 pub fn add_sample<V: Into<Data>>(&mut self, time: Time, val: V) -> Result<()> {
324 let value = val.into();
325
326 match self {
327 Value::Uniform(_uniform_value) => {
328 *self = Value::animated(vec![(time, value)])?;
332 Ok(())
333 }
334 Value::Animated(samples) => {
335 let data_type = samples.data_type();
336 if value.data_type() != data_type {
337 return Err(Error::SampleTypeMismatch {
338 expected: data_type,
339 got: value.data_type(),
340 });
341 }
342
343 samples.try_insert(time, value)
345 }
346 }
347 }
348
349 pub fn remove_sample(&mut self, time: &Time) -> Option<Data> {
355 match self {
356 Value::Uniform(_) => None,
357 Value::Animated(samples) => samples.remove_at(time),
358 }
359 }
360
361 pub fn sample_at(&self, time: Time) -> Option<Data> {
366 match self {
367 Value::Uniform(v) => Some(v.clone()),
368 Value::Animated(samples) => samples.sample_at(time),
369 }
370 }
371
372 pub fn sample_at_or_before(&self, time: Time) -> Option<Data> {
374 match self {
375 Value::Uniform(v) => Some(v.clone()),
376 Value::Animated(_samples) => {
377 Some(self.interpolate(time))
380 }
381 }
382 }
383
384 pub fn sample_at_or_after(&self, time: Time) -> Option<Data> {
386 match self {
387 Value::Uniform(v) => Some(v.clone()),
388 Value::Animated(_samples) => {
389 Some(self.interpolate(time))
392 }
393 }
394 }
395
396 pub fn interpolate(&self, time: Time) -> Data {
402 match self {
403 Value::Uniform(v) => v.clone(),
404 Value::Animated(samples) => samples.interpolate(time),
405 }
406 }
407
408 pub fn sample_surrounding<const N: usize>(&self, time: Time) -> SmallVec<[(Time, Data); N]> {
410 let mut result = SmallVec::<[(Time, Data); N]>::new_const();
411 match self {
412 Value::Uniform(v) => result.push((time, v.clone())),
413 Value::Animated(_samples) => {
414 let value = self.interpolate(time);
418 result.push((time, value));
419 }
420 }
421 result
422 }
423
424 pub fn sample_bracket(&self, time: Time) -> BracketSample {
426 match self {
427 Value::Uniform(v) => (Some((time, v.clone())), None),
428 Value::Animated(_samples) => {
429 let value = self.interpolate(time);
432 (Some((time, value)), None)
433 }
434 }
435 }
436
437 pub fn is_animated(&self) -> bool {
439 match self {
440 Value::Uniform(_) => false,
441 Value::Animated(samples) => samples.is_animated(),
442 }
443 }
444
445 pub fn sample_count(&self) -> usize {
447 match self {
448 Value::Uniform(_) => 1,
449 Value::Animated(samples) => samples.len(),
450 }
451 }
452
453 pub fn times(&self) -> SmallVec<[Time; 10]> {
455 match self {
456 Value::Uniform(_) => SmallVec::<[Time; 10]>::new_const(),
457 Value::Animated(samples) => samples.times(),
458 }
459 }
460
461 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
465 pub fn bezier_handles(&self, time: &Time) -> Option<egui_keyframe::BezierHandles> {
466 match self {
467 Value::Uniform(_) => None,
468 Value::Animated(samples) => samples.bezier_handles(time),
469 }
470 }
471
472 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
476 pub fn set_bezier_handles(
477 &mut self,
478 time: &Time,
479 handles: egui_keyframe::BezierHandles,
480 ) -> Result<()> {
481 match self {
482 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
483 Value::Animated(samples) => samples.set_bezier_handles(time, handles),
484 }
485 }
486
487 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
491 pub fn set_keyframe_type(
492 &mut self,
493 time: &Time,
494 keyframe_type: egui_keyframe::KeyframeType,
495 ) -> Result<()> {
496 match self {
497 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
498 Value::Animated(samples) => samples.set_keyframe_type(time, keyframe_type),
499 }
500 }
501
502 pub fn merge_with<F>(&self, other: &Value, combiner: F) -> Result<Value>
521 where
522 F: Fn(&Data, &Data) -> Data,
523 {
524 match (self, other) {
525 (Value::Uniform(a), Value::Uniform(b)) => Ok(Value::Uniform(combiner(a, b))),
527
528 _ => {
530 let mut all_times = std::collections::BTreeSet::new();
532
533 for t in self.times() {
535 all_times.insert(t);
536 }
537
538 for t in other.times() {
540 all_times.insert(t);
541 }
542
543 if all_times.is_empty() {
545 let a = self.interpolate(Time::default());
546 let b = other.interpolate(Time::default());
547 return Ok(Value::Uniform(combiner(&a, &b)));
548 }
549
550 let mut combined_samples = Vec::new();
552 for time in all_times {
553 let a = self.interpolate(time);
554 let b = other.interpolate(time);
555 let combined = combiner(&a, &b);
556 combined_samples.push((time, combined));
557 }
558
559 if combined_samples.len() == 1 {
561 Ok(Value::Uniform(combined_samples[0].1.clone()))
562 } else {
563 Value::animated(combined_samples)
565 }
566 }
567 }
568 }
569}
570
571impl<V: Into<Data>> From<V> for Value {
573 fn from(value: V) -> Self {
574 Value::uniform(value)
575 }
576}
577
578#[cfg(feature = "vector2")]
580impl_sample_for_value!(Vector2, Vector2);
581#[cfg(feature = "vector3")]
582impl_sample_for_value!(Vector3, Vector3);
583impl_sample_for_value!(Color, Color);
584#[cfg(feature = "matrix3")]
585impl_sample_for_value!(Matrix3, Matrix3);
586#[cfg(feature = "normal3")]
587impl_sample_for_value!(Normal3, Normal3);
588#[cfg(feature = "point3")]
589impl_sample_for_value!(Point3, Point3);
590#[cfg(feature = "matrix4")]
591impl_sample_for_value!(Matrix4, Matrix4);
592
593impl Sample<Real> for Value {
595 fn sample(&self, shutter: &Shutter, samples: NonZeroU16) -> Result<Vec<(Real, SampleWeight)>> {
596 match self {
597 Value::Uniform(data) => {
598 let value = Real(data.to_f32()? as f64);
599 Ok(vec![(value, 1.0)])
600 }
601 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
602 }
603 }
604}
605
606impl Sample<Integer> for Value {
607 fn sample(
608 &self,
609 shutter: &Shutter,
610 samples: NonZeroU16,
611 ) -> Result<Vec<(Integer, SampleWeight)>> {
612 match self {
613 Value::Uniform(data) => {
614 let value = Integer(data.to_i64()?);
615 Ok(vec![(value, 1.0)])
616 }
617 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
618 }
619 }
620}
621
622impl Eq for Value {}
625
626impl Value {
627 pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
634 match self {
635 Value::Uniform(data) => {
636 data.hash(state);
638 }
639 Value::Animated(animated) => {
640 animated.hash_with_shutter(state, shutter);
642 }
643 }
644 }
645}
646
647#[cfg(test)]
648mod tests {
649 use super::*;
650
651 #[cfg(feature = "matrix3")]
652 #[test]
653 fn test_matrix_merge_uniform() {
654 let m1 = crate::math::mat3_from_row_slice(&[2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0]); let m2 = crate::math::mat3_from_row_slice(&[1.0, 0.0, 10.0, 0.0, 1.0, 20.0, 0.0, 0.0, 1.0]); let v1 = Value::uniform(m1);
659 let v2 = Value::uniform(m2);
660
661 let result = v1
663 .merge_with(&v2, |a, b| match (a, b) {
664 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
665 _ => a.clone(),
666 })
667 .unwrap();
668
669 if let Value::Uniform(Data::Matrix3(result_matrix)) = result {
671 let expected = m1 * m2;
672 assert_eq!(result_matrix.0, expected);
673 } else {
674 panic!("Expected uniform result");
675 }
676 }
677
678 #[cfg(feature = "matrix3")]
679 #[test]
680 fn test_matrix_merge_animated() {
681 use frame_tick::Tick;
682
683 let m1_t0 =
685 crate::math::mat3_from_row_slice(&[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]); let m1_t10 =
687 crate::math::mat3_from_row_slice(&[0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]); let v1 = Value::animated([
690 (Tick::from_secs(0.0), m1_t0),
691 (Tick::from_secs(10.0), m1_t10),
692 ])
693 .unwrap();
694
695 let m2_t5 =
697 crate::math::mat3_from_row_slice(&[2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0]);
698 let m2_t15 =
699 crate::math::mat3_from_row_slice(&[3.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 1.0]);
700
701 let v2 = Value::animated([
702 (Tick::from_secs(5.0), m2_t5),
703 (Tick::from_secs(15.0), m2_t15),
704 ])
705 .unwrap();
706
707 let result = v1
709 .merge_with(&v2, |a, b| match (a, b) {
710 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
711 _ => a.clone(),
712 })
713 .unwrap();
714
715 if let Value::Animated(animated) = result {
717 let times = animated.times();
718 assert_eq!(times.len(), 4);
719 assert!(times.contains(&Tick::from_secs(0.0)));
720 assert!(times.contains(&Tick::from_secs(5.0)));
721 assert!(times.contains(&Tick::from_secs(10.0)));
722 assert!(times.contains(&Tick::from_secs(15.0)));
723 } else {
724 panic!("Expected animated result");
725 }
726 }
727}