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 #[cfg(feature = "curves")]
318 DataType::RealCurve => {
319 let typed_samples: Vec<(Time, RealCurve)> = samples_vec
320 .into_iter()
321 .map(|(t, data)| match data {
322 Data::RealCurve(v) => (t, v),
323 _ => unreachable!("Type validation should have caught this"),
324 })
325 .collect();
326 AnimatedData::RealCurve(TimeDataMap::from_iter(typed_samples))
327 }
328 #[cfg(feature = "curves")]
329 DataType::ColorCurve => {
330 let typed_samples: Vec<(Time, ColorCurve)> = samples_vec
331 .into_iter()
332 .map(|(t, data)| match data {
333 Data::ColorCurve(v) => (t, v),
334 _ => unreachable!("Type validation should have caught this"),
335 })
336 .collect();
337 AnimatedData::ColorCurve(TimeDataMap::from_iter(typed_samples))
338 }
339 };
340
341 Ok(Value::Animated(animated_data))
342 }
343
344 pub fn add_sample<V: Into<Data>>(&mut self, time: Time, val: V) -> Result<()> {
346 let value = val.into();
347
348 match self {
349 Value::Uniform(_uniform_value) => {
350 *self = Value::animated(vec![(time, value)])?;
354 Ok(())
355 }
356 Value::Animated(samples) => {
357 let data_type = samples.data_type();
358 if value.data_type() != data_type {
359 return Err(Error::SampleTypeMismatch {
360 expected: data_type,
361 got: value.data_type(),
362 });
363 }
364
365 samples.try_insert(time, value)
367 }
368 }
369 }
370
371 pub fn remove_sample(&mut self, time: &Time) -> Option<Data> {
378 match self {
379 Value::Uniform(_) => None,
380 Value::Animated(samples) => samples.remove_at(time),
381 }
382 }
383
384 pub fn remove_or_make_uniform(&mut self, time: &Time) -> Option<Data> {
391 match self {
392 Value::Uniform(_) => None,
393 Value::Animated(samples) => {
394 let removed = samples.remove_at(time);
395 if removed.is_some() {
396 return removed;
397 }
398 if samples.sample_at(*time).is_some() {
401 let data = samples.interpolate(Time::default());
403 *self = Value::Uniform(data);
404 }
405 None
406 }
407 }
408 }
409
410 pub fn sample_at(&self, time: Time) -> Option<Data> {
415 match self {
416 Value::Uniform(v) => Some(v.clone()),
417 Value::Animated(samples) => samples.sample_at(time),
418 }
419 }
420
421 pub fn sample_at_or_before(&self, time: Time) -> Option<Data> {
423 match self {
424 Value::Uniform(v) => Some(v.clone()),
425 Value::Animated(_samples) => {
426 Some(self.interpolate(time))
429 }
430 }
431 }
432
433 pub fn sample_at_or_after(&self, time: Time) -> Option<Data> {
435 match self {
436 Value::Uniform(v) => Some(v.clone()),
437 Value::Animated(_samples) => {
438 Some(self.interpolate(time))
441 }
442 }
443 }
444
445 pub fn interpolate(&self, time: Time) -> Data {
451 match self {
452 Value::Uniform(v) => v.clone(),
453 Value::Animated(samples) => samples.interpolate(time),
454 }
455 }
456
457 pub fn sample_surrounding<const N: usize>(&self, time: Time) -> SmallVec<[(Time, Data); N]> {
459 let mut result = SmallVec::<[(Time, Data); N]>::new_const();
460 match self {
461 Value::Uniform(v) => result.push((time, v.clone())),
462 Value::Animated(_samples) => {
463 let value = self.interpolate(time);
467 result.push((time, value));
468 }
469 }
470 result
471 }
472
473 pub fn sample_bracket(&self, time: Time) -> BracketSample {
475 match self {
476 Value::Uniform(v) => (Some((time, v.clone())), None),
477 Value::Animated(_samples) => {
478 let value = self.interpolate(time);
481 (Some((time, value)), None)
482 }
483 }
484 }
485
486 pub fn is_animated(&self) -> bool {
488 match self {
489 Value::Uniform(_) => false,
490 Value::Animated(samples) => samples.is_animated(),
491 }
492 }
493
494 pub fn sample_count(&self) -> usize {
496 match self {
497 Value::Uniform(_) => 1,
498 Value::Animated(samples) => samples.len(),
499 }
500 }
501
502 pub fn times(&self) -> SmallVec<[Time; 10]> {
504 match self {
505 Value::Uniform(_) => SmallVec::<[Time; 10]>::new_const(),
506 Value::Animated(samples) => samples.times(),
507 }
508 }
509
510 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
514 pub fn bezier_handles(&self, time: &Time) -> Option<egui_keyframe::BezierHandles> {
515 match self {
516 Value::Uniform(_) => None,
517 Value::Animated(samples) => samples.bezier_handles(time),
518 }
519 }
520
521 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
525 pub fn set_bezier_handles(
526 &mut self,
527 time: &Time,
528 handles: egui_keyframe::BezierHandles,
529 ) -> Result<()> {
530 match self {
531 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
532 Value::Animated(samples) => samples.set_bezier_handles(time, handles),
533 }
534 }
535
536 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
540 pub fn set_keyframe_type(
541 &mut self,
542 time: &Time,
543 keyframe_type: egui_keyframe::KeyframeType,
544 ) -> Result<()> {
545 match self {
546 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
547 Value::Animated(samples) => samples.set_keyframe_type(time, keyframe_type),
548 }
549 }
550
551 pub fn merge_with<F>(&self, other: &Value, combiner: F) -> Result<Value>
570 where
571 F: Fn(&Data, &Data) -> Data,
572 {
573 match (self, other) {
574 (Value::Uniform(a), Value::Uniform(b)) => Ok(Value::Uniform(combiner(a, b))),
576
577 _ => {
579 let mut all_times = std::collections::BTreeSet::new();
581
582 for t in self.times() {
584 all_times.insert(t);
585 }
586
587 for t in other.times() {
589 all_times.insert(t);
590 }
591
592 if all_times.is_empty() {
594 let a = self.interpolate(Time::default());
595 let b = other.interpolate(Time::default());
596 return Ok(Value::Uniform(combiner(&a, &b)));
597 }
598
599 let mut combined_samples = Vec::new();
601 for time in all_times {
602 let a = self.interpolate(time);
603 let b = other.interpolate(time);
604 let combined = combiner(&a, &b);
605 combined_samples.push((time, combined));
606 }
607
608 if combined_samples.len() == 1 {
610 Ok(Value::Uniform(combined_samples[0].1.clone()))
611 } else {
612 Value::animated(combined_samples)
614 }
615 }
616 }
617 }
618}
619
620impl<V: Into<Data>> From<V> for Value {
622 fn from(value: V) -> Self {
623 Value::uniform(value)
624 }
625}
626
627#[cfg(feature = "vector2")]
629impl_sample_for_value!(Vector2, Vector2);
630#[cfg(feature = "vector3")]
631impl_sample_for_value!(Vector3, Vector3);
632impl_sample_for_value!(Color, Color);
633#[cfg(feature = "matrix3")]
634impl_sample_for_value!(Matrix3, Matrix3);
635#[cfg(feature = "normal3")]
636impl_sample_for_value!(Normal3, Normal3);
637#[cfg(feature = "point3")]
638impl_sample_for_value!(Point3, Point3);
639#[cfg(feature = "matrix4")]
640impl_sample_for_value!(Matrix4, Matrix4);
641
642impl Sample<Real> for Value {
644 fn sample(&self, shutter: &Shutter, samples: NonZeroU16) -> Result<Vec<(Real, SampleWeight)>> {
645 match self {
646 Value::Uniform(data) => {
647 let value = Real(data.to_f32()? as f64);
648 Ok(vec![(value, 1.0)])
649 }
650 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
651 }
652 }
653}
654
655impl Sample<Integer> for Value {
656 fn sample(
657 &self,
658 shutter: &Shutter,
659 samples: NonZeroU16,
660 ) -> Result<Vec<(Integer, SampleWeight)>> {
661 match self {
662 Value::Uniform(data) => {
663 let value = Integer(data.to_i64()?);
664 Ok(vec![(value, 1.0)])
665 }
666 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
667 }
668 }
669}
670
671impl Eq for Value {}
674
675impl Value {
676 pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
683 match self {
684 Value::Uniform(data) => {
685 data.hash(state);
687 }
688 Value::Animated(animated) => {
689 animated.hash_with_shutter(state, shutter);
691 }
692 }
693 }
694}
695
696#[cfg(test)]
697mod tests {
698 use super::*;
699
700 #[cfg(feature = "matrix3")]
701 #[test]
702 fn test_matrix_merge_uniform() {
703 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);
708 let v2 = Value::uniform(m2);
709
710 let result = v1
712 .merge_with(&v2, |a, b| match (a, b) {
713 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
714 _ => a.clone(),
715 })
716 .unwrap();
717
718 if let Value::Uniform(Data::Matrix3(result_matrix)) = result {
720 let expected = m1 * m2;
721 assert_eq!(result_matrix.0, expected);
722 } else {
723 panic!("Expected uniform result");
724 }
725 }
726
727 #[cfg(feature = "matrix3")]
728 #[test]
729 fn test_matrix_merge_animated() {
730 use frame_tick::Tick;
731
732 let m1_t0 =
734 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 =
736 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([
739 (Tick::from_secs(0.0), m1_t0),
740 (Tick::from_secs(10.0), m1_t10),
741 ])
742 .unwrap();
743
744 let m2_t5 =
746 crate::math::mat3_from_row_slice(&[2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0]);
747 let m2_t15 =
748 crate::math::mat3_from_row_slice(&[3.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 1.0]);
749
750 let v2 = Value::animated([
751 (Tick::from_secs(5.0), m2_t5),
752 (Tick::from_secs(15.0), m2_t15),
753 ])
754 .unwrap();
755
756 let result = v1
758 .merge_with(&v2, |a, b| match (a, b) {
759 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
760 _ => a.clone(),
761 })
762 .unwrap();
763
764 if let Value::Animated(animated) = result {
766 let times = animated.times();
767 assert_eq!(times.len(), 4);
768 assert!(times.contains(&Tick::from_secs(0.0)));
769 assert!(times.contains(&Tick::from_secs(5.0)));
770 assert!(times.contains(&Tick::from_secs(10.0)));
771 assert!(times.contains(&Tick::from_secs(15.0)));
772 } else {
773 panic!("Expected animated result");
774 }
775 }
776}