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))]
17pub enum Value {
18 Uniform(Data),
20 Animated(AnimatedData),
22}
23
24impl Value {
25 pub fn uniform<V: Into<Data>>(value: V) -> Self {
27 Value::Uniform(value.into())
28 }
29
30 pub fn animated<I, V>(samples: I) -> Result<Self>
35 where
36 I: IntoIterator<Item = (Time, V)>,
37 V: Into<Data>,
38 {
39 let mut samples_vec: Vec<(Time, Data)> =
40 samples.into_iter().map(|(t, v)| (t, v.into())).collect();
41
42 if samples_vec.is_empty() {
43 return Err(anyhow!("Cannot create animated value with no samples"));
44 }
45
46 let data_type = samples_vec[0].1.data_type();
48
49 let mut expected_len: Option<usize> = None;
51 for (time, value) in &mut samples_vec {
52 if value.data_type() != data_type {
53 return Err(anyhow!(
54 "All animated samples must have the same type. Expected {:?}, found {:?} at time {}",
55 data_type,
56 value.data_type(),
57 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(anyhow!(
68 "Vector length {} exceeds expected length {} at time {}",
69 vec_len,
70 expected,
71 time
72 ));
73 } else if vec_len < expected {
74 value.pad_to_length(expected);
76 }
77 }
78 }
79 }
80 }
81
82 let animated_data = match data_type {
86 DataType::Boolean => {
87 let typed_samples: Vec<(Time, Boolean)> = samples_vec
88 .into_iter()
89 .map(|(t, data)| match data {
90 Data::Boolean(v) => (t, v),
91 _ => unreachable!("Type validation should have caught this"),
92 })
93 .collect();
94 AnimatedData::Boolean(TimeDataMap::from_iter(typed_samples))
95 }
96 DataType::Integer => {
97 let typed_samples: Vec<(Time, Integer)> = samples_vec
98 .into_iter()
99 .map(|(t, data)| match data {
100 Data::Integer(v) => (t, v),
101 _ => unreachable!("Type validation should have caught this"),
102 })
103 .collect();
104 AnimatedData::Integer(TimeDataMap::from_iter(typed_samples))
105 }
106 DataType::Real => {
107 let typed_samples: Vec<(Time, Real)> = samples_vec
108 .into_iter()
109 .map(|(t, data)| match data {
110 Data::Real(v) => (t, v),
111 _ => unreachable!("Type validation should have caught this"),
112 })
113 .collect();
114 AnimatedData::Real(TimeDataMap::from_iter(typed_samples))
115 }
116 DataType::String => {
117 let typed_samples: Vec<(Time, String)> = samples_vec
118 .into_iter()
119 .map(|(t, data)| match data {
120 Data::String(v) => (t, v),
121 _ => unreachable!("Type validation should have caught this"),
122 })
123 .collect();
124 AnimatedData::String(TimeDataMap::from_iter(typed_samples))
125 }
126 DataType::Color => {
127 let typed_samples: Vec<(Time, Color)> = samples_vec
128 .into_iter()
129 .map(|(t, data)| match data {
130 Data::Color(v) => (t, v),
131 _ => unreachable!("Type validation should have caught this"),
132 })
133 .collect();
134 AnimatedData::Color(TimeDataMap::from_iter(typed_samples))
135 }
136 #[cfg(feature = "vector2")]
137 DataType::Vector2 => {
138 let typed_samples: Vec<(Time, Vector2)> = samples_vec
139 .into_iter()
140 .map(|(t, data)| match data {
141 Data::Vector2(v) => (t, v),
142 _ => unreachable!("Type validation should have caught this"),
143 })
144 .collect();
145 AnimatedData::Vector2(TimeDataMap::from_iter(typed_samples))
146 }
147 #[cfg(feature = "vector3")]
148 DataType::Vector3 => {
149 let typed_samples: Vec<(Time, Vector3)> = samples_vec
150 .into_iter()
151 .map(|(t, data)| match data {
152 Data::Vector3(v) => (t, v),
153 _ => unreachable!("Type validation should have caught this"),
154 })
155 .collect();
156 AnimatedData::Vector3(TimeDataMap::from_iter(typed_samples))
157 }
158 #[cfg(feature = "matrix3")]
159 DataType::Matrix3 => {
160 let typed_samples: Vec<(Time, Matrix3)> = samples_vec
161 .into_iter()
162 .map(|(t, data)| match data {
163 Data::Matrix3(v) => (t, v),
164 _ => unreachable!("Type validation should have caught this"),
165 })
166 .collect();
167 AnimatedData::Matrix3(TimeDataMap::from_iter(typed_samples))
168 }
169 #[cfg(feature = "normal3")]
170 DataType::Normal3 => {
171 let typed_samples: Vec<(Time, Normal3)> = samples_vec
172 .into_iter()
173 .map(|(t, data)| match data {
174 Data::Normal3(v) => (t, v),
175 _ => unreachable!("Type validation should have caught this"),
176 })
177 .collect();
178 AnimatedData::Normal3(TimeDataMap::from_iter(typed_samples))
179 }
180 #[cfg(feature = "point3")]
181 DataType::Point3 => {
182 let typed_samples: Vec<(Time, Point3)> = samples_vec
183 .into_iter()
184 .map(|(t, data)| match data {
185 Data::Point3(v) => (t, v),
186 _ => unreachable!("Type validation should have caught this"),
187 })
188 .collect();
189 AnimatedData::Point3(TimeDataMap::from_iter(typed_samples))
190 }
191 #[cfg(feature = "matrix4")]
192 DataType::Matrix4 => {
193 let typed_samples: Vec<(Time, Matrix4)> = samples_vec
194 .into_iter()
195 .map(|(t, data)| match data {
196 Data::Matrix4(v) => (t, v),
197 _ => unreachable!("Type validation should have caught this"),
198 })
199 .collect();
200 AnimatedData::Matrix4(TimeDataMap::from_iter(typed_samples))
201 }
202 DataType::BooleanVec => {
203 let typed_samples: Vec<(Time, BooleanVec)> = samples_vec
204 .into_iter()
205 .map(|(t, data)| match data {
206 Data::BooleanVec(v) => (t, v),
207 _ => unreachable!("Type validation should have caught this"),
208 })
209 .collect();
210 AnimatedData::BooleanVec(TimeDataMap::from_iter(typed_samples))
211 }
212 DataType::IntegerVec => {
213 let typed_samples: Vec<(Time, IntegerVec)> = samples_vec
214 .into_iter()
215 .map(|(t, data)| match data {
216 Data::IntegerVec(v) => (t, v),
217 _ => unreachable!("Type validation should have caught this"),
218 })
219 .collect();
220 AnimatedData::IntegerVec(TimeDataMap::from_iter(typed_samples))
221 }
222 DataType::RealVec => {
223 let typed_samples: Vec<(Time, RealVec)> = samples_vec
224 .into_iter()
225 .map(|(t, data)| match data {
226 Data::RealVec(v) => (t, v),
227 _ => unreachable!("Type validation should have caught this"),
228 })
229 .collect();
230 AnimatedData::RealVec(TimeDataMap::from_iter(typed_samples))
231 }
232 DataType::ColorVec => {
233 let typed_samples: Vec<(Time, ColorVec)> = samples_vec
234 .into_iter()
235 .map(|(t, data)| match data {
236 Data::ColorVec(v) => (t, v),
237 _ => unreachable!("Type validation should have caught this"),
238 })
239 .collect();
240 AnimatedData::ColorVec(TimeDataMap::from_iter(typed_samples))
241 }
242 DataType::StringVec => {
243 let typed_samples: Vec<(Time, StringVec)> = samples_vec
244 .into_iter()
245 .map(|(t, data)| match data {
246 Data::StringVec(v) => (t, v),
247 _ => unreachable!("Type validation should have caught this"),
248 })
249 .collect();
250 AnimatedData::StringVec(TimeDataMap::from_iter(typed_samples))
251 }
252 #[cfg(all(feature = "vector2", feature = "vec_variants"))]
253 DataType::Vector2Vec => {
254 let typed_samples: Vec<(Time, Vector2Vec)> = samples_vec
255 .into_iter()
256 .map(|(t, data)| match data {
257 Data::Vector2Vec(v) => (t, v),
258 _ => unreachable!("Type validation should have caught this"),
259 })
260 .collect();
261 AnimatedData::Vector2Vec(TimeDataMap::from_iter(typed_samples))
262 }
263 #[cfg(all(feature = "vector3", feature = "vec_variants"))]
264 DataType::Vector3Vec => {
265 let typed_samples: Vec<(Time, Vector3Vec)> = samples_vec
266 .into_iter()
267 .map(|(t, data)| match data {
268 Data::Vector3Vec(v) => (t, v),
269 _ => unreachable!("Type validation should have caught this"),
270 })
271 .collect();
272 AnimatedData::Vector3Vec(TimeDataMap::from_iter(typed_samples))
273 }
274 #[cfg(all(feature = "matrix3", feature = "vec_variants"))]
275 DataType::Matrix3Vec => {
276 let typed_samples: Vec<(Time, Matrix3Vec)> = samples_vec
277 .into_iter()
278 .map(|(t, data)| match data {
279 Data::Matrix3Vec(v) => (t, v),
280 _ => unreachable!("Type validation should have caught this"),
281 })
282 .collect();
283 AnimatedData::Matrix3Vec(TimeDataMap::from_iter(typed_samples))
284 }
285 #[cfg(all(feature = "normal3", feature = "vec_variants"))]
286 DataType::Normal3Vec => {
287 let typed_samples: Vec<(Time, Normal3Vec)> = samples_vec
288 .into_iter()
289 .map(|(t, data)| match data {
290 Data::Normal3Vec(v) => (t, v),
291 _ => unreachable!("Type validation should have caught this"),
292 })
293 .collect();
294 AnimatedData::Normal3Vec(TimeDataMap::from_iter(typed_samples))
295 }
296 #[cfg(all(feature = "point3", feature = "vec_variants"))]
297 DataType::Point3Vec => {
298 let typed_samples: Vec<(Time, Point3Vec)> = samples_vec
299 .into_iter()
300 .map(|(t, data)| match data {
301 Data::Point3Vec(v) => (t, v),
302 _ => unreachable!("Type validation should have caught this"),
303 })
304 .collect();
305 AnimatedData::Point3Vec(TimeDataMap::from_iter(typed_samples))
306 }
307 #[cfg(all(feature = "matrix4", feature = "vec_variants"))]
308 DataType::Matrix4Vec => {
309 let typed_samples: Vec<(Time, Matrix4Vec)> = samples_vec
310 .into_iter()
311 .map(|(t, data)| match data {
312 Data::Matrix4Vec(v) => (t, v),
313 _ => unreachable!("Type validation should have caught this"),
314 })
315 .collect();
316 AnimatedData::Matrix4Vec(TimeDataMap::from_iter(typed_samples))
317 }
318 };
319
320 Ok(Value::Animated(animated_data))
321 }
322
323 pub fn add_sample<V: Into<Data>>(&mut self, time: Time, val: V) -> Result<()> {
325 let value = val.into();
326
327 match self {
328 Value::Uniform(_uniform_value) => {
329 *self = Value::animated(vec![(time, value)])?;
333 Ok(())
334 }
335 Value::Animated(samples) => {
336 let data_type = samples.data_type();
337 if value.data_type() != data_type {
338 return Err(anyhow!(
339 "Type mismatch: cannot add {:?} to animated {:?}",
340 value.data_type(),
341 data_type
342 ));
343 }
344
345 samples.try_insert(time, value)
347 }
348 }
349 }
350
351 pub fn remove_sample(&mut self, time: &Time) -> Option<Data> {
357 match self {
358 Value::Uniform(_) => None,
359 Value::Animated(samples) => samples.remove_at(time),
360 }
361 }
362
363 pub fn sample_at(&self, time: Time) -> Option<Data> {
368 match self {
369 Value::Uniform(v) => Some(v.clone()),
370 Value::Animated(samples) => samples.sample_at(time),
371 }
372 }
373
374 pub fn sample_at_or_before(&self, time: Time) -> Option<Data> {
376 match self {
377 Value::Uniform(v) => Some(v.clone()),
378 Value::Animated(_samples) => {
379 Some(self.interpolate(time))
382 }
383 }
384 }
385
386 pub fn sample_at_or_after(&self, time: Time) -> Option<Data> {
388 match self {
389 Value::Uniform(v) => Some(v.clone()),
390 Value::Animated(_samples) => {
391 Some(self.interpolate(time))
394 }
395 }
396 }
397
398 pub fn interpolate(&self, time: Time) -> Data {
404 match self {
405 Value::Uniform(v) => v.clone(),
406 Value::Animated(samples) => samples.interpolate(time),
407 }
408 }
409
410 pub fn sample_surrounding<const N: usize>(&self, time: Time) -> SmallVec<[(Time, Data); N]> {
412 let mut result = SmallVec::<[(Time, Data); N]>::new_const();
413 match self {
414 Value::Uniform(v) => result.push((time, v.clone())),
415 Value::Animated(_samples) => {
416 let value = self.interpolate(time);
420 result.push((time, value));
421 }
422 }
423 result
424 }
425
426 pub fn sample_bracket(&self, time: Time) -> BracketSample {
428 match self {
429 Value::Uniform(v) => (Some((time, v.clone())), None),
430 Value::Animated(_samples) => {
431 let value = self.interpolate(time);
434 (Some((time, value)), None)
435 }
436 }
437 }
438
439 pub fn is_animated(&self) -> bool {
441 match self {
442 Value::Uniform(_) => false,
443 Value::Animated(samples) => samples.is_animated(),
444 }
445 }
446
447 pub fn sample_count(&self) -> usize {
449 match self {
450 Value::Uniform(_) => 1,
451 Value::Animated(samples) => samples.len(),
452 }
453 }
454
455 pub fn times(&self) -> SmallVec<[Time; 10]> {
457 match self {
458 Value::Uniform(_) => SmallVec::<[Time; 10]>::new_const(),
459 Value::Animated(samples) => samples.times(),
460 }
461 }
462
463 pub fn merge_with<F>(&self, other: &Value, combiner: F) -> Result<Value>
482 where
483 F: Fn(&Data, &Data) -> Data,
484 {
485 match (self, other) {
486 (Value::Uniform(a), Value::Uniform(b)) => Ok(Value::Uniform(combiner(a, b))),
488
489 _ => {
491 let mut all_times = std::collections::BTreeSet::new();
493
494 for t in self.times() {
496 all_times.insert(t);
497 }
498
499 for t in other.times() {
501 all_times.insert(t);
502 }
503
504 if all_times.is_empty() {
506 let a = self.interpolate(Time::default());
507 let b = other.interpolate(Time::default());
508 return Ok(Value::Uniform(combiner(&a, &b)));
509 }
510
511 let mut combined_samples = Vec::new();
513 for time in all_times {
514 let a = self.interpolate(time);
515 let b = other.interpolate(time);
516 let combined = combiner(&a, &b);
517 combined_samples.push((time, combined));
518 }
519
520 if combined_samples.len() == 1 {
522 Ok(Value::Uniform(combined_samples[0].1.clone()))
523 } else {
524 Value::animated(combined_samples)
526 }
527 }
528 }
529 }
530}
531
532impl<V: Into<Data>> From<V> for Value {
534 fn from(value: V) -> Self {
535 Value::uniform(value)
536 }
537}
538
539#[cfg(feature = "vector2")]
541impl_sample_for_value!(Vector2, Vector2);
542#[cfg(feature = "vector3")]
543impl_sample_for_value!(Vector3, Vector3);
544impl_sample_for_value!(Color, Color);
545#[cfg(feature = "matrix3")]
546impl_sample_for_value!(Matrix3, Matrix3);
547#[cfg(feature = "normal3")]
548impl_sample_for_value!(Normal3, Normal3);
549#[cfg(feature = "point3")]
550impl_sample_for_value!(Point3, Point3);
551#[cfg(feature = "matrix4")]
552impl_sample_for_value!(Matrix4, Matrix4);
553
554impl Sample<Real> for Value {
556 fn sample(&self, shutter: &Shutter, samples: NonZeroU16) -> Result<Vec<(Real, SampleWeight)>> {
557 match self {
558 Value::Uniform(data) => {
559 let value = Real(data.to_f32()? as f64);
560 Ok(vec![(value, 1.0)])
561 }
562 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
563 }
564 }
565}
566
567impl Sample<Integer> for Value {
568 fn sample(
569 &self,
570 shutter: &Shutter,
571 samples: NonZeroU16,
572 ) -> Result<Vec<(Integer, SampleWeight)>> {
573 match self {
574 Value::Uniform(data) => {
575 let value = Integer(data.to_i64()?);
576 Ok(vec![(value, 1.0)])
577 }
578 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
579 }
580 }
581}
582
583impl Eq for Value {}
586
587impl Value {
588 pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
595 match self {
596 Value::Uniform(data) => {
597 data.hash(state);
599 }
600 Value::Animated(animated) => {
601 animated.hash_with_shutter(state, shutter);
603 }
604 }
605 }
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611
612 #[cfg(feature = "matrix3")]
613 #[test]
614 fn test_matrix_merge_uniform() {
615 let m1 = nalgebra::Matrix3::new(2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0); let m2 = nalgebra::Matrix3::new(1.0, 0.0, 10.0, 0.0, 1.0, 20.0, 0.0, 0.0, 1.0); let v1 = Value::uniform(m1);
620 let v2 = Value::uniform(m2);
621
622 let result = v1
624 .merge_with(&v2, |a, b| match (a, b) {
625 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
626 _ => a.clone(),
627 })
628 .unwrap();
629
630 if let Value::Uniform(Data::Matrix3(result_matrix)) = result {
632 let expected = m1 * m2;
633 assert_eq!(result_matrix.0, expected);
634 } else {
635 panic!("Expected uniform result");
636 }
637 }
638
639 #[cfg(feature = "matrix3")]
640 #[test]
641 fn test_matrix_merge_animated() {
642 use frame_tick::Tick;
643
644 let m1_t0 = nalgebra::Matrix3::identity();
646 let m1_t10 = nalgebra::Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); let v1 = Value::animated([
649 (Tick::from_secs(0.0), m1_t0),
650 (Tick::from_secs(10.0), m1_t10),
651 ])
652 .unwrap();
653
654 let m2_t5 = nalgebra::Matrix3::new(2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0);
656 let m2_t15 = nalgebra::Matrix3::new(3.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 1.0);
657
658 let v2 = Value::animated([
659 (Tick::from_secs(5.0), m2_t5),
660 (Tick::from_secs(15.0), m2_t15),
661 ])
662 .unwrap();
663
664 let result = v1
666 .merge_with(&v2, |a, b| match (a, b) {
667 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
668 _ => a.clone(),
669 })
670 .unwrap();
671
672 if let Value::Animated(animated) = result {
674 let times = animated.times();
675 assert_eq!(times.len(), 4);
676 assert!(times.contains(&Tick::from_secs(0.0)));
677 assert!(times.contains(&Tick::from_secs(5.0)));
678 assert!(times.contains(&Tick::from_secs(10.0)));
679 assert!(times.contains(&Tick::from_secs(15.0)));
680 } else {
681 panic!("Expected animated result");
682 }
683 }
684}