Skip to main content

token_value_map/time_data_map/
sample.rs

1use super::*;
2use core::num::NonZeroU16;
3#[cfg(feature = "builtin-types")]
4use rayon::prelude::*;
5
6/// Weight value for motion blur sampling.
7pub type SampleWeight = f32;
8
9/// `trait` for generating motion blur samples with shutter timing.
10///
11/// The [`Sample`] `trait` generates multiple samples across a [`Shutter`]
12/// interval for motion blur rendering. Each sample includes the interpolated
13/// value and a weight based on the shutter opening at that time.
14pub trait Sample<T> {
15    /// Generate samples across the shutter interval.
16    ///
17    /// Returns a vector of value-weight pairs for the specified number of
18    /// samples distributed across the shutter's time range.
19    fn sample(&self, shutter: &Shutter, samples: NonZeroU16) -> Result<Vec<(T, SampleWeight)>>;
20}
21
22#[cfg(feature = "builtin-types")]
23macro_rules! impl_sample {
24    ($data_type:ty) => {
25        impl Sample<$data_type> for TimeDataMap<$data_type> {
26            fn sample(
27                &self,
28                shutter: &Shutter,
29                samples: NonZeroU16,
30            ) -> Result<Vec<($data_type, SampleWeight)>> {
31                Ok((0..samples.into())
32                    .into_par_iter()
33                    .map(|t| {
34                        let time = shutter.evaluate(t as f32 / u16::from(samples) as f32);
35                        (self.interpolate(time), shutter.opening(time))
36                    })
37                    .collect::<Vec<_>>())
38            }
39        }
40    };
41}
42
43#[cfg(feature = "builtin-types")]
44impl_sample!(Real);
45#[cfg(feature = "builtin-types")]
46impl_sample!(Integer);
47#[cfg(feature = "builtin-types")]
48impl_sample!(Color);
49
50#[cfg(all(feature = "builtin-types", feature = "vector2"))]
51impl_sample!(Vector2);
52#[cfg(all(feature = "builtin-types", feature = "vector3"))]
53impl_sample!(Vector3);
54#[cfg(all(feature = "builtin-types", feature = "normal3"))]
55impl_sample!(Normal3);
56#[cfg(all(feature = "builtin-types", feature = "point3"))]
57impl_sample!(Point3);
58#[cfg(all(feature = "builtin-types", feature = "matrix4"))]
59impl_sample!(Matrix4);
60
61// AIDEV-NOTE: Matrix3 sampling uses analytical 2×2 SVD decomposition for proper
62// rotation/stretch interpolation on all backends. Rotation is interpolated via
63// shortest-path angle slerp; translation and stretch are interpolated linearly.
64#[cfg(all(feature = "builtin-types", feature = "matrix3"))]
65impl Sample<Matrix3> for TimeDataMap<Matrix3> {
66    fn sample(
67        &self,
68        shutter: &Shutter,
69        samples: NonZeroU16,
70    ) -> Result<Vec<(Matrix3, SampleWeight)>> {
71        // Split all matrices into their component parts via analytical 2×2 SVD.
72        let mut translations = BTreeMap::new();
73        let mut rotations = BTreeMap::new();
74        let mut stretches = BTreeMap::new();
75
76        #[cfg(not(feature = "interpolation"))]
77        for (time, matrix) in self.values.as_btree_map().iter() {
78            let crate::Matrix3(ref inner) = *matrix;
79            let (translate, rotate, stretch) = decompose_matrix(inner);
80            translations.insert(*time, translate);
81            rotations.insert(*time, rotate);
82            stretches.insert(*time, stretch);
83        }
84        #[cfg(feature = "interpolation")]
85        for (time, (matrix, _spec)) in self.values.as_btree_map().iter() {
86            let crate::Matrix3(ref inner) = *matrix;
87            let (translate, rotate, stretch) = decompose_matrix(inner);
88            translations.insert(*time, translate);
89            rotations.insert(*time, rotate);
90            stretches.insert(*time, stretch);
91        }
92
93        // Interpolate the samples and recompose the matrices.
94        Ok((0..samples.into())
95            .into_par_iter()
96            .map(|t| {
97                let time = shutter.evaluate(t as f32 / u16::from(samples) as f32);
98                (
99                    crate::Matrix3(recompose_matrix(
100                        interpolate(&translations, time),
101                        interpolate_rotation(&rotations, time),
102                        interpolate(&stretches, time),
103                    )),
104                    shutter.opening(time),
105                )
106            })
107            .collect::<Vec<_>>())
108    }
109}