subdiv_kernels/interpolate.rs
1//! Interpolation trait for subdivision-compatible data.
2
3use core::ops::AddAssign;
4
5/// Trait for data that can be interpolated by subdivision rules.
6///
7/// Implement this for any type you want to subdivide: positions, UVs,
8/// colors, normals, scalar weights, custom attributes.
9///
10/// Follows the OpenSubdiv `Clear()` / `AddWithWeight()` pattern.
11/// Using a local trait avoids orphan-rule problems that arise with
12/// `Mul<f32>` on foreign array types.
13///
14/// # Example
15///
16/// ```
17/// use subdiv_kernels::Interpolatable;
18///
19/// #[derive(Default, Clone)]
20/// struct Color { r: f32, g: f32, b: f32, a: f32 }
21///
22/// impl Interpolatable for Color {
23/// fn add_with_weight(&mut self, src: &Self, weight: f32) {
24/// self.r += src.r * weight;
25/// self.g += src.g * weight;
26/// self.b += src.b * weight;
27/// self.a += src.a * weight;
28/// }
29/// }
30/// ```
31pub trait Interpolatable: Default + Clone {
32 /// Accumulate `src` scaled by `weight` into `self`.
33 fn add_with_weight(&mut self, src: &Self, weight: f32);
34}
35
36// ── Blanket impls for scalars ──────────────────────────────────────────
37
38impl Interpolatable for f32 {
39 #[inline]
40 fn add_with_weight(&mut self, src: &Self, weight: f32) {
41 *self += src * weight;
42 }
43}
44
45impl Interpolatable for f64 {
46 #[inline]
47 fn add_with_weight(&mut self, src: &Self, weight: f32) {
48 *self += *src * weight as f64;
49 }
50}
51
52// ── Blanket impls for fixed-size float arrays ──────────────────────────
53//
54// Const-generic over the length, so any `[f32; N]` / `[f64; N]` works
55// (`[1.0; 3]` positions, `[_; 2]` UVs, `[_; 4]` colors, `[_; 8]` skin weights,
56// …). The `where [_; N]: Default` bound is required because the standard
57// library only implements `Default` for arrays up to length 32.
58
59impl<const N: usize> Interpolatable for [f32; N]
60where
61 [f32; N]: Default,
62{
63 #[inline]
64 fn add_with_weight(&mut self, src: &Self, weight: f32) {
65 self.iter_mut()
66 .zip(src.iter())
67 .for_each(|(dst, s)| dst.add_assign(s * weight));
68 }
69}
70
71impl<const N: usize> Interpolatable for [f64; N]
72where
73 [f64; N]: Default,
74{
75 #[inline]
76 fn add_with_weight(&mut self, src: &Self, weight: f32) {
77 let w = weight as f64;
78 self.iter_mut()
79 .zip(src.iter())
80 .for_each(|(dst, s)| dst.add_assign(s * w));
81 }
82}