1use crate::{
2 math::{cubicf, lerpf},
3 visitor::prelude::*,
4};
5use std::cmp::Ordering;
6use uuid::Uuid;
7
8fn stepf(p0: f32, p1: f32, t: f32) -> f32 {
9 if t.eq(&1.0) {
10 p1
11 } else {
12 p0
13 }
14}
15
16#[derive(Visit, Clone, Debug, PartialEq)]
17pub enum CurveKeyKind {
18 Constant,
19 Linear,
20 Cubic {
21 left_tangent: f32,
23 right_tangent: f32,
25 },
26}
27
28impl CurveKeyKind {
29 #[inline]
30 pub fn new_cubic(left_angle_radians: f32, right_angle_radians: f32) -> Self {
31 Self::Cubic {
32 left_tangent: left_angle_radians.tan(),
33 right_tangent: right_angle_radians.tan(),
34 }
35 }
36}
37
38impl Default for CurveKeyKind {
39 #[inline]
40 fn default() -> Self {
41 Self::Constant
42 }
43}
44
45#[derive(Visit, Clone, Default, Debug, PartialEq)]
46pub struct CurveKey {
47 pub id: Uuid,
48 location: f32,
49 pub value: f32,
50 pub kind: CurveKeyKind,
51}
52
53impl CurveKey {
54 #[inline]
55 pub fn new(location: f32, value: f32, kind: CurveKeyKind) -> Self {
56 Self {
57 id: Uuid::new_v4(),
58 location,
59 value,
60 kind,
61 }
62 }
63}
64
65impl CurveKey {
66 #[inline]
67 pub fn location(&self) -> f32 {
68 self.location
69 }
70
71 #[inline]
72 pub fn interpolate(&self, other: &Self, t: f32) -> f32 {
73 match (&self.kind, &other.kind) {
74 (CurveKeyKind::Constant, CurveKeyKind::Constant)
76 | (CurveKeyKind::Constant, CurveKeyKind::Linear)
77 | (CurveKeyKind::Constant, CurveKeyKind::Cubic { .. }) => {
78 stepf(self.value, other.value, t)
79 }
80
81 (CurveKeyKind::Linear, CurveKeyKind::Constant)
83 | (CurveKeyKind::Linear, CurveKeyKind::Linear)
84 | (CurveKeyKind::Linear, CurveKeyKind::Cubic { .. }) => {
85 lerpf(self.value, other.value, t)
86 }
87
88 (
90 CurveKeyKind::Cubic {
91 right_tangent: left_tangent,
92 ..
93 },
94 CurveKeyKind::Constant,
95 )
96 | (
97 CurveKeyKind::Cubic {
98 right_tangent: left_tangent,
99 ..
100 },
101 CurveKeyKind::Linear,
102 ) => cubicf(self.value, other.value, t, *left_tangent, 0.0),
103
104 (
106 CurveKeyKind::Cubic {
107 right_tangent: left_tangent,
108 ..
109 },
110 CurveKeyKind::Cubic {
111 left_tangent: right_tangent,
112 ..
113 },
114 ) => cubicf(self.value, other.value, t, *left_tangent, *right_tangent),
115 }
116 }
117}
118
119#[derive(Visit, Default, Clone, Debug, PartialEq)]
120pub struct Curve {
121 keys: Vec<CurveKey>,
122}
123
124fn sort_keys(keys: &mut [CurveKey]) {
125 keys.sort_by(|a, b| {
126 if a.location > b.location {
127 Ordering::Greater
128 } else if a.location < b.location {
129 Ordering::Less
130 } else {
131 Ordering::Equal
132 }
133 });
134}
135
136impl From<Vec<CurveKey>> for Curve {
137 fn from(mut keys: Vec<CurveKey>) -> Self {
138 sort_keys(&mut keys);
139 Self { keys }
140 }
141}
142
143impl Curve {
144 #[inline]
145 pub fn clear(&mut self) {
146 self.keys.clear()
147 }
148
149 #[inline]
150 pub fn is_empty(&self) -> bool {
151 self.keys.is_empty()
152 }
153
154 #[inline]
155 pub fn keys(&self) -> &[CurveKey] {
156 &self.keys
157 }
158
159 #[inline]
160 pub fn add_key(&mut self, new_key: CurveKey) {
161 self.keys.push(new_key);
162 sort_keys(&mut self.keys);
163 }
164
165 #[inline]
166 pub fn move_key(&mut self, key_id: usize, location: f32) {
167 if let Some(key) = self.keys.get_mut(key_id) {
168 key.location = location;
169 sort_keys(&mut self.keys);
170 }
171 }
172
173 #[inline]
174 pub fn value_at(&self, location: f32) -> f32 {
175 if self.keys.is_empty() {
176 return Default::default();
178 } else if self.keys.len() == 1 {
179 return self.keys.first().unwrap().value;
181 } else if self.keys.len() == 2 {
182 let pt_a = self.keys.get(0).unwrap();
184 let pt_b = self.keys.get(1).unwrap();
185 if location >= pt_a.location && location <= pt_b.location {
186 let span = pt_b.location - pt_a.location;
187 let t = (location - pt_a.location) / span;
188 return pt_a.interpolate(pt_b, t);
189 } else if location < pt_a.location {
190 return pt_a.value;
191 } else {
192 return pt_b.value;
193 }
194 }
195
196 let first = self.keys.first().unwrap();
198 let last = self.keys.last().unwrap();
199 if location <= first.location {
200 first.value
201 } else if location >= last.location {
202 last.value
203 } else {
204 let mut pt_a_index = 0;
206 for (i, pt) in self.keys.iter().enumerate() {
207 if location >= pt.location {
208 pt_a_index = i;
209 }
210 }
211 let pt_b_index = pt_a_index + 1;
212
213 let pt_a = self.keys.get(pt_a_index).unwrap();
214 let pt_b = self.keys.get(pt_b_index).unwrap();
215
216 let span = pt_b.location - pt_a.location;
217 let t = (location - pt_a.location) / span;
218 pt_a.interpolate(pt_b, t)
219 }
220 }
221}