imageproc/
point.rs

1//! A 2d point type.
2
3use num::{Num, NumCast};
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5
6/// A 2d point.
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub struct Point<T> {
9    /// x-coordinate.
10    pub x: T,
11    /// y-coordinate.
12    pub y: T,
13}
14
15impl<T> Point<T> {
16    /// Construct a point at (x, y).
17    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    /// Converts to a Point<f64>. Panics if the cast fails.
54    pub(crate) fn to_f64(&self) -> Point<f64> {
55        Point::new(self.x.to_f64().unwrap(), self.y.to_f64().unwrap())
56    }
57
58    /// Converts to a Point<i32>. Panics if the cast fails.
59    pub(crate) fn to_i32(&self) -> Point<i32> {
60        Point::new(self.x.to_i32().unwrap(), self.y.to_i32().unwrap())
61    }
62}
63
64/// Returns the Euclidean distance between two points.
65pub(crate) fn distance<T: NumCast>(p: Point<T>, q: Point<T>) -> f64 {
66    distance_sq(p, q).sqrt()
67}
68
69/// Returns the square of the Euclidean distance between two points.
70pub(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/// A fixed rotation. This struct exists solely to cache the values of `sin(theta)` and `cos(theta)` when
77/// applying a fixed rotation to multiple points.
78#[derive(Debug, Copy, Clone, PartialEq)]
79pub(crate) struct Rotation {
80    sin_theta: f64,
81    cos_theta: f64,
82}
83
84impl Rotation {
85    /// A rotation of `theta` radians.
86    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    /// Rotates a point.
97    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    /// Inverts a rotation.
104    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/// A line of the form Ax + By + C = 0.
112#[derive(Debug, Copy, Clone, PartialEq)]
113pub(crate) struct Line {
114    a: f64,
115    b: f64,
116    c: f64,
117}
118
119impl Line {
120    /// Returns the `Line` that passes through p and q.
121    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    /// Computes the shortest distance from this line to the given point.
129    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}