token_value_map/time_data_map/
sample.rs

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