surpass/
point.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::ops::{Add, Div, Mul, Sub};
6use std::{f32, hash};
7
8use crate::CanonBits;
9
10#[derive(Clone, Copy, Debug)]
11pub struct Point {
12    pub x: f32,
13    pub y: f32,
14}
15
16impl Eq for Point {}
17
18impl PartialEq for Point {
19    fn eq(&self, other: &Self) -> bool {
20        self.x == other.x && self.y == other.y
21    }
22}
23
24impl hash::Hash for Point {
25    fn hash<H: hash::Hasher>(&self, state: &mut H) {
26        self.x.to_canon_bits().hash(state);
27        self.y.to_canon_bits().hash(state);
28    }
29}
30
31impl Point {
32    pub fn new(x: f32, y: f32) -> Self {
33        Self { x, y }
34    }
35
36    pub fn to_array(self) -> [f32; 2] {
37        [self.x, self.y]
38    }
39}
40
41#[allow(clippy::many_single_char_names)]
42fn approx_atan2(y: f32, x: f32) -> f32 {
43    let x_abs = x.abs();
44    let y_abs = y.abs();
45
46    let a = x_abs.min(y_abs) / x_abs.max(y_abs);
47    let s = a * a;
48    let mut r = s.mul_add(-0.046_496_473, 0.159_314_22).mul_add(s, -0.327_622_77).mul_add(s * a, a);
49
50    if y_abs > x_abs {
51        r = f32::consts::FRAC_PI_2 - r;
52    }
53
54    if x < 0.0 {
55        r = f32::consts::PI - r;
56    }
57
58    if y < 0.0 {
59        r = -r;
60    }
61
62    r
63}
64
65// Restrict the Point functions visibility as we do not want to run into the business of delivering a linear algebra package.
66impl Point {
67    pub(crate) fn len(self) -> f32 {
68        (self.x * self.x + self.y * self.y).sqrt()
69    }
70
71    pub(crate) fn angle(self) -> Option<f32> {
72        (self.len() >= f32::EPSILON).then(|| approx_atan2(self.y, self.x))
73    }
74}
75
76impl Add<Point> for Point {
77    type Output = Self;
78
79    #[inline]
80    fn add(self, other: Self) -> Self {
81        Self { x: self.x + other.x, y: self.y + other.y }
82    }
83}
84
85impl Sub<Point> for Point {
86    type Output = Self;
87
88    #[inline]
89    fn sub(self, other: Self) -> Self {
90        Self { x: self.x - other.x, y: self.y - other.y }
91    }
92}
93
94impl Mul<f32> for Point {
95    type Output = Self;
96
97    #[inline]
98    fn mul(self, other: f32) -> Self {
99        Self { x: self.x * other, y: self.y * other }
100    }
101}
102
103impl Div<f32> for Point {
104    type Output = Self;
105
106    #[inline]
107    fn div(self, other: f32) -> Self {
108        Self { x: self.x / other, y: self.y / other }
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    use std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
116
117    #[test]
118    fn add() {
119        assert_eq!(
120            Point { x: 32.0, y: 126.0 },
121            Point { x: 13.0, y: 27.0 } + Point { x: 19.0, y: 99.0 }
122        );
123    }
124
125    #[test]
126    fn sub() {
127        assert_eq!(
128            Point { x: -6.0, y: -72.0 },
129            Point { x: 13.0, y: 27.0 } - Point { x: 19.0, y: 99.0 }
130        );
131    }
132
133    #[test]
134    fn mul() {
135        assert_eq!(Point { x: 3.0, y: 21.0 }, Point { x: 1.0, y: 7.0 } * 3.0);
136    }
137
138    #[test]
139    fn div() {
140        assert_eq!(Point { x: 1.0, y: 7.0 }, Point { x: 3.0, y: 21.0 } / 3.0);
141    }
142
143    #[test]
144    fn angle() {
145        assert_eq!(Some(0.0), Point { x: 1.0, y: 0.0 }.angle());
146        assert_eq!(Some(0.0), Point { x: 1e10, y: 0.0 }.angle());
147        assert_eq!(Some(PI), Point { x: -1.0, y: 0.0 }.angle());
148        assert_eq!(Some(FRAC_PI_2), Point { x: 0.0, y: 1.0 }.angle());
149        assert_eq!(Some(-FRAC_PI_2), Point { x: 0.0, y: -1.0 }.angle());
150        assert!((FRAC_PI_4 - Point { x: 1.0, y: 1.0 }.angle().unwrap()).abs() < 1e-3);
151        assert!((-FRAC_PI_4 - Point { x: 1.0, y: -1.0 }.angle().unwrap()).abs() < 1e-3);
152    }
153}