1#![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 let dot_product = self.dot_product(b);
225 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 #[must_use]
252 pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
253 (1.0 - t) * a + t * b
254 }
255
256 #[must_use]
273 pub fn rlerp(a: f64, b: f64, v: f64) -> f64 {
274 (v - a) / (b - a)
275 }
276
277 #[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 #[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 #[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 #[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 #[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 #[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 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}