textcanvas/
maths.rs

1// `x`, `y`, `u`, `v`, etc. are standard notation.
2#![allow(clippy::many_single_char_names)]
3
4use std::ops;
5
6#[derive(Copy, Clone, Debug, PartialEq)]
7pub struct Vec2D {
8    x: f64,
9    y: f64,
10}
11
12impl Vec2D {
13    #[must_use]
14    pub fn new(x: f64, y: f64) -> Self {
15        Self { x, y }
16    }
17
18    #[must_use]
19    pub fn from_i32(x: i32, y: i32) -> Self {
20        Self::new(f64::from(x), f64::from(y))
21    }
22
23    #[must_use]
24    pub fn from_segment(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
25        Self::new(x2 - x1, y2 - y1)
26    }
27
28    #[must_use]
29    pub fn from_segment_i32(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
30        Self::from_i32(x2 - x1, y2 - y1)
31    }
32
33    #[allow(clippy::cast_possible_truncation)]
34    #[must_use]
35    pub fn to_i32(&self) -> (i32, i32) {
36        (self.x.trunc() as i32, self.y.trunc() as i32)
37    }
38
39    #[must_use]
40    pub fn zero() -> Self {
41        Self::new(0.0, 0.0)
42    }
43
44    #[must_use]
45    pub fn one() -> Self {
46        Self::new(1.0, 1.0)
47    }
48}
49
50impl Default for Vec2D {
51    fn default() -> Self {
52        Self::zero()
53    }
54}
55
56impl ops::Add for Vec2D {
57    type Output = Self;
58
59    fn add(self, rhs: Self) -> Self::Output {
60        Self::new(self.x + rhs.x, self.y + rhs.y)
61    }
62}
63
64impl ops::AddAssign for Vec2D {
65    fn add_assign(&mut self, rhs: Self) {
66        self.x += rhs.x;
67        self.y += rhs.y;
68    }
69}
70
71impl ops::Sub for Vec2D {
72    type Output = Self;
73
74    fn sub(self, rhs: Self) -> Self::Output {
75        Self::new(self.x - rhs.x, self.y - rhs.y)
76    }
77}
78
79impl ops::SubAssign for Vec2D {
80    fn sub_assign(&mut self, rhs: Self) {
81        self.x -= rhs.x;
82        self.y -= rhs.y;
83    }
84}
85
86impl ops::Neg for Vec2D {
87    type Output = Self;
88
89    fn neg(self) -> Self::Output {
90        self * -1
91    }
92}
93
94impl ops::Mul for Vec2D {
95    type Output = Self;
96
97    fn mul(self, rhs: Self) -> Self::Output {
98        Self::new(self.x * rhs.x, self.y * rhs.y)
99    }
100}
101
102impl<T> ops::Mul<T> for Vec2D
103where
104    T: Into<f64> + Copy,
105{
106    type Output = Self;
107
108    fn mul(self, rhs: T) -> Self::Output {
109        Self::new(self.x * rhs.into(), self.y * rhs.into())
110    }
111}
112
113impl ops::Mul<Vec2D> for f64 {
114    type Output = Vec2D;
115
116    fn mul(self, rhs: Vec2D) -> Self::Output {
117        rhs * self
118    }
119}
120
121impl ops::Mul<Vec2D> for i32 {
122    type Output = Vec2D;
123
124    fn mul(self, rhs: Vec2D) -> Self::Output {
125        rhs * self
126    }
127}
128
129impl ops::MulAssign for Vec2D {
130    fn mul_assign(&mut self, rhs: Self) {
131        self.x *= rhs.x;
132        self.y *= rhs.y;
133    }
134}
135
136impl<T> ops::MulAssign<T> for Vec2D
137where
138    T: Into<f64> + Copy,
139{
140    fn mul_assign(&mut self, rhs: T) {
141        self.x *= rhs.into();
142        self.y *= rhs.into();
143    }
144}
145
146impl ops::Div for Vec2D {
147    type Output = Self;
148
149    fn div(self, rhs: Self) -> Self::Output {
150        Self::new(self.x / rhs.x, self.y / rhs.y)
151    }
152}
153
154impl<T> ops::Div<T> for Vec2D
155where
156    T: Into<f64> + Copy,
157{
158    type Output = Self;
159
160    fn div(self, rhs: T) -> Self::Output {
161        Self::new(self.x / rhs.into(), self.y / rhs.into())
162    }
163}
164
165impl ops::DivAssign for Vec2D {
166    fn div_assign(&mut self, rhs: Self) {
167        self.x /= rhs.x;
168        self.y /= rhs.y;
169    }
170}
171
172impl<T> ops::DivAssign<T> for Vec2D
173where
174    T: Into<f64> + Copy,
175{
176    fn div_assign(&mut self, rhs: T) {
177        self.x /= rhs.into();
178        self.y /= rhs.into();
179    }
180}
181
182impl Vec2D {
183    #[must_use]
184    pub fn sum(vectors: &[Self]) -> Self {
185        let mut acc = Self::zero();
186        for vec in vectors {
187            acc += *vec;
188        }
189        acc
190    }
191
192    #[allow(clippy::cast_precision_loss)]
193    #[must_use]
194    pub fn mean(vectors: &[Self]) -> Self {
195        let sum = Self::sum(vectors);
196        sum / vectors.len() as f64
197    }
198
199    #[must_use]
200    pub fn magnitude(&self) -> f64 {
201        self.x.hypot(self.y)
202    }
203
204    #[must_use]
205    pub fn normalize(&self) -> Self {
206        let length = self.magnitude();
207        *self / length
208    }
209
210    #[must_use]
211    pub fn normal(&self) -> Self {
212        let Self { x, y } = *self;
213        Self::new(y, -x)
214    }
215
216    #[must_use]
217    pub fn dot_product(&self, rhs: Self) -> f64 {
218        self.x * rhs.x + self.y * rhs.y
219    }
220
221    #[must_use]
222    pub fn projection_onto(&self, b: Self) -> f64 {
223        // (a⋅b)/∥b∥^2
224        let dot_product = self.dot_product(b);
225        // magnitude(b) ** 2 involves a square root, canceled by "** 2".
226        // It is more efficient to do it manually and avoid the sqrt().
227        let squared_magnitude_of_b = b.x * b.x + b.y * b.y;
228        dot_product / squared_magnitude_of_b
229    }
230}
231
232pub struct Interpolation;
233
234impl Interpolation {
235    /// Linear.
236    ///
237    /// Find value given time.
238    ///
239    /// # Examples
240    ///
241    /// ```rust
242    /// # use textcanvas::maths::Interpolation;
243    /// assert_eq!(Interpolation::lerp(10.0, 20.0, 0.5), 15.0);
244    /// ```
245    ///
246    /// # Arguments
247    ///
248    /// - `a` - Start
249    /// - `b` - End
250    /// - `t` - Time [0; 1]
251    #[must_use]
252    pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
253        (1.0 - t) * a + t * b
254    }
255
256    /// Reverse Linear.
257    ///
258    /// Find time given value.
259    ///
260    /// # Examples
261    ///
262    /// ```rust
263    /// # use textcanvas::maths::Interpolation;
264    /// assert_eq!(Interpolation::rlerp(10.0, 20.0, 15.0), 0.5);
265    /// ```
266    ///
267    /// # Arguments
268    ///
269    /// - `a` - Start
270    /// - `b` - End
271    /// - `v` - Value [start; end]
272    #[must_use]
273    pub fn rlerp(a: f64, b: f64, v: f64) -> f64 {
274        (v - a) / (b - a)
275    }
276
277    /// Ease In Quad (^2) - Start slow, accelerate.
278    ///
279    /// # Examples
280    ///
281    /// ```rust
282    /// # use textcanvas::maths::Interpolation;
283    /// assert_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.00), 0.0);
284    /// assert_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.25), 6.25);
285    /// assert_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.50), 25.0);
286    /// assert_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.75), 56.25);
287    /// assert_eq!(Interpolation::ease_in_quad(0.0, 100.0, 1.00), 100.0);
288    /// ```
289    ///
290    /// # Arguments
291    ///
292    /// - `a` - Start
293    /// - `b` - End
294    /// - `t` - Time [0; 1]
295    #[must_use]
296    pub fn ease_in_quad(a: f64, b: f64, mut t: f64) -> f64 {
297        t = t * t;
298        Self::lerp(a, b, t)
299    }
300
301    /// Ease Out Quad (^2) - Start fast, decelerate.
302    ///
303    /// # Examples
304    ///
305    /// ```rust
306    /// # use textcanvas::maths::Interpolation;
307    /// assert_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.00), 0.0);
308    /// assert_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.25), 43.75);
309    /// assert_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.50), 75.0);
310    /// assert_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.75), 93.75);
311    /// assert_eq!(Interpolation::ease_out_quad(0.0, 100.0, 1.00), 100.0);
312    /// ```
313    ///
314    /// # Arguments
315    ///
316    /// - `a` - Start
317    /// - `b` - End
318    /// - `t` - Time [0; 1]
319    #[must_use]
320    pub fn ease_out_quad(a: f64, b: f64, mut t: f64) -> f64 {
321        t = 1.0 - (1.0 - t) * (1.0 - t);
322        Self::lerp(a, b, t)
323    }
324
325    /// Ease In-Out Quad (^2) - Start slow, accelerate, end slow.
326    ///
327    /// # Examples
328    ///
329    /// ```rust
330    /// # use textcanvas::maths::Interpolation;
331    /// assert_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.00), 0.0);
332    /// assert_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.25), 12.5);
333    /// assert_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.50), 50.0);
334    /// assert_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.75), 87.5);
335    /// assert_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 1.00), 100.0);
336    /// ```
337    ///
338    /// # Arguments
339    ///
340    /// - `a` - Start
341    /// - `b` - End
342    /// - `t` - Time [0; 1]
343    #[must_use]
344    pub fn ease_in_out_quad(a: f64, b: f64, mut t: f64) -> f64 {
345        if t < 0.5 {
346            t = 2.0 * t * t;
347        } else {
348            t = 1.0 - (-2.0 * t + 2.0).powi(2) / 2.0;
349        }
350        Self::lerp(a, b, t)
351    }
352
353    /// Smoothstep (Smooth ease in-out).
354    ///
355    /// # Examples
356    ///
357    /// ```rust
358    /// # use textcanvas::maths::Interpolation;
359    /// assert_eq!(Interpolation::smoothstep(0.0, 100.0, 0.00), 0.0);
360    /// assert_eq!(Interpolation::smoothstep(0.0, 100.0, 0.25), 15.625);
361    /// assert_eq!(Interpolation::smoothstep(0.0, 100.0, 0.50), 50.0);
362    /// assert_eq!(Interpolation::smoothstep(0.0, 100.0, 0.75), 84.375);
363    /// assert_eq!(Interpolation::smoothstep(0.0, 100.0, 1.00), 100.0);
364    /// ```
365    ///
366    /// # Arguments
367    ///
368    /// - `a` - Start
369    /// - `b` - End
370    /// - `t` - Time [0; 1]
371    #[must_use]
372    pub fn smoothstep(a: f64, b: f64, mut t: f64) -> f64 {
373        t = t * t * (3.0 - 2.0 * t);
374        Self::lerp(a, b, t)
375    }
376
377    /// Catmull-Rom.
378    ///
379    /// ```text
380    ///       P1              - P3
381    ///       -  *          -
382    ///     -     *      -
383    /// P0 -        *  *
384    ///                P2
385    /// ```
386    ///
387    /// # Examples
388    ///
389    /// ```no_run
390    /// # use textcanvas::maths::{Interpolation, Vec2D};
391    /// let p0 = Vec2D::new(0.0, 0.25);
392    /// let p1 = Vec2D::new(0.33, 0.85);
393    /// let p2 = Vec2D::new(0.67, 0.15);
394    /// let p3 = Vec2D::new(1.0, 0.75);
395    ///
396    /// assert_eq!(Interpolation::catmull_rom(p0, p1, p2, p3, 0.00, 0.5), p1);
397    /// assert_eq!(Interpolation::catmull_rom(p0, p1, p2, p3, 0.25, 0.5), Vec2D::new(0.416, 0.740));
398    /// assert_eq!(Interpolation::catmull_rom(p0, p1, p2, p3, 0.50, 0.5), Vec2D::new(0.5, 0.5));
399    /// assert_eq!(Interpolation::catmull_rom(p0, p1, p2, p3, 0.75, 0.5), Vec2D::new(0.584, 0.260));
400    /// assert_eq!(Interpolation::catmull_rom(p0, p1, p2, p3, 1.00, 0.5), p2);
401    /// ```
402    ///
403    /// # Arguments
404    ///
405    /// - `p0` - Control point 1
406    /// - `p1` - Spline start
407    /// - `p2` - Spline end
408    /// - `p3` - Control point 2
409    /// - `t` - Time [0; 1]
410    /// - `alpha` - 0.5 = centripetal, 0 = uniform, 1 = chordal
411    #[must_use]
412    pub fn catmull_rom(
413        p0: Vec2D,
414        p1: Vec2D,
415        p2: Vec2D,
416        p3: Vec2D,
417        mut t: f64,
418        alpha: f64,
419    ) -> Vec2D {
420        let get_t = |t: f64, alpha: f64, p0: Vec2D, p1: Vec2D| -> f64 {
421            let d = p1 - p0;
422            let a = d.dot_product(d);
423            let b = a.powf(alpha * 0.5);
424            b + t
425        };
426
427        let t0 = 0.0;
428        let t1 = get_t(t0, alpha, p0, p1);
429        let t2 = get_t(t1, alpha, p1, p2);
430        let t3 = get_t(t2, alpha, p2, p3);
431        t = Self::lerp(t1, t2, t);
432
433        let a1 = ((t1 - t) / (t1 - t0) * p0) + ((t - t0) / (t1 - t0) * p1);
434        let a2 = ((t2 - t) / (t2 - t1) * p1) + ((t - t1) / (t2 - t1) * p2);
435        let a3 = ((t3 - t) / (t3 - t2) * p2) + ((t - t2) / (t3 - t2) * p3);
436        let b1 = ((t2 - t) / (t2 - t0) * a1) + ((t - t0) / (t2 - t0) * a2);
437        let b2 = ((t3 - t) / (t3 - t1) * a2) + ((t - t1) / (t3 - t1) * a3);
438
439        ((t2 - t) / (t2 - t1) * b1) + ((t - t1) / (t2 - t1) * b2)
440    }
441}
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446
447    macro_rules! assert_almost_eq {
448        ($a:expr, $b:expr) => {
449            assert!(($a - $b).abs() < f64::EPSILON, "{} != {}", $a, $b);
450        };
451    }
452
453    // Vec2D.
454
455    #[test]
456    fn new() {
457        let v = Vec2D::new(3.0, 6.0);
458
459        assert_almost_eq!(v.x, 3.0);
460        assert_almost_eq!(v.y, 6.0);
461    }
462
463    #[test]
464    fn from_i32() {
465        let v = Vec2D::from_i32(3, 6);
466
467        assert_almost_eq!(v.x, 3.0);
468        assert_almost_eq!(v.y, 6.0);
469    }
470
471    #[test]
472    fn from_segment() {
473        let v = Vec2D::from_segment(9.0, 2.0, 5.0, 7.0);
474
475        assert_eq!(v, Vec2D::new(-4.0, 5.0));
476    }
477
478    #[test]
479    fn from_segment_i32() {
480        let v = Vec2D::from_segment_i32(9, 2, 5, 7);
481
482        assert_eq!(v, Vec2D::new(-4.0, 5.0));
483    }
484
485    #[test]
486    fn to_i32() {
487        let v = Vec2D::new(3.0, 6.0);
488
489        let (x, y) = v.to_i32();
490
491        assert_eq!(x, 3);
492        assert_eq!(y, 6);
493    }
494
495    #[test]
496    fn zero() {
497        assert_eq!(Vec2D::zero(), Vec2D::new(0.0, 0.0));
498    }
499
500    #[test]
501    fn one() {
502        assert_eq!(Vec2D::one(), Vec2D::new(1.0, 1.0));
503    }
504
505    #[test]
506    fn default() {
507        assert_eq!(Vec2D::default(), Vec2D::new(0.0, 0.0));
508    }
509
510    #[test]
511    fn vec_add() {
512        let u = Vec2D::new(1.0, 0.0);
513        let v = Vec2D::new(2.0, 3.0);
514
515        assert_eq!(u + v, Vec2D::new(3.0, 3.0));
516        assert_eq!(v + v, Vec2D::new(4.0, 6.0));
517    }
518
519    #[test]
520    fn vec_add_assign() {
521        let mut u = Vec2D::new(1.0, 0.0);
522        let mut v = Vec2D::new(2.0, 3.0);
523
524        u += v;
525        v += v;
526
527        assert_eq!(u, Vec2D::new(3.0, 3.0));
528        assert_eq!(v, Vec2D::new(4.0, 6.0));
529    }
530
531    #[test]
532    fn vec_subtract() {
533        let u = Vec2D::new(1.0, 0.0);
534        let v = Vec2D::new(2.0, 3.0);
535
536        assert_eq!(u - v, Vec2D::new(-1.0, -3.0));
537        assert_eq!(v - u, Vec2D::new(1.0, 3.0));
538        assert_eq!(v - v, Vec2D::new(0.0, 0.0));
539    }
540
541    #[test]
542    fn vec_subtract_assign() {
543        let mut u = Vec2D::new(1.0, 0.0);
544        let mut v = Vec2D::new(2.0, 3.0);
545        let mut w = Vec2D::new(2.0, 3.0);
546
547        u -= v;
548        v -= Vec2D::new(1.0, 0.0);
549        w -= w;
550
551        assert_eq!(u, Vec2D::new(-1.0, -3.0));
552        assert_eq!(v, Vec2D::new(1.0, 3.0));
553        assert_eq!(w, Vec2D::new(0.0, 0.0));
554    }
555
556    #[test]
557    fn vec_negative() {
558        let v = Vec2D::new(6.0, 9.0);
559
560        assert_eq!(-v, Vec2D::new(-6.0, -9.0));
561    }
562
563    #[test]
564    fn vec_multiply() {
565        let u = Vec2D::new(1.0, 0.0);
566        let v = Vec2D::new(2.0, 3.0);
567
568        assert_eq!(u * v, Vec2D::new(2.0, 0.0));
569        assert_eq!(v * v, Vec2D::new(4.0, 9.0));
570    }
571
572    #[test]
573    fn vec_multiply_by_scalar() {
574        let v = Vec2D::new(2.0, 3.0);
575
576        assert_eq!(v * 3.0, Vec2D::new(6.0, 9.0));
577        assert_eq!(v * 3, Vec2D::new(6.0, 9.0));
578    }
579
580    #[test]
581    fn vec_multiply_scalar_by_vec() {
582        let v = Vec2D::new(2.0, 3.0);
583
584        assert_eq!(3.0 * v, Vec2D::new(6.0, 9.0));
585        assert_eq!(3 * v, Vec2D::new(6.0, 9.0));
586    }
587
588    #[test]
589    fn vec_multiply_assign() {
590        let mut u = Vec2D::new(1.0, 0.0);
591        let mut v = Vec2D::new(2.0, 3.0);
592
593        u *= v;
594        v *= v;
595
596        assert_eq!(u, Vec2D::new(2.0, 0.0));
597        assert_eq!(v, Vec2D::new(4.0, 9.0));
598    }
599
600    #[test]
601    fn vec_multiply_by_scalar_assign() {
602        let mut u = Vec2D::new(2.0, 3.0);
603        let mut v = Vec2D::new(2.0, 3.0);
604
605        u *= 3.0;
606        v *= 3;
607
608        assert_eq!(u, Vec2D::new(6.0, 9.0));
609        assert_eq!(v, Vec2D::new(6.0, 9.0));
610    }
611
612    #[test]
613    fn vec_divide() {
614        let u = Vec2D::new(1.0, 0.0);
615        let v = Vec2D::new(2.0, 3.0);
616
617        assert_eq!(u / v, Vec2D::new(0.5, 0.0));
618        assert_eq!(v / v, Vec2D::new(1.0, 1.0));
619    }
620
621    #[test]
622    fn vec_divide_by_scalar() {
623        let v = Vec2D::new(6.0, 9.0);
624
625        assert_eq!(v / 3.0, Vec2D::new(2.0, 3.0));
626        assert_eq!(v / 3, Vec2D::new(2.0, 3.0));
627    }
628
629    #[test]
630    fn vec_divide_assign() {
631        let mut u = Vec2D::new(1.0, 0.0);
632        let mut v = Vec2D::new(2.0, 3.0);
633
634        u /= v;
635        v /= v;
636
637        assert_eq!(u, Vec2D::new(0.5, 0.0));
638        assert_eq!(v, Vec2D::new(1.0, 1.0));
639    }
640
641    #[test]
642    fn vec_divide_by_scalar_assign() {
643        let mut u = Vec2D::new(6.0, 9.0);
644        let mut v = Vec2D::new(6.0, 9.0);
645
646        u /= 3.0;
647        v /= 3;
648
649        assert_eq!(u, Vec2D::new(2.0, 3.0));
650        assert_eq!(v, Vec2D::new(2.0, 3.0));
651    }
652
653    #[test]
654    fn sum() {
655        let vectors = [
656            Vec2D::new(1.0, 0.0),
657            Vec2D::new(2.0, 3.0),
658            Vec2D::new(-1.0, -0.5),
659        ];
660
661        let sum = Vec2D::sum(&vectors);
662
663        assert_eq!(sum, Vec2D::new(2.0, 2.5));
664    }
665
666    #[test]
667    fn mean() {
668        let vectors = [
669            Vec2D::new(5.0, -9.5),
670            Vec2D::new(2.0, 1.0),
671            Vec2D::new(-1.0, -0.5),
672        ];
673
674        let mean = Vec2D::mean(&vectors);
675
676        assert_eq!(mean, Vec2D::new(2.0, -3.0));
677    }
678
679    #[test]
680    fn magnitude() {
681        let v = Vec2D::new(3.0, 4.0);
682
683        assert_almost_eq!(v.magnitude(), 5.0);
684    }
685
686    #[test]
687    fn normalize() {
688        let v = Vec2D::new(3.0, 4.0);
689
690        assert_eq!(v.normalize(), Vec2D::new(0.6, 0.8));
691    }
692
693    #[test]
694    fn normal() {
695        let v = Vec2D::new(3.0, 4.0);
696
697        assert_eq!(v.normal(), Vec2D::new(4.0, -3.0));
698    }
699
700    #[test]
701    fn dot_product() {
702        let u = Vec2D::new(1.0, 0.0);
703        let v = Vec2D::new(-1.0, 0.0);
704        let w = Vec2D::new(0.0, 1.0);
705        let x = Vec2D::new(0.5, 0.5);
706        let y = Vec2D::new(-0.5, -0.5);
707
708        assert_almost_eq!(u.dot_product(u), 1.0);
709        assert_almost_eq!(u.dot_product(v), -1.0);
710        assert_almost_eq!(u.dot_product(w), 0.0);
711        assert_almost_eq!(u.dot_product(x), 0.5);
712        assert_almost_eq!(u.dot_product(y), -0.5);
713    }
714
715    #[test]
716    fn projection_onto() {
717        let u = Vec2D::new(1.0, 0.0);
718        let v = Vec2D::new(-1.0, 0.0);
719        let w = Vec2D::new(0.0, 1.0);
720        let x = Vec2D::new(0.5, 0.5);
721        let y = Vec2D::new(-0.5, -0.5);
722        let z = Vec2D::new(2.0, 0.0);
723
724        assert_almost_eq!(u.projection_onto(u), 1.0);
725        assert_almost_eq!(v.projection_onto(u), -1.0);
726        assert_almost_eq!(w.projection_onto(u), 0.0);
727        assert_almost_eq!(x.projection_onto(u), 0.5);
728        assert_almost_eq!(y.projection_onto(u), -0.5);
729        assert_almost_eq!(z.projection_onto(u), 2.0);
730    }
731
732    // Interpolation.
733
734    macro_rules! assert_vec_almost_eq {
735        ($a:expr, $b:expr) => {
736            assert_almost_eq!($a.x, $b.x);
737            assert_almost_eq!($a.y, $b.y);
738        };
739    }
740
741    #[test]
742    fn lerp() {
743        assert_almost_eq!(Interpolation::lerp(10.0, 20.0, 0.5), 15.0);
744    }
745
746    #[test]
747    fn rlerp() {
748        assert_almost_eq!(Interpolation::rlerp(10.0, 20.0, 15.0), 0.5);
749    }
750
751    #[test]
752    fn ease_in_quad() {
753        assert_almost_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.00), 0.0);
754        assert_almost_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.25), 6.25);
755        assert_almost_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.50), 25.0);
756        assert_almost_eq!(Interpolation::ease_in_quad(0.0, 100.0, 0.75), 56.25);
757        assert_almost_eq!(Interpolation::ease_in_quad(0.0, 100.0, 1.00), 100.0);
758    }
759
760    #[test]
761    fn ease_out_quad() {
762        assert_almost_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.00), 0.0);
763        assert_almost_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.25), 43.75);
764        assert_almost_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.50), 75.0);
765        assert_almost_eq!(Interpolation::ease_out_quad(0.0, 100.0, 0.75), 93.75);
766        assert_almost_eq!(Interpolation::ease_out_quad(0.0, 100.0, 1.00), 100.0);
767    }
768
769    #[test]
770    fn ease_in_out_quad() {
771        assert_almost_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.00), 0.0);
772        assert_almost_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.25), 12.5);
773        assert_almost_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.50), 50.0);
774        assert_almost_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 0.75), 87.5);
775        assert_almost_eq!(Interpolation::ease_in_out_quad(0.0, 100.0, 1.00), 100.0);
776    }
777
778    #[test]
779    fn smoothstep() {
780        assert_almost_eq!(Interpolation::smoothstep(0.0, 100.0, 0.00), 0.0);
781        assert_almost_eq!(Interpolation::smoothstep(0.0, 100.0, 0.25), 15.625);
782        assert_almost_eq!(Interpolation::smoothstep(0.0, 100.0, 0.50), 50.0);
783        assert_almost_eq!(Interpolation::smoothstep(0.0, 100.0, 0.75), 84.375);
784        assert_almost_eq!(Interpolation::smoothstep(0.0, 100.0, 1.00), 100.0);
785    }
786
787    #[test]
788    fn catmull_rom() {
789        let p0 = Vec2D::new(0.0, 0.25);
790        let p1 = Vec2D::new(0.33, 0.85);
791        let p2 = Vec2D::new(0.67, 0.15);
792        let p3 = Vec2D::new(1.0, 0.75);
793
794        assert_vec_almost_eq!(Interpolation::catmull_rom(p0, p1, p2, p3, 0.00, 0.5), p1);
795        assert_vec_almost_eq!(
796            Interpolation::catmull_rom(p0, p1, p2, p3, 0.25, 0.5),
797            Vec2D::new(0.415_570_592_232_469_2, 0.739_802_500_912_719_5)
798        );
799        assert_vec_almost_eq!(
800            Interpolation::catmull_rom(p0, p1, p2, p3, 0.50, 0.5),
801            Vec2D::new(0.5, 0.5)
802        );
803        assert_vec_almost_eq!(
804            Interpolation::catmull_rom(p0, p1, p2, p3, 0.75, 0.5),
805            Vec2D::new(0.584_429_407_767_530_7, 0.260_197_499_087_280_35)
806        );
807        assert_vec_almost_eq!(Interpolation::catmull_rom(p0, p1, p2, p3, 1.00, 0.5), p2);
808    }
809}