Advent of Code solutions
at main 171 lines 4.7 kB view raw
1use std::ops::Neg; 2 3use crate::{dir::Direction, pos::Position}; 4 5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 6/// A line between two points. 7/// 8/// This line is represented by two [Position]s. 9/// 10/// # Examples 11/// 12/// ``` 13/// use utils::prelude::*; 14/// 15/// let line = Line::new(Position::new(0, 0), Position::new(1, 1)); 16/// assert_eq!(line.get_slope(), 1.0); 17/// assert_eq!(line.get_intercept(), 0.0); 18/// ``` 19/// 20pub struct Line(Position, Position); 21 22impl Line { 23 /// Create a new line between two points. 24 /// 25 /// # Examples 26 /// 27 /// ``` 28 /// use utils::prelude::*; 29 /// 30 /// let line = Line::new(Position::new(0, 0), Position::new(1, 1)); 31 /// assert_eq!(line.end().x, 1); 32 /// ``` 33 /// 34 pub fn new(start: Position, end: Position) -> Self { 35 Self(start, end) 36 } 37 38 /// Create a new line from a starting point and a direction. 39 /// 40 /// # Examples 41 /// 42 /// ``` 43 /// use utils::prelude::*; 44 /// 45 /// let line = Line::from_dir(Position::new(0, 0), Direction::East); 46 /// assert_eq!(line.end().x, 1); 47 /// ``` 48 /// 49 pub fn from_dir(start: Position, dir: Direction) -> Self { 50 Self(start, start.move_dir(dir)) 51 } 52 53 /// Get the linear slope of the line. 54 /// 55 /// # Examples 56 /// 57 /// ``` 58 /// use utils::prelude::*; 59 /// 60 /// let line = Line::new(Position::new(0, 0), Position::new(1, 1)); 61 /// assert_eq!(line.get_slope(), 1.0); 62 /// 63 /// let line = Line::new(Position::new(0, 0), Position::new(2, 1)); 64 /// assert_eq!(line.get_slope(), 0.5); 65 /// ``` 66 /// 67 pub fn get_slope(&self) -> f64 { 68 let dx = self.1.x - self.0.x; 69 let dy = self.1.y - self.0.y; 70 dy as f64 / dx as f64 71 } 72 73 /// Get the y-intercept of the line. 74 /// 75 /// # Examples 76 /// 77 /// ``` 78 /// use utils::prelude::*; 79 /// 80 /// let line = Line::new(Position::new(0, 0), Position::new(1, 1)); 81 /// assert_eq!(line.get_intercept(), 0.0); 82 /// 83 /// let line = Line::new(Position::new(0, 5), Position::new(2, 1)); 84 /// assert_eq!(line.get_intercept(), 5.0); 85 /// ``` 86 /// 87 pub fn get_intercept(&self) -> f64 { 88 let slope = self.get_slope(); 89 self.0.y as f64 - slope * self.0.x as f64 90 } 91 92 /// Check that the given point is *after* the start position on the line. 93 /// 94 /// Note this doesn't check if the point is on the line, just that it is 95 /// on the same side of the line as the end point. 96 /// 97 /// # Examples 98 /// 99 /// ``` 100 /// use utils::prelude::*; 101 /// 102 /// let line = Line::new(Position::new(0, 0), Position::new(2, 2)); 103 /// assert_eq!(line.check_after(&Position::new(1, 1)), true); 104 /// 105 /// let line = Line::new(Position::new(0, 0), Position::new(2, 2)); 106 /// assert_eq!(line.check_after(&Position::new(-1, -1)), false); 107 /// ``` 108 /// 109 pub fn check_after(&self, pos: &Position) -> bool { 110 let relative = pos.sub(&self.0); 111 let d = self.1.sub(&self.0); 112 relative.normalize() == d.normalize() 113 } 114 115 /// Get the intersection point between this line and another. 116 /// 117 /// Pass `check_after` as `true` to ensure that the intersection point is 118 /// after the start of both lines. 119 /// 120 /// # Examples 121 /// 122 /// ``` 123 /// use utils::prelude::*; 124 /// 125 /// let line1 = Line::new(Position::new(2, -2), Position::new(-2, 2)); 126 /// let line2 = Line::new(Position::new(2, 2), Position::new(-2, -2)); 127 /// assert_eq!(line1.get_intersection(&line2, true), Some(Position::new(0, 0))); 128 /// 129 /// let line1 = Line::new(Position::new(5, 0), Position::new(6, 8)); 130 /// let line2 = Line::new(Position::new(0, 1), Position::new(-4, -3)); 131 /// assert_eq!(line1.get_intersection(&line2, true), None); 132 /// ``` 133 /// 134 pub fn get_intersection(&self, other: &Self, check_after: bool) -> Option<Position> { 135 let slope = self.get_slope(); 136 let intercept = self.get_intercept(); 137 let other_slope = other.get_slope(); 138 let other_intercept = other.get_intercept(); 139 140 if slope == other_slope { 141 return None; 142 } 143 144 let x = (other_intercept - intercept) / (slope - other_slope); 145 let y = slope * x + intercept; 146 147 let point = Position::new(x as isize, y as isize); 148 149 if !check_after || self.check_after(&point) && other.check_after(&point) { 150 Some(point) 151 } else { 152 None 153 } 154 } 155 156 pub fn start(&self) -> Position { 157 self.0 158 } 159 160 pub fn end(&self) -> Position { 161 self.1 162 } 163} 164 165impl Neg for Line { 166 type Output = Self; 167 168 fn neg(self) -> Self::Output { 169 Self(self.1, self.0) 170 } 171}