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 | |