ultraviolet/
interp.rs

1//! Interpolation on types for which it makes sense.
2use crate::*;
3
4/// Pure linear interpolation, i.e. `(1.0 - t) * self + (t) * end`.
5///
6/// For interpolating `Rotor`s with linear interpolation, you almost certainly
7/// want to normalize the returned `Rotor`. For example,
8/// ```rs
9/// let interpolated_rotor = rotor1.lerp(rotor2, 0.5).normalized();
10/// ```
11/// For most cases (especially where performance is the primary concern, like in
12/// animation interpolation for games, this 'normalized lerp' or 'nlerp' is probably
13/// what you want to use. However, there are situations in which you really want
14/// the interpolation between two `Rotor`s to be of constant angular velocity. In this
15/// case, check out `Slerp`.
16pub trait Lerp<T> {
17    fn lerp(&self, end: Self, t: T) -> Self;
18}
19
20macro_rules! impl_lerp {
21    ($($tt:ident => ($($vt:ident),+)),+) => {
22        $($(impl Lerp<$tt> for $vt {
23            /// Linearly interpolate between `self` and `end` by `t` between 0.0 and 1.0.
24            /// i.e. `(1.0 - t) * self + (t) * end`.
25            ///
26            /// For interpolating `Rotor`s with linear interpolation, you almost certainly
27            /// want to normalize the returned `Rotor`. For example,
28            /// ```rs
29            /// let interpolated_rotor = rotor1.lerp(rotor2, 0.5).normalized();
30            /// ```
31            /// For most cases (especially where performance is the primary concern, like in
32            /// animation interpolation for games, this 'normalized lerp' or 'nlerp' is probably
33            /// what you want to use. However, there are situations in which you really want
34            /// the interpolation between two `Rotor`s to be of constant angular velocity. In this
35            /// case, check out `Slerp`.
36            #[inline]
37            fn lerp(&self, end: Self, t: $tt) -> Self {
38                *self * ($tt::splat(1.0) - t) + end * t
39            }
40        })+)+
41    };
42}
43
44impl_lerp!(
45    f32 => (f32, Vec2, Vec3, Vec4, Bivec2, Bivec3, Rotor2, Rotor3),
46    f32x4 => (f32x4, Vec2x4, Vec3x4, Vec4x4, Bivec2x4, Bivec3x4, Rotor2x4, Rotor3x4),
47    f32x8 => (f32x8, Vec2x8, Vec3x8, Vec4x8, Bivec2x8, Bivec3x8, Rotor2x8, Rotor3x8)
48);
49
50#[cfg(feature = "f64")]
51impl_lerp!(
52    f64 => (f64, DVec2, DVec3, DVec4, DBivec2, DBivec3, DRotor2, DRotor3),
53    f64x2 => (f64x2, DVec2x2, DVec3x2, DVec4x2, DBivec2x2, DBivec3x2, DRotor2x2, DRotor3x2),
54    f64x4 => (f64x4, DVec2x4, DVec3x4, DVec4x4, DBivec2x4, DBivec3x4, DRotor2x4, DRotor3x4)
55);
56
57/// Spherical-linear interpolation.
58///
59/// Basically, interpolation that maintains a constant angular velocity
60/// from one orientation on a unit hypersphere to another. This is sorta the "high quality" interpolation
61/// for `Rotor`s, and it can also be used to interpolate other things, one example being interpolation of
62/// 3d normal vectors.
63///
64/// Note that you should often normalize the result returned by this operation, when working with `Rotor`s, etc!
65pub trait Slerp<T> {
66    fn slerp(&self, end: Self, t: T) -> Self;
67}
68
69macro_rules! impl_slerp_rotor3 {
70    ($($tt:ident => ($($vt:ident),+)),+) => {
71        $($(impl Slerp<$tt> for $vt {
72            /// Spherical-linear interpolation between `self` and `end` based on `t` from 0.0 to 1.0.
73            ///
74            /// `self` and `end` should both be normalized or something bad will happen!
75            ///
76            /// Basically, interpolation that maintains a constant angular velocity
77            /// from one orientation on a unit hypersphere to another. This is sorta the "high quality" interpolation
78            /// for `Rotor`s, and it can also be used to interpolate other things, one example being interpolation of
79            /// 3d normal vectors.
80            ///
81            /// Note that you should often normalize the result returned by this operation, when working with `Rotor`s, etc!
82            #[inline]
83            fn slerp(&self, mut end: Self, t: $tt) -> Self {
84                let mut dot = self.dot(end);
85
86                // make sure interpolation takes shortest path in case dot product is negative
87                if dot < 0.0 {
88                    end *= -1.0;
89                    dot = -dot;
90                }
91
92                if dot > 0.9995 {
93                    return self.lerp(end, t);
94                }
95
96                let dot = dot.min(1.0).max(-1.0);
97
98                let theta_0 = dot.acos(); // angle between inputs
99                let theta = theta_0 * t; // amount of said angle to travel
100
101                let v2 = (end - (*self * dot)).normalized(); // create orthonormal basis between self and `v2`
102
103                let (s, c) = theta.sin_cos();
104
105                let mut n = *self;
106
107                n.s = (c * self.s) + (s * v2.s);
108                n.bv.xy = (c * self.bv.xy) + (s * v2.bv.xy);
109                n.bv.xz = (c * self.bv.xz) + (s * v2.bv.xz);
110                n.bv.yz = (c * self.bv.yz) + (s * v2.bv.yz);
111
112                n
113            }
114        })+)+
115    };
116}
117
118impl_slerp_rotor3!(
119    f32 => (Rotor3)
120);
121
122#[cfg(feature = "f64")]
123impl_slerp_rotor3!(
124    f64 => (DRotor3)
125);
126
127macro_rules! impl_slerp_rotor3_wide {
128    ($($tt:ident => ($($vt:ident),+)),+) => {
129        $($(impl Slerp<$tt> for $vt {
130            /// Spherical-linear interpolation between `self` and `end` based on `t` from 0.0 to 1.0.
131            ///
132            /// `self` and `end` should both be normalized or something bad will happen!
133            ///
134            /// The implementation for SIMD types also requires that the two things being interpolated between
135            /// are not exactly aligned, or else the result is undefined.
136            ///
137            /// Basically, interpolation that maintains a constant angular velocity
138            /// from one orientation on a unit hypersphere to another. This is sorta the "high quality" interpolation
139            /// for `Rotor`s, and it can also be used to interpolate other things, one example being interpolation of
140            /// 3d normal vectors.
141            ///
142            /// Note that you should often normalize the result returned by this operation, when working with `Rotor`s, etc!
143            #[inline]
144            fn slerp(&self, end: Self, t: $tt) -> Self {
145                let dot = self.dot(end);
146
147                let dot = dot.min($tt::splat(1.0)).max($tt::splat(-1.0));
148
149                let theta_0 = dot.acos(); // angle between inputs
150                let theta = theta_0 * t; // amount of said angle to travel
151
152                let v2 = (end - (*self * dot)).normalized(); // create orthonormal basis between self and `v2`
153
154                let (s, c) = theta.sin_cos();
155
156                let mut n = *self;
157
158                n.s = (c * self.s) + (s * v2.s);
159                n.bv.xy = (c * self.bv.xy) + (s * v2.bv.xy);
160                n.bv.xz = (c * self.bv.xz) + (s * v2.bv.xz);
161                n.bv.yz = (c * self.bv.yz) + (s * v2.bv.yz);
162
163                n
164            }
165        })+)+
166    };
167}
168
169impl_slerp_rotor3_wide!(
170    f32x4 => (Rotor3x4),
171    f32x8 => (Rotor3x8)
172);
173
174#[cfg(feature = "f64")]
175impl_slerp_rotor3_wide!(
176    f64x2 => (DRotor3x2),
177    f64x4 => (DRotor3x4)
178);
179
180macro_rules! impl_slerp_gen {
181    ($($tt:ident => ($($vt:ident),+)),+) => {
182        $($(impl Slerp<$tt> for $vt {
183            /// Spherical-linear interpolation between `self` and `end` based on `t` from 0.0 to 1.0.
184            ///
185            /// `self` and `end` should both be normalized or something bad will happen!
186            ///
187            /// The implementation for SIMD types also requires that the two things being interpolated between
188            /// are not exactly aligned, or else the result is undefined.
189            ///
190            /// Basically, interpolation that maintains a constant angular velocity
191            /// from one orientation on a unit hypersphere to another. This is sorta the "high quality" interpolation
192            /// for `Rotor`s, and it can also be used to interpolate other things, one example being interpolation of
193            /// 3d normal vectors.
194            ///
195            /// Note that you should often normalize the result returned by this operation, when working with `Rotor`s, etc!
196            #[inline]
197            fn slerp(&self, end: Self, t: $tt) -> Self {
198                let dot = self.dot(end);
199
200                let dot = dot.min($tt::splat(1.0)).max($tt::splat(-1.0));
201
202                let theta_0 = dot.acos(); // angle between inputs
203                let theta = theta_0 * t; // amount of said angle to travel
204
205                let v2 = (end - (*self * dot)).normalized(); // create orthonormal basis between self and `v2`
206
207                let (s, c) = theta.sin_cos();
208
209                *self * c + v2 * s
210            }
211        })+)+
212    };
213}
214
215impl_slerp_gen!(
216    f32 => (Vec2, Vec3, Vec4, Bivec2, Bivec3, Rotor2),
217    f32x4 => (Vec2x4, Vec3x4, Vec4x4, Bivec2x4, Bivec3x4, Rotor2x4),
218    f32x8 => (Vec2x8, Vec3x8, Vec4x8, Bivec2x8, Bivec3x8, Rotor2x8)
219);
220
221#[cfg(feature = "f64")]
222impl_slerp_gen!(
223    f64 => (DVec2, DVec3, DVec4, DBivec2, DBivec3, DRotor2),
224    f64x2 => (DVec2x2, DVec3x2, DVec4x2, DBivec2x2, DBivec3x2, DRotor2x2),
225    f64x4 => (DVec2x4, DVec3x4, DVec4x4, DBivec2x4, DBivec3x4, DRotor2x4)
226);