aoc/util/
point.rs

1//! Comprehensive 2-dimensional point implementation, designed to work together with [`Grid`].
2//!
3//! A common theme in Advent of Code is operations in 2 dimensions. This module provides a
4//! [`Point`] struct along with implementations of several of the [`std::ops`] traits to support
5//! operator overloading, that allows shorthand expressions such as:
6//!
7//! ```
8//!   # use aoc::util::point::Point;
9//!
10//!   let a = Point::new(1, 2);
11//!   let b = Point::new(3, 4);
12//!   let k = 2;
13//!
14//!   assert_eq!(a + b, Point::new(4, 6));
15//!   assert_eq!(a - b, Point::new(-2, -2));
16//!   assert_eq!(a * k, Point::new(2, 4));
17//! ```
18//!
19//! Additionally, there are [`clockwise`] and [`counter_clockwise`]
20//! functions for 90-degree rotations and a [`manhattan`] function for the
21//! [Manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry) between 2 points.
22//!
23//! [`clockwise`]: Point::clockwise
24//! [`counter_clockwise`]: Point::counter_clockwise
25//! [`manhattan`]: Point::manhattan
26//! [`Grid`]: crate::util::grid
27use std::hash::{Hash, Hasher};
28use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
29
30pub const ORIGIN: Point = Point::new(0, 0);
31pub const UP: Point = Point::new(0, -1);
32pub const DOWN: Point = Point::new(0, 1);
33pub const LEFT: Point = Point::new(-1, 0);
34pub const RIGHT: Point = Point::new(1, 0);
35pub const ORTHOGONAL: [Point; 4] = [UP, DOWN, LEFT, RIGHT];
36// Left to right and top to bottom.
37pub const DIAGONAL: [Point; 8] = [
38    Point::new(-1, -1),
39    UP,
40    Point::new(1, -1),
41    LEFT,
42    RIGHT,
43    Point::new(-1, 1),
44    DOWN,
45    Point::new(1, 1),
46];
47
48#[derive(Copy, Clone, Debug, Eq, PartialEq)]
49pub struct Point {
50    pub x: i32,
51    pub y: i32,
52}
53
54impl Point {
55    #[inline]
56    #[must_use]
57    pub const fn new(x: i32, y: i32) -> Self {
58        Point { x, y }
59    }
60
61    #[inline]
62    #[must_use]
63    pub fn clockwise(self) -> Self {
64        Point::new(-self.y, self.x)
65    }
66
67    #[inline]
68    #[must_use]
69    pub fn counter_clockwise(self) -> Self {
70        Point::new(self.y, -self.x)
71    }
72
73    #[inline]
74    #[must_use]
75    pub fn manhattan(self, other: Self) -> i32 {
76        (self.x - other.x).abs() + (self.y - other.y).abs()
77    }
78
79    #[inline]
80    #[must_use]
81    pub fn signum(self, other: Self) -> Self {
82        Point::new((self.x - other.x).signum(), (self.y - other.y).signum())
83    }
84}
85
86impl From<u8> for Point {
87    #[inline]
88    fn from(value: u8) -> Self {
89        match value {
90            b'^' | b'U' => UP,
91            b'v' | b'D' => DOWN,
92            b'<' | b'L' => LEFT,
93            b'>' | b'R' => RIGHT,
94            _ => unreachable!(),
95        }
96    }
97}
98
99impl Hash for Point {
100    #[inline]
101    fn hash<H: Hasher>(&self, state: &mut H) {
102        state.write_u32(self.x as u32);
103        state.write_u32(self.y as u32);
104    }
105}
106
107impl Add for Point {
108    type Output = Self;
109
110    #[inline]
111    fn add(self, rhs: Self) -> Self {
112        Point::new(self.x + rhs.x, self.y + rhs.y)
113    }
114}
115
116impl AddAssign for Point {
117    #[inline]
118    fn add_assign(&mut self, rhs: Self) {
119        self.x += rhs.x;
120        self.y += rhs.y;
121    }
122}
123
124impl Mul<i32> for Point {
125    type Output = Self;
126
127    #[inline]
128    fn mul(self, rhs: i32) -> Self {
129        Point::new(self.x * rhs, self.y * rhs)
130    }
131}
132
133impl Sub for Point {
134    type Output = Self;
135
136    #[inline]
137    fn sub(self, rhs: Self) -> Self {
138        Point::new(self.x - rhs.x, self.y - rhs.y)
139    }
140}
141
142impl SubAssign for Point {
143    #[inline]
144    fn sub_assign(&mut self, rhs: Self) {
145        self.x -= rhs.x;
146        self.y -= rhs.y;
147    }
148}