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> {
377 match self {
378 Value::Uniform(_) => None,
379 Value::Animated(samples) => samples.remove_at(time),
380 }
381 }
382
383 pub fn sample_at(&self, time: Time) -> Option<Data> {
388 match self {
389 Value::Uniform(v) => Some(v.clone()),
390 Value::Animated(samples) => samples.sample_at(time),
391 }
392 }
393
394 pub fn sample_at_or_before(&self, time: Time) -> Option<Data> {
396 match self {
397 Value::Uniform(v) => Some(v.clone()),
398 Value::Animated(_samples) => {
399 Some(self.interpolate(time))
402 }
403 }
404 }
405
406 pub fn sample_at_or_after(&self, time: Time) -> Option<Data> {
408 match self {
409 Value::Uniform(v) => Some(v.clone()),
410 Value::Animated(_samples) => {
411 Some(self.interpolate(time))
414 }
415 }
416 }
417
418 pub fn interpolate(&self, time: Time) -> Data {
424 match self {
425 Value::Uniform(v) => v.clone(),
426 Value::Animated(samples) => samples.interpolate(time),
427 }
428 }
429
430 pub fn sample_surrounding<const N: usize>(&self, time: Time) -> SmallVec<[(Time, Data); N]> {
432 let mut result = SmallVec::<[(Time, Data); N]>::new_const();
433 match self {
434 Value::Uniform(v) => result.push((time, v.clone())),
435 Value::Animated(_samples) => {
436 let value = self.interpolate(time);
440 result.push((time, value));
441 }
442 }
443 result
444 }
445
446 pub fn sample_bracket(&self, time: Time) -> BracketSample {
448 match self {
449 Value::Uniform(v) => (Some((time, v.clone())), None),
450 Value::Animated(_samples) => {
451 let value = self.interpolate(time);
454 (Some((time, value)), None)
455 }
456 }
457 }
458
459 pub fn is_animated(&self) -> bool {
461 match self {
462 Value::Uniform(_) => false,
463 Value::Animated(samples) => samples.is_animated(),
464 }
465 }
466
467 pub fn sample_count(&self) -> usize {
469 match self {
470 Value::Uniform(_) => 1,
471 Value::Animated(samples) => samples.len(),
472 }
473 }
474
475 pub fn times(&self) -> SmallVec<[Time; 10]> {
477 match self {
478 Value::Uniform(_) => SmallVec::<[Time; 10]>::new_const(),
479 Value::Animated(samples) => samples.times(),
480 }
481 }
482
483 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
487 pub fn bezier_handles(&self, time: &Time) -> Option<egui_keyframe::BezierHandles> {
488 match self {
489 Value::Uniform(_) => None,
490 Value::Animated(samples) => samples.bezier_handles(time),
491 }
492 }
493
494 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
498 pub fn set_bezier_handles(
499 &mut self,
500 time: &Time,
501 handles: egui_keyframe::BezierHandles,
502 ) -> Result<()> {
503 match self {
504 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
505 Value::Animated(samples) => samples.set_bezier_handles(time, handles),
506 }
507 }
508
509 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
513 pub fn set_keyframe_type(
514 &mut self,
515 time: &Time,
516 keyframe_type: egui_keyframe::KeyframeType,
517 ) -> Result<()> {
518 match self {
519 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
520 Value::Animated(samples) => samples.set_keyframe_type(time, keyframe_type),
521 }
522 }
523
524 pub fn merge_with<F>(&self, other: &Value, combiner: F) -> Result<Value>
543 where
544 F: Fn(&Data, &Data) -> Data,
545 {
546 match (self, other) {
547 (Value::Uniform(a), Value::Uniform(b)) => Ok(Value::Uniform(combiner(a, b))),
549
550 _ => {
552 let mut all_times = std::collections::BTreeSet::new();
554
555 for t in self.times() {
557 all_times.insert(t);
558 }
559
560 for t in other.times() {
562 all_times.insert(t);
563 }
564
565 if all_times.is_empty() {
567 let a = self.interpolate(Time::default());
568 let b = other.interpolate(Time::default());
569 return Ok(Value::Uniform(combiner(&a, &b)));
570 }
571
572 let mut combined_samples = Vec::new();
574 for time in all_times {
575 let a = self.interpolate(time);
576 let b = other.interpolate(time);
577 let combined = combiner(&a, &b);
578 combined_samples.push((time, combined));
579 }
580
581 if combined_samples.len() == 1 {
583 Ok(Value::Uniform(combined_samples[0].1.clone()))
584 } else {
585 Value::animated(combined_samples)
587 }
588 }
589 }
590 }
591}
592
593impl<V: Into<Data>> From<V> for Value {
595 fn from(value: V) -> Self {
596 Value::uniform(value)
597 }
598}
599
600#[cfg(feature = "vector2")]
602impl_sample_for_value!(Vector2, Vector2);
603#[cfg(feature = "vector3")]
604impl_sample_for_value!(Vector3, Vector3);
605impl_sample_for_value!(Color, Color);
606#[cfg(feature = "matrix3")]
607impl_sample_for_value!(Matrix3, Matrix3);
608#[cfg(feature = "normal3")]
609impl_sample_for_value!(Normal3, Normal3);
610#[cfg(feature = "point3")]
611impl_sample_for_value!(Point3, Point3);
612#[cfg(feature = "matrix4")]
613impl_sample_for_value!(Matrix4, Matrix4);
614
615impl Sample<Real> for Value {
617 fn sample(&self, shutter: &Shutter, samples: NonZeroU16) -> Result<Vec<(Real, SampleWeight)>> {
618 match self {
619 Value::Uniform(data) => {
620 let value = Real(data.to_f32()? as f64);
621 Ok(vec![(value, 1.0)])
622 }
623 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
624 }
625 }
626}
627
628impl Sample<Integer> for Value {
629 fn sample(
630 &self,
631 shutter: &Shutter,
632 samples: NonZeroU16,
633 ) -> Result<Vec<(Integer, SampleWeight)>> {
634 match self {
635 Value::Uniform(data) => {
636 let value = Integer(data.to_i64()?);
637 Ok(vec![(value, 1.0)])
638 }
639 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
640 }
641 }
642}
643
644impl Eq for Value {}
647
648impl Value {
649 pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
656 match self {
657 Value::Uniform(data) => {
658 data.hash(state);
660 }
661 Value::Animated(animated) => {
662 animated.hash_with_shutter(state, shutter);
664 }
665 }
666 }
667}
668
669#[cfg(test)]
670mod tests {
671 use super::*;
672
673 #[cfg(feature = "matrix3")]
674 #[test]
675 fn test_matrix_merge_uniform() {
676 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);
681 let v2 = Value::uniform(m2);
682
683 let result = v1
685 .merge_with(&v2, |a, b| match (a, b) {
686 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
687 _ => a.clone(),
688 })
689 .unwrap();
690
691 if let Value::Uniform(Data::Matrix3(result_matrix)) = result {
693 let expected = m1 * m2;
694 assert_eq!(result_matrix.0, expected);
695 } else {
696 panic!("Expected uniform result");
697 }
698 }
699
700 #[cfg(feature = "matrix3")]
701 #[test]
702 fn test_matrix_merge_animated() {
703 use frame_tick::Tick;
704
705 let m1_t0 =
707 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 =
709 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([
712 (Tick::from_secs(0.0), m1_t0),
713 (Tick::from_secs(10.0), m1_t10),
714 ])
715 .unwrap();
716
717 let m2_t5 =
719 crate::math::mat3_from_row_slice(&[2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0]);
720 let m2_t15 =
721 crate::math::mat3_from_row_slice(&[3.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 1.0]);
722
723 let v2 = Value::animated([
724 (Tick::from_secs(5.0), m2_t5),
725 (Tick::from_secs(15.0), m2_t15),
726 ])
727 .unwrap();
728
729 let result = v1
731 .merge_with(&v2, |a, b| match (a, b) {
732 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
733 _ => a.clone(),
734 })
735 .unwrap();
736
737 if let Value::Animated(animated) = result {
739 let times = animated.times();
740 assert_eq!(times.len(), 4);
741 assert!(times.contains(&Tick::from_secs(0.0)));
742 assert!(times.contains(&Tick::from_secs(5.0)));
743 assert!(times.contains(&Tick::from_secs(10.0)));
744 assert!(times.contains(&Tick::from_secs(15.0)));
745 } else {
746 panic!("Expected animated result");
747 }
748 }
749}