1use num::{Num, NumCast};
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5
6#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub struct Point<T> {
9 pub x: T,
11 pub y: T,
13}
14
15impl<T> Point<T> {
16 pub fn new(x: T, y: T) -> Point<T> {
18 Point::<T> { x, y }
19 }
20}
21
22impl<T: Num> Add for Point<T> {
23 type Output = Self;
24
25 fn add(self, other: Point<T>) -> Point<T> {
26 Point::new(self.x + other.x, self.y + other.y)
27 }
28}
29
30impl<T: Num + Copy> AddAssign for Point<T> {
31 fn add_assign(&mut self, rhs: Self) {
32 self.x = self.x + rhs.x;
33 self.y = self.y + rhs.y;
34 }
35}
36
37impl<T: Num> Sub for Point<T> {
38 type Output = Self;
39
40 fn sub(self, other: Point<T>) -> Point<T> {
41 Point::new(self.x - other.x, self.y - other.y)
42 }
43}
44
45impl<T: Num + Copy> SubAssign for Point<T> {
46 fn sub_assign(&mut self, rhs: Self) {
47 self.x = self.x - rhs.x;
48 self.y = self.y - rhs.y;
49 }
50}
51
52impl<T: NumCast> Point<T> {
53 pub(crate) fn to_f64(&self) -> Point<f64> {
55 Point::new(self.x.to_f64().unwrap(), self.y.to_f64().unwrap())
56 }
57
58 pub(crate) fn to_i32(&self) -> Point<i32> {
60 Point::new(self.x.to_i32().unwrap(), self.y.to_i32().unwrap())
61 }
62}
63
64pub(crate) fn distance<T: NumCast>(p: Point<T>, q: Point<T>) -> f64 {
66 distance_sq(p, q).sqrt()
67}
68
69pub(crate) fn distance_sq<T: NumCast>(p: Point<T>, q: Point<T>) -> f64 {
71 let p = p.to_f64();
72 let q = q.to_f64();
73 (p.x - q.x).powf(2.0) + (p.y - q.y).powf(2.0)
74}
75
76#[derive(Debug, Copy, Clone, PartialEq)]
79pub(crate) struct Rotation {
80 sin_theta: f64,
81 cos_theta: f64,
82}
83
84impl Rotation {
85 pub(crate) fn new(theta: f64) -> Rotation {
87 let (sin_theta, cos_theta) = theta.sin_cos();
88 Rotation {
89 sin_theta,
90 cos_theta,
91 }
92 }
93}
94
95impl Point<f64> {
96 pub(crate) fn rotate(&self, rotation: Rotation) -> Point<f64> {
98 let x = self.x * rotation.cos_theta + self.y * rotation.sin_theta;
99 let y = self.y * rotation.cos_theta - self.x * rotation.sin_theta;
100 Point::new(x, y)
101 }
102
103 pub(crate) fn invert_rotation(&self, rotation: Rotation) -> Point<f64> {
105 let x = self.x * rotation.cos_theta - self.y * rotation.sin_theta;
106 let y = self.y * rotation.cos_theta + self.x * rotation.sin_theta;
107 Point::new(x, y)
108 }
109}
110
111#[derive(Debug, Copy, Clone, PartialEq)]
113pub(crate) struct Line {
114 a: f64,
115 b: f64,
116 c: f64,
117}
118
119impl Line {
120 pub fn from_points(p: Point<f64>, q: Point<f64>) -> Line {
122 let a = p.y - q.y;
123 let b = q.x - p.x;
124 let c = p.x * q.y - q.x * p.y;
125 Line { a, b, c }
126 }
127
128 pub fn distance_from_point(&self, point: Point<f64>) -> f64 {
130 let Line { a, b, c } = self;
131 (a * point.x + b * point.y + c).abs() / (a.powf(2.0) + b.powf(2.)).sqrt()
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn line_from_points() {
141 let p = Point::new(5.0, 7.0);
142 let q = Point::new(10.0, 3.0);
143 assert_eq!(
144 Line::from_points(p, q),
145 Line {
146 a: 4.0,
147 b: 5.0,
148 c: -55.0
149 }
150 );
151 }
152
153 #[test]
154 fn distance_between_line_and_point() {
155 assert_approx_eq!(
156 Line {
157 a: 8.0,
158 b: 7.0,
159 c: 5.0
160 }
161 .distance_from_point(Point::new(2.0, 3.0)),
162 3.9510276472,
163 1e-10
164 );
165 }
166}