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_at<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 #[deprecated(since = "0.2.3", note = "renamed to `add_at`")]
373 pub fn add_sample<V: Into<Data>>(&mut self, time: Time, val: V) -> Result<()> {
374 self.add_at(time, val)
375 }
376
377 pub fn remove_at(&mut self, time: &Time) -> Option<Data> {
384 match self {
385 Value::Uniform(_) => None,
386 Value::Animated(samples) => samples.remove_at(time),
387 }
388 }
389
390 #[deprecated(since = "0.2.2", note = "renamed to `remove_at`")]
392 pub fn remove_sample(&mut self, time: &Time) -> Option<Data> {
393 self.remove_at(time)
394 }
395
396 pub fn remove_at_or_to_uniform(&mut self, time: &Time) -> Option<Data> {
403 match self {
404 Value::Uniform(_) => None,
405 Value::Animated(samples) => {
406 let removed = samples.remove_at(time);
407 if removed.is_some() {
408 return removed;
409 }
410 if samples.sample_at(*time).is_some() {
413 let data = samples.interpolate(Time::default());
415 *self = Value::Uniform(data);
416 }
417 None
418 }
419 }
420 }
421
422 pub fn sample_at(&self, time: Time) -> Option<Data> {
427 match self {
428 Value::Uniform(v) => Some(v.clone()),
429 Value::Animated(samples) => samples.sample_at(time),
430 }
431 }
432
433 pub fn sample_at_or_before(&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 sample_at_or_after(&self, time: Time) -> Option<Data> {
447 match self {
448 Value::Uniform(v) => Some(v.clone()),
449 Value::Animated(_samples) => {
450 Some(self.interpolate(time))
453 }
454 }
455 }
456
457 pub fn interpolate(&self, time: Time) -> Data {
463 match self {
464 Value::Uniform(v) => v.clone(),
465 Value::Animated(samples) => samples.interpolate(time),
466 }
467 }
468
469 pub fn sample_surrounding<const N: usize>(&self, time: Time) -> SmallVec<[(Time, Data); N]> {
471 let mut result = SmallVec::<[(Time, Data); N]>::new_const();
472 match self {
473 Value::Uniform(v) => result.push((time, v.clone())),
474 Value::Animated(_samples) => {
475 let value = self.interpolate(time);
479 result.push((time, value));
480 }
481 }
482 result
483 }
484
485 pub fn sample_bracket(&self, time: Time) -> BracketSample {
487 match self {
488 Value::Uniform(v) => (Some((time, v.clone())), None),
489 Value::Animated(_samples) => {
490 let value = self.interpolate(time);
493 (Some((time, value)), None)
494 }
495 }
496 }
497
498 pub fn is_animated(&self) -> bool {
500 match self {
501 Value::Uniform(_) => false,
502 Value::Animated(samples) => samples.is_animated(),
503 }
504 }
505
506 pub fn sample_count(&self) -> usize {
508 match self {
509 Value::Uniform(_) => 1,
510 Value::Animated(samples) => samples.len(),
511 }
512 }
513
514 pub fn times(&self) -> SmallVec<[Time; 10]> {
516 match self {
517 Value::Uniform(_) => SmallVec::<[Time; 10]>::new_const(),
518 Value::Animated(samples) => samples.times(),
519 }
520 }
521
522 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
526 pub fn bezier_handles(&self, time: &Time) -> Option<egui_keyframe::BezierHandles> {
527 match self {
528 Value::Uniform(_) => None,
529 Value::Animated(samples) => samples.bezier_handles(time),
530 }
531 }
532
533 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
537 pub fn set_bezier_handles(
538 &mut self,
539 time: &Time,
540 handles: egui_keyframe::BezierHandles,
541 ) -> Result<()> {
542 match self {
543 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
544 Value::Animated(samples) => samples.set_bezier_handles(time, handles),
545 }
546 }
547
548 #[cfg(all(feature = "interpolation", feature = "egui-keyframe"))]
552 pub fn set_keyframe_type(
553 &mut self,
554 time: &Time,
555 keyframe_type: egui_keyframe::KeyframeType,
556 ) -> Result<()> {
557 match self {
558 Value::Uniform(_) => Err(Error::InterpolationOnUniform),
559 Value::Animated(samples) => samples.set_keyframe_type(time, keyframe_type),
560 }
561 }
562
563 pub fn merge_with<F>(&self, other: &Value, combiner: F) -> Result<Value>
582 where
583 F: Fn(&Data, &Data) -> Data,
584 {
585 match (self, other) {
586 (Value::Uniform(a), Value::Uniform(b)) => Ok(Value::Uniform(combiner(a, b))),
588
589 _ => {
591 let mut all_times = std::collections::BTreeSet::new();
593
594 for t in self.times() {
596 all_times.insert(t);
597 }
598
599 for t in other.times() {
601 all_times.insert(t);
602 }
603
604 if all_times.is_empty() {
606 let a = self.interpolate(Time::default());
607 let b = other.interpolate(Time::default());
608 return Ok(Value::Uniform(combiner(&a, &b)));
609 }
610
611 let mut combined_samples = Vec::new();
613 for time in all_times {
614 let a = self.interpolate(time);
615 let b = other.interpolate(time);
616 let combined = combiner(&a, &b);
617 combined_samples.push((time, combined));
618 }
619
620 if combined_samples.len() == 1 {
622 Ok(Value::Uniform(combined_samples[0].1.clone()))
623 } else {
624 Value::animated(combined_samples)
626 }
627 }
628 }
629 }
630}
631
632impl<V: Into<Data>> From<V> for Value {
634 fn from(value: V) -> Self {
635 Value::uniform(value)
636 }
637}
638
639#[cfg(feature = "vector2")]
641impl_sample_for_value!(Vector2, Vector2);
642#[cfg(feature = "vector3")]
643impl_sample_for_value!(Vector3, Vector3);
644impl_sample_for_value!(Color, Color);
645#[cfg(feature = "matrix3")]
646impl_sample_for_value!(Matrix3, Matrix3);
647#[cfg(feature = "normal3")]
648impl_sample_for_value!(Normal3, Normal3);
649#[cfg(feature = "point3")]
650impl_sample_for_value!(Point3, Point3);
651#[cfg(feature = "matrix4")]
652impl_sample_for_value!(Matrix4, Matrix4);
653
654impl Sample<Real> for Value {
656 fn sample(&self, shutter: &Shutter, samples: NonZeroU16) -> Result<Vec<(Real, SampleWeight)>> {
657 match self {
658 Value::Uniform(data) => {
659 let value = Real(data.to_f32()? as f64);
660 Ok(vec![(value, 1.0)])
661 }
662 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
663 }
664 }
665}
666
667impl Sample<Integer> for Value {
668 fn sample(
669 &self,
670 shutter: &Shutter,
671 samples: NonZeroU16,
672 ) -> Result<Vec<(Integer, SampleWeight)>> {
673 match self {
674 Value::Uniform(data) => {
675 let value = Integer(data.to_i64()?);
676 Ok(vec![(value, 1.0)])
677 }
678 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
679 }
680 }
681}
682
683impl Eq for Value {}
686
687impl Value {
688 pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
695 match self {
696 Value::Uniform(data) => {
697 data.hash(state);
699 }
700 Value::Animated(animated) => {
701 animated.hash_with_shutter(state, shutter);
703 }
704 }
705 }
706}
707
708#[cfg(test)]
709mod tests {
710 use super::*;
711
712 #[cfg(feature = "matrix3")]
713 #[test]
714 fn test_matrix_merge_uniform() {
715 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);
720 let v2 = Value::uniform(m2);
721
722 let result = v1
724 .merge_with(&v2, |a, b| match (a, b) {
725 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
726 _ => a.clone(),
727 })
728 .unwrap();
729
730 if let Value::Uniform(Data::Matrix3(result_matrix)) = result {
732 let expected = m1 * m2;
733 assert_eq!(result_matrix.0, expected);
734 } else {
735 panic!("Expected uniform result");
736 }
737 }
738
739 #[cfg(feature = "matrix3")]
740 #[test]
741 fn test_matrix_merge_animated() {
742 use frame_tick::Tick;
743
744 let m1_t0 =
746 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 =
748 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([
751 (Tick::from_secs(0.0), m1_t0),
752 (Tick::from_secs(10.0), m1_t10),
753 ])
754 .unwrap();
755
756 let m2_t5 =
758 crate::math::mat3_from_row_slice(&[2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0]);
759 let m2_t15 =
760 crate::math::mat3_from_row_slice(&[3.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 1.0]);
761
762 let v2 = Value::animated([
763 (Tick::from_secs(5.0), m2_t5),
764 (Tick::from_secs(15.0), m2_t15),
765 ])
766 .unwrap();
767
768 let result = v1
770 .merge_with(&v2, |a, b| match (a, b) {
771 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
772 _ => a.clone(),
773 })
774 .unwrap();
775
776 if let Value::Animated(animated) = result {
778 let times = animated.times();
779 assert_eq!(times.len(), 4);
780 assert!(times.contains(&Tick::from_secs(0.0)));
781 assert!(times.contains(&Tick::from_secs(5.0)));
782 assert!(times.contains(&Tick::from_secs(10.0)));
783 assert!(times.contains(&Tick::from_secs(15.0)));
784 } else {
785 panic!("Expected animated result");
786 }
787 }
788}