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 sample_at(&self, time: Time) -> Option<Data> {
356 match self {
357 Value::Uniform(v) => Some(v.clone()),
358 Value::Animated(samples) => samples.sample_at(time),
359 }
360 }
361
362 pub fn sample_at_or_before(&self, time: Time) -> Option<Data> {
364 match self {
365 Value::Uniform(v) => Some(v.clone()),
366 Value::Animated(_samples) => {
367 Some(self.interpolate(time))
370 }
371 }
372 }
373
374 pub fn sample_at_or_after(&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 interpolate(&self, time: Time) -> Data {
392 match self {
393 Value::Uniform(v) => v.clone(),
394 Value::Animated(samples) => samples.interpolate(time),
395 }
396 }
397
398 pub fn sample_surrounding<const N: usize>(&self, time: Time) -> SmallVec<[(Time, Data); N]> {
400 let mut result = SmallVec::<[(Time, Data); N]>::new_const();
401 match self {
402 Value::Uniform(v) => result.push((time, v.clone())),
403 Value::Animated(_samples) => {
404 let value = self.interpolate(time);
408 result.push((time, value));
409 }
410 }
411 result
412 }
413
414 pub fn sample_bracket(&self, time: Time) -> BracketSample {
416 match self {
417 Value::Uniform(v) => (Some((time, v.clone())), None),
418 Value::Animated(_samples) => {
419 let value = self.interpolate(time);
422 (Some((time, value)), None)
423 }
424 }
425 }
426
427 pub fn is_animated(&self) -> bool {
429 match self {
430 Value::Uniform(_) => false,
431 Value::Animated(samples) => samples.is_animated(),
432 }
433 }
434
435 pub fn sample_count(&self) -> usize {
437 match self {
438 Value::Uniform(_) => 1,
439 Value::Animated(samples) => samples.len(),
440 }
441 }
442
443 pub fn times(&self) -> SmallVec<[Time; 10]> {
445 match self {
446 Value::Uniform(_) => SmallVec::<[Time; 10]>::new_const(),
447 Value::Animated(samples) => samples.times(),
448 }
449 }
450
451 pub fn merge_with<F>(&self, other: &Value, combiner: F) -> Result<Value>
470 where
471 F: Fn(&Data, &Data) -> Data,
472 {
473 match (self, other) {
474 (Value::Uniform(a), Value::Uniform(b)) => Ok(Value::Uniform(combiner(a, b))),
476
477 _ => {
479 let mut all_times = std::collections::BTreeSet::new();
481
482 for t in self.times() {
484 all_times.insert(t);
485 }
486
487 for t in other.times() {
489 all_times.insert(t);
490 }
491
492 if all_times.is_empty() {
494 let a = self.interpolate(Time::default());
495 let b = other.interpolate(Time::default());
496 return Ok(Value::Uniform(combiner(&a, &b)));
497 }
498
499 let mut combined_samples = Vec::new();
501 for time in all_times {
502 let a = self.interpolate(time);
503 let b = other.interpolate(time);
504 let combined = combiner(&a, &b);
505 combined_samples.push((time, combined));
506 }
507
508 if combined_samples.len() == 1 {
510 Ok(Value::Uniform(combined_samples[0].1.clone()))
511 } else {
512 Value::animated(combined_samples)
514 }
515 }
516 }
517 }
518}
519
520impl<V: Into<Data>> From<V> for Value {
522 fn from(value: V) -> Self {
523 Value::uniform(value)
524 }
525}
526
527#[cfg(feature = "vector2")]
529impl_sample_for_value!(Vector2, Vector2);
530#[cfg(feature = "vector3")]
531impl_sample_for_value!(Vector3, Vector3);
532impl_sample_for_value!(Color, Color);
533#[cfg(feature = "matrix3")]
534impl_sample_for_value!(Matrix3, Matrix3);
535#[cfg(feature = "normal3")]
536impl_sample_for_value!(Normal3, Normal3);
537#[cfg(feature = "point3")]
538impl_sample_for_value!(Point3, Point3);
539#[cfg(feature = "matrix4")]
540impl_sample_for_value!(Matrix4, Matrix4);
541
542impl Sample<Real> for Value {
544 fn sample(&self, shutter: &Shutter, samples: NonZeroU16) -> Result<Vec<(Real, SampleWeight)>> {
545 match self {
546 Value::Uniform(data) => {
547 let value = Real(data.to_f32()? as f64);
548 Ok(vec![(value, 1.0)])
549 }
550 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
551 }
552 }
553}
554
555impl Sample<Integer> for Value {
556 fn sample(
557 &self,
558 shutter: &Shutter,
559 samples: NonZeroU16,
560 ) -> Result<Vec<(Integer, SampleWeight)>> {
561 match self {
562 Value::Uniform(data) => {
563 let value = Integer(data.to_i64()?);
564 Ok(vec![(value, 1.0)])
565 }
566 Value::Animated(animated_data) => animated_data.sample(shutter, samples),
567 }
568 }
569}
570
571impl Eq for Value {}
574
575impl Value {
576 pub fn hash_with_shutter<H: Hasher>(&self, state: &mut H, shutter: &Shutter) {
583 match self {
584 Value::Uniform(data) => {
585 data.hash(state);
587 }
588 Value::Animated(animated) => {
589 animated.hash_with_shutter(state, shutter);
591 }
592 }
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use super::*;
599
600 #[cfg(feature = "matrix3")]
601 #[test]
602 fn test_matrix_merge_uniform() {
603 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);
608 let v2 = Value::uniform(m2);
609
610 let result = v1
612 .merge_with(&v2, |a, b| match (a, b) {
613 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
614 _ => a.clone(),
615 })
616 .unwrap();
617
618 if let Value::Uniform(Data::Matrix3(result_matrix)) = result {
620 let expected = m1 * m2;
621 assert_eq!(result_matrix.0, expected);
622 } else {
623 panic!("Expected uniform result");
624 }
625 }
626
627 #[cfg(feature = "matrix3")]
628 #[test]
629 fn test_matrix_merge_animated() {
630 use frame_tick::Tick;
631
632 let m1_t0 = nalgebra::Matrix3::identity();
634 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([
637 (Tick::from_secs(0.0), m1_t0),
638 (Tick::from_secs(10.0), m1_t10),
639 ])
640 .unwrap();
641
642 let m2_t5 = nalgebra::Matrix3::new(2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0);
644 let m2_t15 = nalgebra::Matrix3::new(3.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 1.0);
645
646 let v2 = Value::animated([
647 (Tick::from_secs(5.0), m2_t5),
648 (Tick::from_secs(15.0), m2_t15),
649 ])
650 .unwrap();
651
652 let result = v1
654 .merge_with(&v2, |a, b| match (a, b) {
655 (Data::Matrix3(ma), Data::Matrix3(mb)) => Data::Matrix3(ma.clone() * mb.clone()),
656 _ => a.clone(),
657 })
658 .unwrap();
659
660 if let Value::Animated(animated) = result {
662 let times = animated.times();
663 assert_eq!(times.len(), 4);
664 assert!(times.contains(&Tick::from_secs(0.0)));
665 assert!(times.contains(&Tick::from_secs(5.0)));
666 assert!(times.contains(&Tick::from_secs(10.0)));
667 assert!(times.contains(&Tick::from_secs(15.0)));
668 } else {
669 panic!("Expected animated result");
670 }
671 }
672}