fyrox_math/
curve.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::{cubicf, inf_sup_cubicf, lerpf, Rect};
22use std::cmp::Ordering;
23use uuid::Uuid;
24
25fn stepf(p0: f32, p1: f32, t: f32) -> f32 {
26    if t.eq(&1.0) {
27        p1
28    } else {
29        p0
30    }
31}
32
33#[derive(Default, Clone, Debug, PartialEq)]
34pub enum CurveKeyKind {
35    #[default]
36    Constant,
37    Linear,
38    Cubic {
39        /// A `tan(angle)` of left tangent.
40        left_tangent: f32,
41        /// A `tan(angle)` of right tangent.
42        right_tangent: f32,
43    },
44}
45
46impl CurveKeyKind {
47    #[inline]
48    pub fn new_cubic(left_angle_radians: f32, right_angle_radians: f32) -> Self {
49        Self::Cubic {
50            left_tangent: left_angle_radians.tan(),
51            right_tangent: right_angle_radians.tan(),
52        }
53    }
54}
55
56#[derive(Clone, Default, Debug, PartialEq)]
57pub struct CurveKey {
58    pub id: Uuid,
59    pub location: f32,
60    pub value: f32,
61    pub kind: CurveKeyKind,
62}
63
64impl CurveKey {
65    #[inline]
66    pub fn new(location: f32, value: f32, kind: CurveKeyKind) -> Self {
67        Self {
68            id: Uuid::new_v4(),
69            location,
70            value,
71            kind,
72        }
73    }
74}
75
76fn short_path_angles(mut start: f32, mut end: f32) -> (f32, f32) {
77    if (end - start).abs() > std::f32::consts::PI {
78        if end > start {
79            start += std::f32::consts::TAU;
80        } else {
81            end += std::f32::consts::TAU;
82        }
83    }
84    (start, end)
85}
86
87fn interpolate(
88    left_value: f32,
89    left_kind: &CurveKeyKind,
90    right_value: f32,
91    right_kind: &CurveKeyKind,
92    t: f32,
93) -> f32 {
94    match (left_kind, right_kind) {
95        // Constant-to-any
96        (CurveKeyKind::Constant, CurveKeyKind::Constant)
97        | (CurveKeyKind::Constant, CurveKeyKind::Linear)
98        | (CurveKeyKind::Constant, CurveKeyKind::Cubic { .. }) => stepf(left_value, right_value, t),
99
100        // Linear-to-any
101        (CurveKeyKind::Linear, CurveKeyKind::Constant)
102        | (CurveKeyKind::Linear, CurveKeyKind::Linear)
103        | (CurveKeyKind::Linear, CurveKeyKind::Cubic { .. }) => lerpf(left_value, right_value, t),
104
105        // Cubic-to-constant or cubic-to-linear
106        (
107            CurveKeyKind::Cubic {
108                right_tangent: left_tangent,
109                ..
110            },
111            CurveKeyKind::Constant,
112        )
113        | (
114            CurveKeyKind::Cubic {
115                right_tangent: left_tangent,
116                ..
117            },
118            CurveKeyKind::Linear,
119        ) => cubicf(left_value, right_value, t, *left_tangent, 0.0),
120
121        // Cubic-to-cubic
122        (
123            CurveKeyKind::Cubic {
124                right_tangent: left_tangent,
125                ..
126            },
127            CurveKeyKind::Cubic {
128                left_tangent: right_tangent,
129                ..
130            },
131        ) => cubicf(left_value, right_value, t, *left_tangent, *right_tangent),
132    }
133}
134
135impl CurveKey {
136    #[inline]
137    pub fn location(&self) -> f32 {
138        self.location
139    }
140
141    #[inline]
142    pub fn interpolate(&self, other: &Self, t: f32) -> f32 {
143        interpolate(self.value, &self.kind, other.value, &other.kind, t)
144    }
145
146    #[inline]
147    pub fn interpolate_angles(&self, other: &Self, t: f32) -> f32 {
148        let (left_value, right_value) = short_path_angles(self.value, other.value);
149        interpolate(left_value, &self.kind, right_value, &other.kind, t)
150    }
151}
152
153#[derive(Clone, Debug, PartialEq)]
154pub struct Curve {
155    pub id: Uuid,
156    pub name: String,
157    pub keys: Vec<CurveKey>,
158}
159
160impl Default for Curve {
161    fn default() -> Self {
162        Self {
163            id: Uuid::new_v4(),
164            name: Default::default(),
165            keys: Default::default(),
166        }
167    }
168}
169
170fn sort_keys(keys: &mut [CurveKey]) {
171    keys.sort_by(|a, b| {
172        if a.location > b.location {
173            Ordering::Greater
174        } else if a.location < b.location {
175            Ordering::Less
176        } else {
177            Ordering::Equal
178        }
179    });
180}
181
182impl From<Vec<CurveKey>> for Curve {
183    fn from(mut keys: Vec<CurveKey>) -> Self {
184        sort_keys(&mut keys);
185        Self {
186            id: Uuid::new_v4(),
187            name: Default::default(),
188            keys,
189        }
190    }
191}
192
193impl Curve {
194    #[inline]
195    pub fn set_id(&mut self, id: Uuid) {
196        self.id = id;
197    }
198
199    #[inline]
200    pub fn id(&self) -> Uuid {
201        self.id
202    }
203
204    #[inline]
205    pub fn set_name<S: AsRef<str>>(&mut self, name: S) {
206        name.as_ref().clone_into(&mut self.name);
207    }
208
209    #[inline]
210    pub fn name(&self) -> &str {
211        &self.name
212    }
213
214    #[inline]
215    pub fn clear(&mut self) {
216        self.keys.clear()
217    }
218
219    #[inline]
220    pub fn is_empty(&self) -> bool {
221        self.keys.is_empty()
222    }
223
224    #[inline]
225    pub fn keys(&self) -> &[CurveKey] {
226        &self.keys
227    }
228
229    #[inline]
230    pub fn keys_values(&mut self) -> impl Iterator<Item = &mut f32> {
231        self.keys.iter_mut().map(|k| &mut k.value)
232    }
233
234    #[inline]
235    pub fn add_key(&mut self, new_key: CurveKey) {
236        let pos = self.keys.partition_point(|k| k.location < new_key.location);
237        self.keys.insert(pos, new_key);
238    }
239
240    #[inline]
241    pub fn move_key(&mut self, key_id: usize, location: f32) {
242        if let Some(key) = self.keys.get_mut(key_id) {
243            key.location = location;
244            sort_keys(&mut self.keys);
245        }
246    }
247
248    #[inline]
249    pub fn max_location(&self) -> f32 {
250        self.keys.last().map(|k| k.location).unwrap_or_default()
251    }
252
253    #[inline]
254    fn fetch_at<I>(&self, location: f32, interpolator: I) -> f32
255    where
256        I: FnOnce(&CurveKey, &CurveKey, f32) -> f32,
257    {
258        if let (Some(first), Some(last)) = (self.keys.first(), self.keys.last()) {
259            if location <= first.location {
260                first.value
261            } else if location >= last.location {
262                last.value
263            } else {
264                // Use binary search for multiple spans.
265                let pos = self.keys.partition_point(|k| k.location < location);
266                let left = self.keys.get(pos.saturating_sub(1)).unwrap();
267                let right = self.keys.get(pos).unwrap();
268                let t = (location - left.location) / (right.location - left.location);
269                interpolator(left, right, t)
270            }
271        } else {
272            0.0
273        }
274    }
275
276    #[inline]
277    pub fn value_at(&self, location: f32) -> f32 {
278        self.fetch_at(location, |a, b, t| a.interpolate(b, t))
279    }
280
281    #[inline]
282    pub fn angle_at(&self, location: f32) -> f32 {
283        self.fetch_at(location, |a, b, t| a.interpolate_angles(b, t))
284    }
285
286    pub fn bounds(&self) -> Rect<f32> {
287        // Handle edge cases first.
288        if self.keys.is_empty() {
289            return Rect::new(0.0, 0.0, 0.0, 0.0);
290        }
291
292        if self.keys.len() == 1 {
293            let first = self.keys().first().unwrap();
294            return Rect::new(first.location, first.value, 0.0, 0.0);
295        }
296
297        // If there's 2 or more keys, calculate bounds normally.
298        let mut max_y = -f32::MAX;
299        let mut min_y = f32::MAX;
300        let mut max_x = -f32::MAX;
301        let mut min_x = f32::MAX;
302
303        let mut push = |x: f32, y: f32| {
304            if x > max_x {
305                max_x = x;
306            }
307            if x < min_x {
308                min_x = x;
309            }
310            if y > max_y {
311                max_y = y;
312            }
313            if y < min_y {
314                min_y = y;
315            }
316        };
317
318        for keys in self.keys.windows(2) {
319            let left = &keys[0];
320            let right = &keys[1];
321            match (&left.kind, &right.kind) {
322                // Cubic-to-constant and cubic-to-linear is depicted as Hermite spline with right tangent == 0.0.
323                (
324                    CurveKeyKind::Cubic {
325                        right_tangent: left_tangent,
326                        ..
327                    },
328                    CurveKeyKind::Constant,
329                )
330                | (
331                    CurveKeyKind::Cubic {
332                        right_tangent: left_tangent,
333                        ..
334                    },
335                    CurveKeyKind::Linear,
336                ) => {
337                    let (y0, y1) = inf_sup_cubicf(left.value, right.value, *left_tangent, 0.0);
338                    push(left.location, y0);
339                    push(right.location, y1);
340                }
341
342                // Cubic-to-cubic is depicted as Hermite spline.
343                (
344                    CurveKeyKind::Cubic {
345                        right_tangent: left_tangent,
346                        ..
347                    },
348                    CurveKeyKind::Cubic {
349                        left_tangent: right_tangent,
350                        ..
351                    },
352                ) => {
353                    let (y0, y1) =
354                        inf_sup_cubicf(left.value, right.value, *left_tangent, *right_tangent);
355                    push(left.location, y0);
356                    push(right.location, y1);
357                }
358                _ => {
359                    push(left.location, left.value);
360                    push(right.location, right.value);
361                }
362            }
363        }
364
365        Rect::new(min_x, min_y, max_x - min_x, max_y - min_y)
366    }
367}
368
369#[cfg(test)]
370mod test {
371    use uuid::Uuid;
372
373    use crate::curve::{Curve, CurveKey, CurveKeyKind};
374
375    #[test]
376    fn test_curve_key_insertion_order() {
377        let mut curve = Curve::default();
378
379        // Insert keys in arbitrary order with arbitrary location.
380        curve.add_key(CurveKey::new(0.0, 0.0, CurveKeyKind::Constant));
381        curve.add_key(CurveKey::new(-1.0, 0.0, CurveKeyKind::Constant));
382        curve.add_key(CurveKey::new(3.0, 0.0, CurveKeyKind::Constant));
383        curve.add_key(CurveKey::new(2.0, 0.0, CurveKeyKind::Constant));
384        curve.add_key(CurveKey::new(-5.0, 0.0, CurveKeyKind::Constant));
385
386        // Ensure that keys are sorted by their location.
387        assert_eq!(curve.keys[0].location, -5.0);
388        assert_eq!(curve.keys[1].location, -1.0);
389        assert_eq!(curve.keys[2].location, 0.0);
390        assert_eq!(curve.keys[3].location, 2.0);
391        assert_eq!(curve.keys[4].location, 3.0);
392    }
393
394    #[test]
395    fn test_curve() {
396        let mut curve = Curve::default();
397
398        // Test fetching from empty curve.
399        assert_eq!(curve.value_at(0.0), 0.0);
400
401        curve.add_key(CurveKey::new(0.0, 1.0, CurveKeyKind::Linear));
402
403        // One-key curves must always return its single key value.
404        assert_eq!(curve.value_at(-1.0), 1.0);
405        assert_eq!(curve.value_at(1.0), 1.0);
406        assert_eq!(curve.value_at(0.0), 1.0);
407
408        curve.add_key(CurveKey::new(1.0, 0.0, CurveKeyKind::Linear));
409
410        // Two-key curves must always use interpolation.
411        assert_eq!(curve.value_at(-1.0), 1.0);
412        assert_eq!(curve.value_at(2.0), 0.0);
413        assert_eq!(curve.value_at(0.5), 0.5);
414
415        // Add one more key and do more checks.
416        curve.add_key(CurveKey::new(2.0, 1.0, CurveKeyKind::Linear));
417
418        // Check order of the keys.
419        assert!(curve.keys[0].location <= curve.keys[1].location);
420        assert!(curve.keys[1].location <= curve.keys[2].location);
421
422        // Check generic out-of-bounds fetching.
423        assert_eq!(curve.value_at(-1.0), 1.0); // Left side oob
424        assert_eq!(curve.value_at(3.0), 1.0); // Right side oob.
425
426        // Check edge cases.
427        assert_eq!(curve.value_at(0.0), 1.0); // Left edge.
428        assert_eq!(curve.value_at(2.0), 1.0); // Right edge.
429
430        // Check interpolation.
431        assert_eq!(curve.value_at(0.5), 0.5);
432
433        // Check id.
434        let id = Uuid::new_v4();
435        curve.set_id(id);
436        assert_eq!(curve.id(), id);
437
438        // Check name.
439        let name = "name";
440        curve.set_name(name);
441        assert_eq!(curve.name(), name);
442
443        // Check keys capacity.
444        assert!(!curve.is_empty());
445        curve.clear();
446        assert!(curve.is_empty());
447
448        // Check keys.
449        let key = CurveKey::new(0.0, 5.0, CurveKeyKind::Constant);
450        let key2 = CurveKey::new(1.0, 10.0, CurveKeyKind::Linear);
451        curve.add_key(key.clone());
452        curve.add_key(key2.clone());
453        assert_eq!(curve.keys(), vec![key.clone(), key2.clone()]);
454
455        // Check keys values.
456        let mut values = [5.0, 10.0];
457        assert!(curve.keys_values().eq(values.iter_mut()));
458
459        // Check max location.
460        assert_eq!(curve.max_location(), 1.0);
461
462        // Check key moving.
463        let mut curve2 = curve.clone();
464        let key3 = CurveKey::default();
465        curve2.add_key(key3.clone());
466        assert_eq!(curve2.keys(), vec![key3.clone(), key.clone(), key2.clone()]);
467        curve2.move_key(key3.id.get_version_num(), 20.0);
468        assert_eq!(
469            curve2.keys(),
470            vec![
471                key.clone(),
472                key2.clone(),
473                CurveKey {
474                    location: 20.0,
475                    ..Default::default()
476                }
477            ]
478        );
479    }
480
481    #[test]
482    fn test_curve_key_kind() {
483        assert_eq!(CurveKeyKind::default(), CurveKeyKind::Constant);
484        assert_eq!(
485            CurveKeyKind::new_cubic(0.0, 0.0),
486            CurveKeyKind::Cubic {
487                left_tangent: 0.0,
488                right_tangent: 0.0
489            }
490        );
491    }
492
493    #[test]
494    fn test_curve_key() {
495        assert_eq!(
496            CurveKey::default(),
497            CurveKey {
498                id: Uuid::default(),
499                location: 0.0,
500                value: 0.0,
501                kind: CurveKeyKind::Constant,
502            },
503        );
504
505        let key = CurveKey::new(0.0, 5.0, CurveKeyKind::Constant);
506        let key2 = CurveKey::new(1.0, 10.0, CurveKeyKind::Linear);
507        let key3 = CurveKey::new(2.0, 20.0, CurveKeyKind::new_cubic(0.0, 0.0));
508        let key4 = CurveKey::new(3.0, 30.0, CurveKeyKind::new_cubic(0.0, 0.0));
509
510        assert_eq!(key.location(), 0.0);
511
512        // Constant-to-any
513        assert_eq!(key.interpolate(&key2, 1.0), 10.0);
514        assert_eq!(key.interpolate(&key2, 0.0), 5.0);
515        assert_eq!(key.interpolate(&key3, 1.0), 20.0);
516        assert_eq!(key.interpolate(&key3, 0.0), 5.0);
517
518        // Linear-to-any
519        assert_eq!(key2.interpolate(&key, 1.0), 5.0);
520        assert_eq!(key2.interpolate(&key, 0.0), 10.0);
521        assert_eq!(key2.interpolate(&key3, 1.0), 20.0);
522        assert_eq!(key2.interpolate(&key3, 0.0), 10.0);
523
524        // Cubic-to-constant or cubic-to-linear
525        assert_eq!(key3.interpolate(&key, 1.0), 5.0);
526        assert_eq!(key3.interpolate(&key, 0.0), 20.0);
527        assert_eq!(key3.interpolate(&key2, 1.0), 10.0);
528        assert_eq!(key3.interpolate(&key2, 0.0), 20.0);
529
530        // Cubic-to-cubic
531        assert_eq!(key3.interpolate(&key4, 1.0), 30.0);
532        assert_eq!(key3.interpolate(&key4, 0.0), 20.0);
533    }
534
535    #[test]
536    fn test_curve_from_vec() {
537        let key = CurveKey::new(-1.0, -1.0, CurveKeyKind::Constant);
538        let key2 = CurveKey::new(0.0, 0.0, CurveKeyKind::Constant);
539        let key3 = CurveKey::new(1.0, 1.0, CurveKeyKind::Constant);
540        let key4 = key2.clone();
541        let curve = Curve::from(vec![key2.clone(), key3.clone(), key.clone(), key4.clone()]);
542        assert_eq!(curve.name(), "");
543        assert_eq!(curve.keys(), vec![key, key2, key4, key3,]);
544    }
545}