lighthouse_protocol/utils/
rotation.rs1use std::ops::{Add, Mul, Neg};
2
3use rand::{thread_rng, Rng};
4
5use crate::Vec2;
6
7use super::{Unity, Zero};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct Rotation<T> {
14 matrix: [T; 4],
16}
17
18impl<T> Rotation<T> where T: Zero + Unity + Neg<Output = T> {
19 pub const IDENTITY: Self = Self::new([
21 T::ONE, T::ZERO,
22 T::ZERO, T::ONE,
23 ]);
24 pub const CW_90: Self = Self::new([
26 T::ZERO, T::NEG_ONE,
27 T::ONE, T::ZERO,
28 ]);
29 pub const CW_180: Self = Self::new([
31 T::NEG_ONE, T::ZERO,
32 T::ZERO, T::NEG_ONE,
33 ]);
34 pub const CW_270: Self = Self::new([
36 T::ZERO, T::ONE,
37 T::NEG_ONE, T::ZERO,
38 ]);
39
40 pub const fn new(matrix: [T; 4]) -> Self {
42 Self { matrix }
43 }
44
45 pub fn random_cardinal_with(rng: &mut impl Rng) -> Self {
47 match rng.gen_range(0..4) {
48 0 => Self::IDENTITY,
49 1 => Self::CW_90,
50 2 => Self::CW_180,
51 3 => Self::CW_270,
52 _ => unreachable!(),
53 }
54 }
55
56 pub fn random_cardinal() -> Self {
58 Self::random_cardinal_with(&mut thread_rng())
59 }
60}
61
62impl<T> Mul<Self> for Rotation<T> where T: Zero + Unity + Neg<Output = T> + Add<Output = T> + Mul<Output = T> + Copy {
63 type Output = Self;
64
65 fn mul(self, rhs: Self) -> Self {
66 Self::new([
68 self.matrix[0] * rhs.matrix[0] + self.matrix[1] * rhs.matrix[2],
69 self.matrix[0] * rhs.matrix[1] + self.matrix[1] * rhs.matrix[3],
70 self.matrix[2] * rhs.matrix[0] + self.matrix[3] * rhs.matrix[2],
71 self.matrix[2] * rhs.matrix[1] + self.matrix[3] * rhs.matrix[3],
72 ])
73 }
74}
75
76impl<T> Mul<Vec2<T>> for Rotation<T> where T: Zero + Unity + Neg<Output = T> + Add<Output = T> + Mul<Output = T> + Copy {
77 type Output = Vec2<T>;
78
79 fn mul(self, rhs: Vec2<T>) -> Vec2<T> {
80 Vec2::new(
82 self.matrix[0] * rhs.x + self.matrix[1] * rhs.y,
83 self.matrix[2] * rhs.x + self.matrix[3] * rhs.y ,
84 )
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::Vec2;
91
92 use super::Rotation;
93
94 #[test]
95 fn rotation() {
96 assert_eq!(Rotation::IDENTITY * Vec2::new(4, -3), Vec2::new(4, -3));
97 assert_eq!(Rotation::CW_90 * Vec2::new(2, 3), Vec2::new(-3, 2));
98 assert_eq!(Rotation::CW_90 * Vec2::<i32>::RIGHT, Vec2::DOWN);
99 assert_eq!(Rotation::CW_90 * Vec2::<i32>::DOWN, Vec2::LEFT);
100 assert_eq!(Rotation::CW_90 * Vec2::<i32>::LEFT, Vec2::UP);
101 assert_eq!(Rotation::CW_90 * Vec2::<i32>::UP, Vec2::RIGHT);
102 }
103
104 #[test]
105 fn matmul() {
106 assert_eq!(Rotation::IDENTITY * Rotation::<i32>::IDENTITY, Rotation::IDENTITY);
107 assert_eq!(Rotation::IDENTITY * Rotation::<i32>::CW_90, Rotation::CW_90);
108 assert_eq!(Rotation::CW_90 * Rotation::<i32>::CW_90, Rotation::CW_180);
109 assert_eq!(Rotation::CW_90 * Rotation::<i32>::CW_180, Rotation::CW_270);
110 assert_eq!(Rotation::CW_180 * Rotation::<i32>::CW_180, Rotation::IDENTITY);
111 }
112}