1use core::{
2 iter,
3 ops::{Add, AddAssign, Sub, SubAssign},
4};
5
6use sealed::sealed;
7
8use super::{vector::Vec2, Range, Shape, __seal_shape};
9
10#[derive(Debug, Copy, Clone, PartialEq)]
11pub struct Point(Vec2);
12
13impl Point {
14 pub const ORIGIN: Self = Self(Vec2::ZERO);
15
16 #[must_use]
17 pub fn new(x: f32, y: f32) -> Self {
18 Self(Vec2::new(x, y))
19 }
20
21 #[must_use]
22 pub fn x(self) -> f32 {
23 self.0.x
24 }
25
26 #[must_use]
27 pub fn y(self) -> f32 {
28 self.0.y
29 }
30}
31
32impl From<[f32; 2]> for Point {
33 fn from([x, y]: [f32; 2]) -> Self {
34 Self::new(x, y)
35 }
36}
37
38impl From<Point> for [f32; 2] {
39 fn from(Point(v): Point) -> Self {
40 v.into()
41 }
42}
43
44impl From<(f32, f32)> for Point {
45 fn from((x, y): (f32, f32)) -> Self {
46 Point::new(x, y)
47 }
48}
49
50impl From<Point> for (f32, f32) {
51 fn from(Point(v): Point) -> Self {
52 v.into()
53 }
54}
55
56impl From<Vec2> for Point {
57 fn from(value: Vec2) -> Self {
58 Self(value)
59 }
60}
61
62impl From<Point> for Vec2 {
63 fn from(Point(v): Point) -> Self {
64 v
65 }
66}
67
68#[sealed]
69impl Shape for Point {
70 fn axes(&self) -> impl Iterator<Item = Vec2> {
71 iter::empty()
72 }
73
74 fn focals(&self) -> impl Iterator<Item = Point> {
75 iter::empty()
76 }
77
78 fn vertices(&self) -> impl Iterator<Item = Point> {
79 iter::once(*self)
80 }
81
82 fn project_on(&self, axis: Vec2) -> Range {
83 let p = self.0.dot(axis);
84 Range::from_min_max(p, p)
85 }
86}
87
88impl AddAssign<Vec2> for Point {
89 fn add_assign(&mut self, rhs: Vec2) {
90 self.0 += rhs;
91 }
92}
93
94impl Add<Vec2> for Point {
95 type Output = Self;
96 fn add(mut self, rhs: Vec2) -> Self::Output {
97 self += rhs;
98 self
99 }
100}
101
102impl SubAssign<Vec2> for Point {
103 fn sub_assign(&mut self, rhs: Vec2) {
104 self.0 -= rhs;
105 }
106}
107
108impl Sub<Vec2> for Point {
109 type Output = Self;
110 fn sub(mut self, rhs: Vec2) -> Self::Output {
111 self -= rhs;
112 self
113 }
114}
115
116#[cfg(test)]
117impl approx::AbsDiffEq for Point {
118 type Epsilon = f32;
119 fn default_epsilon() -> Self::Epsilon {
120 f32::default_epsilon()
121 }
122 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
123 self.0.abs_diff_eq(&other.0, epsilon)
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use approx::assert_abs_diff_eq;
130 use rstest::rstest;
131
132 use super::*;
133
134 #[rstest]
135 #[case(Vec2::ZERO, Vec2::ZERO, 0.0)]
136 #[case(Vec2::ZERO, Vec2::X, 0.0)]
137 #[case(Vec2::X, Vec2::X, 1.0)]
138 #[case(Vec2::X, Vec2::Y, 0.0)]
139 #[case(Vec2::Y, Vec2::Y, 1.0)]
140 #[case(Vec2::Y, Vec2::X, 0.0)]
141 #[case(Vec2::new(3.0, 4.0), Vec2::X, 3.0)]
142 #[case(Vec2::new(3.0, 4.0), Vec2::Y, 4.0)]
143 #[case(Vec2::new(3.0, 4.0),Vec2::new(3.0/5.0, 4.0/5.0),5.0)]
144 #[case(Vec2::new(3.0, 3.0),Vec2::new(2f32.sqrt(), -(2f32.sqrt())),0.0)]
145 fn test_axis_projection(
146 #[case] point: impl Into<Point>,
147 #[case] axis: Vec2,
148 #[case] expected: f32,
149 ) {
150 let expected = Range::from_min_max(expected, expected);
151 assert_abs_diff_eq!(point.into().project_on(axis), expected);
152 }
153
154 #[test]
155 fn test_sat_axes() {
156 assert_eq!(Point::from(Vec2::ZERO).axes().next(), None);
157 }
158}