hexasphere/
interpolation.rs

1use crate::math::{acos, sin as sinf, sqrt};
2use glam::f32::Vec3A;
3///
4/// Implements spherical interpolation along the great arc created by
5/// the initial points. This returns a new point `p` percent of the way
6/// along that arc.
7///
8/// Note: `a` and `b` should both be normalized for normalized results.
9///
10pub fn geometric_slerp(a: Vec3A, b: Vec3A, p: f32) -> Vec3A {
11    let angle = acos(a.dot(b));
12
13    let sin = sinf(angle).recip();
14    a * (sinf((1.0 - p) * angle) * sin) + b * (sinf(p * angle) * sin)
15}
16
17///
18/// This is an optimization for the `geometric_slerp` in the case where `p`
19/// is `0.5` or 50%.
20///
21/// Note: `a` and `b` should both be normalized for normalized results.
22///
23pub fn geometric_slerp_half(a: Vec3A, b: Vec3A) -> Vec3A {
24    (a + b) * sqrt(2.0 * (1.0 + a.dot(b))).recip()
25}
26
27///
28/// This is an optimization for the case where multiple points require the
29/// calculation of varying values of `p` for the same start and end points.
30///
31/// See the intended use in [`BaseShape::interpolate_multiple`](crate::BaseShape::interpolate_multiple).
32///
33/// Note: `a` and `b` should both be normalized for normalized results.
34///
35pub fn geometric_slerp_multiple(a: Vec3A, b: Vec3A, indices: &[u32], points: &mut [Vec3A]) {
36    let angle = acos(a.dot(b));
37    let sin = sinf(angle).recip();
38
39    for (percent, index) in indices.iter().enumerate() {
40        let percent = (percent + 1) as f32 / (indices.len() + 1) as f32;
41
42        points[*index as usize] =
43            a * (sinf((1.0 - percent) * angle) * sin) + b * (sinf(percent * angle) * sin);
44    }
45}
46
47///
48/// Performs normalized linear interpolation. This creates distortion when
49/// compared with spherical interpolation along an arc, however this is most
50/// likely faster, as though this avoids expensive sin and acos calculations.
51///
52pub fn normalized_lerp(a: Vec3A, b: Vec3A, p: f32) -> Vec3A {
53    ((1.0 - p) * a + p * b).normalize()
54}
55
56///
57/// This is an optimization of `normalized_lerp` which avoids a multiplication.
58///
59pub fn normalized_lerp_half(a: Vec3A, b: Vec3A) -> Vec3A {
60    (a + b).normalize()
61}
62
63///
64/// This is provided as a plug in for people who need it, but this implements
65/// essentially the same algorithm as `BaseShape` would without ever being
66/// reimplemented.
67///
68pub fn normalized_lerp_multiple(a: Vec3A, b: Vec3A, indices: &[u32], points: &mut [Vec3A]) {
69    for (percent, index) in indices.iter().enumerate() {
70        let percent = (percent + 1) as f32 / (indices.len() + 1) as f32;
71
72        points[*index as usize] = ((1.0 - percent) * a + percent * b).normalize();
73    }
74}
75
76///
77/// Simple linear interpolation. No weirdness here.
78///
79pub fn lerp(a: Vec3A, b: Vec3A, p: f32) -> Vec3A {
80    (1.0 - p) * a + p * b
81}
82
83///
84/// Gives the average of the two points.
85///
86pub fn lerp_half(a: Vec3A, b: Vec3A) -> Vec3A {
87    (a + b) * 0.5
88}
89
90///
91/// This is provided as a plug in for people who need it, but this implements
92/// essentially the same algorithm as `BaseShape` would without ever being
93/// reimplemented.
94///
95pub fn lerp_multiple(a: Vec3A, b: Vec3A, indices: &[u32], points: &mut [Vec3A]) {
96    for (percent, index) in indices.iter().enumerate() {
97        let percent = (percent + 1) as f32 / (indices.len() + 1) as f32;
98
99        points[*index as usize] = (1.0 - percent) * a + percent * b;
100    }
101}