1 | // Copyright 2019 the Kurbo Authors |
2 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
3 | |
4 | //! A 2D point. |
5 | |
6 | use core::fmt; |
7 | use core::ops::{Add, AddAssign, Sub, SubAssign}; |
8 | |
9 | use crate::common::FloatExt; |
10 | use crate::Vec2; |
11 | |
12 | #[cfg (not(feature = "std" ))] |
13 | use crate::common::FloatFuncs; |
14 | |
15 | /// A 2D point. |
16 | /// |
17 | /// This type represents a point in 2D space. It has the same layout as [`Vec2`][crate::Vec2], but |
18 | /// its meaning is different: `Vec2` represents a change in location (for example velocity). |
19 | /// |
20 | /// In general, `kurbo` overloads math operators where it makes sense, for example implementing |
21 | /// `Affine * Point` as the point under the affine transformation. However `Point + Point` and |
22 | /// `f64 * Point` are not implemented, because the operations do not make geometric sense. If you |
23 | /// need to apply these operations, then 1) check what you're doing makes geometric sense, then 2) |
24 | /// use [`Point::to_vec2`] to convert the point to a `Vec2`. |
25 | #[derive (Clone, Copy, Default, PartialEq)] |
26 | #[cfg_attr (feature = "schemars" , derive(schemars::JsonSchema))] |
27 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
28 | pub struct Point { |
29 | /// The x coordinate. |
30 | pub x: f64, |
31 | /// The y coordinate. |
32 | pub y: f64, |
33 | } |
34 | |
35 | impl Point { |
36 | /// The point (0, 0). |
37 | pub const ZERO: Point = Point::new(0., 0.); |
38 | |
39 | /// The point at the origin; (0, 0). |
40 | pub const ORIGIN: Point = Point::new(0., 0.); |
41 | |
42 | /// Create a new `Point` with the provided `x` and `y` coordinates. |
43 | #[inline ] |
44 | pub const fn new(x: f64, y: f64) -> Self { |
45 | Point { x, y } |
46 | } |
47 | |
48 | /// Convert this point into a `Vec2`. |
49 | #[inline ] |
50 | pub const fn to_vec2(self) -> Vec2 { |
51 | Vec2::new(self.x, self.y) |
52 | } |
53 | |
54 | /// Linearly interpolate between two points. |
55 | #[inline ] |
56 | pub fn lerp(self, other: Point, t: f64) -> Point { |
57 | self.to_vec2().lerp(other.to_vec2(), t).to_point() |
58 | } |
59 | |
60 | /// Determine the midpoint of two points. |
61 | #[inline ] |
62 | pub fn midpoint(self, other: Point) -> Point { |
63 | Point::new(0.5 * (self.x + other.x), 0.5 * (self.y + other.y)) |
64 | } |
65 | |
66 | /// Euclidean distance. |
67 | #[inline ] |
68 | pub fn distance(self, other: Point) -> f64 { |
69 | (self - other).hypot() |
70 | } |
71 | |
72 | /// Squared Euclidean distance. |
73 | #[inline ] |
74 | pub fn distance_squared(self, other: Point) -> f64 { |
75 | (self - other).hypot2() |
76 | } |
77 | |
78 | /// Returns a new `Point`, |
79 | /// with `x` and `y` rounded to the nearest integer. |
80 | /// |
81 | /// # Examples |
82 | /// |
83 | /// ``` |
84 | /// use kurbo::Point; |
85 | /// let a = Point::new(3.3, 3.6).round(); |
86 | /// let b = Point::new(3.0, -3.1).round(); |
87 | /// assert_eq!(a.x, 3.0); |
88 | /// assert_eq!(a.y, 4.0); |
89 | /// assert_eq!(b.x, 3.0); |
90 | /// assert_eq!(b.y, -3.0); |
91 | /// ``` |
92 | #[inline ] |
93 | pub fn round(self) -> Point { |
94 | Point::new(self.x.round(), self.y.round()) |
95 | } |
96 | |
97 | /// Returns a new `Point`, |
98 | /// with `x` and `y` rounded up to the nearest integer, |
99 | /// unless they are already an integer. |
100 | /// |
101 | /// # Examples |
102 | /// |
103 | /// ``` |
104 | /// use kurbo::Point; |
105 | /// let a = Point::new(3.3, 3.6).ceil(); |
106 | /// let b = Point::new(3.0, -3.1).ceil(); |
107 | /// assert_eq!(a.x, 4.0); |
108 | /// assert_eq!(a.y, 4.0); |
109 | /// assert_eq!(b.x, 3.0); |
110 | /// assert_eq!(b.y, -3.0); |
111 | /// ``` |
112 | #[inline ] |
113 | pub fn ceil(self) -> Point { |
114 | Point::new(self.x.ceil(), self.y.ceil()) |
115 | } |
116 | |
117 | /// Returns a new `Point`, |
118 | /// with `x` and `y` rounded down to the nearest integer, |
119 | /// unless they are already an integer. |
120 | /// |
121 | /// # Examples |
122 | /// |
123 | /// ``` |
124 | /// use kurbo::Point; |
125 | /// let a = Point::new(3.3, 3.6).floor(); |
126 | /// let b = Point::new(3.0, -3.1).floor(); |
127 | /// assert_eq!(a.x, 3.0); |
128 | /// assert_eq!(a.y, 3.0); |
129 | /// assert_eq!(b.x, 3.0); |
130 | /// assert_eq!(b.y, -4.0); |
131 | /// ``` |
132 | #[inline ] |
133 | pub fn floor(self) -> Point { |
134 | Point::new(self.x.floor(), self.y.floor()) |
135 | } |
136 | |
137 | /// Returns a new `Point`, |
138 | /// with `x` and `y` rounded away from zero to the nearest integer, |
139 | /// unless they are already an integer. |
140 | /// |
141 | /// # Examples |
142 | /// |
143 | /// ``` |
144 | /// use kurbo::Point; |
145 | /// let a = Point::new(3.3, 3.6).expand(); |
146 | /// let b = Point::new(3.0, -3.1).expand(); |
147 | /// assert_eq!(a.x, 4.0); |
148 | /// assert_eq!(a.y, 4.0); |
149 | /// assert_eq!(b.x, 3.0); |
150 | /// assert_eq!(b.y, -4.0); |
151 | /// ``` |
152 | #[inline ] |
153 | pub fn expand(self) -> Point { |
154 | Point::new(self.x.expand(), self.y.expand()) |
155 | } |
156 | |
157 | /// Returns a new `Point`, |
158 | /// with `x` and `y` rounded towards zero to the nearest integer, |
159 | /// unless they are already an integer. |
160 | /// |
161 | /// # Examples |
162 | /// |
163 | /// ``` |
164 | /// use kurbo::Point; |
165 | /// let a = Point::new(3.3, 3.6).trunc(); |
166 | /// let b = Point::new(3.0, -3.1).trunc(); |
167 | /// assert_eq!(a.x, 3.0); |
168 | /// assert_eq!(a.y, 3.0); |
169 | /// assert_eq!(b.x, 3.0); |
170 | /// assert_eq!(b.y, -3.0); |
171 | /// ``` |
172 | #[inline ] |
173 | pub fn trunc(self) -> Point { |
174 | Point::new(self.x.trunc(), self.y.trunc()) |
175 | } |
176 | |
177 | /// Is this point finite? |
178 | #[inline ] |
179 | pub fn is_finite(self) -> bool { |
180 | self.x.is_finite() && self.y.is_finite() |
181 | } |
182 | |
183 | /// Is this point NaN? |
184 | #[inline ] |
185 | pub fn is_nan(self) -> bool { |
186 | self.x.is_nan() || self.y.is_nan() |
187 | } |
188 | } |
189 | |
190 | impl From<(f64, f64)> for Point { |
191 | #[inline ] |
192 | fn from(v: (f64, f64)) -> Point { |
193 | Point { x: v.0, y: v.1 } |
194 | } |
195 | } |
196 | |
197 | impl From<Point> for (f64, f64) { |
198 | #[inline ] |
199 | fn from(v: Point) -> (f64, f64) { |
200 | (v.x, v.y) |
201 | } |
202 | } |
203 | |
204 | impl Add<Vec2> for Point { |
205 | type Output = Point; |
206 | |
207 | #[inline ] |
208 | fn add(self, other: Vec2) -> Self { |
209 | Point::new(self.x + other.x, self.y + other.y) |
210 | } |
211 | } |
212 | |
213 | impl AddAssign<Vec2> for Point { |
214 | #[inline ] |
215 | fn add_assign(&mut self, other: Vec2) { |
216 | *self = Point::new(self.x + other.x, self.y + other.y) |
217 | } |
218 | } |
219 | |
220 | impl Sub<Vec2> for Point { |
221 | type Output = Point; |
222 | |
223 | #[inline ] |
224 | fn sub(self, other: Vec2) -> Self { |
225 | Point::new(self.x - other.x, self.y - other.y) |
226 | } |
227 | } |
228 | |
229 | impl SubAssign<Vec2> for Point { |
230 | #[inline ] |
231 | fn sub_assign(&mut self, other: Vec2) { |
232 | *self = Point::new(self.x - other.x, self.y - other.y) |
233 | } |
234 | } |
235 | |
236 | impl Add<(f64, f64)> for Point { |
237 | type Output = Point; |
238 | |
239 | #[inline ] |
240 | fn add(self, (x: f64, y: f64): (f64, f64)) -> Self { |
241 | Point::new(self.x + x, self.y + y) |
242 | } |
243 | } |
244 | |
245 | impl AddAssign<(f64, f64)> for Point { |
246 | #[inline ] |
247 | fn add_assign(&mut self, (x: f64, y: f64): (f64, f64)) { |
248 | *self = Point::new(self.x + x, self.y + y) |
249 | } |
250 | } |
251 | |
252 | impl Sub<(f64, f64)> for Point { |
253 | type Output = Point; |
254 | |
255 | #[inline ] |
256 | fn sub(self, (x: f64, y: f64): (f64, f64)) -> Self { |
257 | Point::new(self.x - x, self.y - y) |
258 | } |
259 | } |
260 | |
261 | impl SubAssign<(f64, f64)> for Point { |
262 | #[inline ] |
263 | fn sub_assign(&mut self, (x: f64, y: f64): (f64, f64)) { |
264 | *self = Point::new(self.x - x, self.y - y) |
265 | } |
266 | } |
267 | |
268 | impl Sub<Point> for Point { |
269 | type Output = Vec2; |
270 | |
271 | #[inline ] |
272 | fn sub(self, other: Point) -> Vec2 { |
273 | Vec2::new(self.x - other.x, self.y - other.y) |
274 | } |
275 | } |
276 | |
277 | impl fmt::Debug for Point { |
278 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
279 | write!(f, "( {:?}, {:?})" , self.x, self.y) |
280 | } |
281 | } |
282 | |
283 | impl fmt::Display for Point { |
284 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
285 | write!(formatter, "(" )?; |
286 | fmt::Display::fmt(&self.x, f:formatter)?; |
287 | write!(formatter, ", " )?; |
288 | fmt::Display::fmt(&self.y, f:formatter)?; |
289 | write!(formatter, ")" ) |
290 | } |
291 | } |
292 | |
293 | #[cfg (test)] |
294 | mod tests { |
295 | use super::*; |
296 | #[test ] |
297 | fn point_arithmetic() { |
298 | assert_eq!( |
299 | Point::new(0., 0.) - Vec2::new(10., 0.), |
300 | Point::new(-10., 0.) |
301 | ); |
302 | assert_eq!( |
303 | Point::new(0., 0.) - Point::new(-5., 101.), |
304 | Vec2::new(5., -101.) |
305 | ); |
306 | } |
307 | |
308 | #[test ] |
309 | #[allow (clippy::float_cmp)] |
310 | fn distance() { |
311 | let p1 = Point::new(0., 10.); |
312 | let p2 = Point::new(0., 5.); |
313 | assert_eq!(p1.distance(p2), 5.); |
314 | |
315 | let p1 = Point::new(-11., 1.); |
316 | let p2 = Point::new(-7., -2.); |
317 | assert_eq!(p1.distance(p2), 5.); |
318 | } |
319 | |
320 | #[test ] |
321 | fn display() { |
322 | let p = Point::new(0.12345, 9.87654); |
323 | assert_eq!(format!(" {p}" ), "(0.12345, 9.87654)" ); |
324 | |
325 | let p = Point::new(0.12345, 9.87654); |
326 | assert_eq!(format!(" {p:.2}" ), "(0.12, 9.88)" ); |
327 | } |
328 | } |
329 | |
330 | #[cfg (feature = "mint" )] |
331 | impl From<Point> for mint::Point2<f64> { |
332 | #[inline ] |
333 | fn from(p: Point) -> mint::Point2<f64> { |
334 | mint::Point2 { x: p.x, y: p.y } |
335 | } |
336 | } |
337 | |
338 | #[cfg (feature = "mint" )] |
339 | impl From<mint::Point2<f64>> for Point { |
340 | #[inline ] |
341 | fn from(p: mint::Point2<f64>) -> Point { |
342 | Point { x: p.x, y: p.y } |
343 | } |
344 | } |
345 | |