/rust/registry/src/index.crates.io-6f17d22bba15001f/kurbo-0.11.3/src/point.rs
Line | Count | Source (jump to first uncovered line) |
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`], 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(always)] |
44 | 0 | pub const fn new(x: f64, y: f64) -> Self { |
45 | 0 | Point { x, y } |
46 | 0 | } |
47 | | |
48 | | /// Convert this point into a `Vec2`. |
49 | | #[inline(always)] |
50 | 0 | pub const fn to_vec2(self) -> Vec2 { |
51 | 0 | Vec2::new(self.x, self.y) |
52 | 0 | } |
53 | | |
54 | | /// Linearly interpolate between two points. |
55 | | #[inline] |
56 | 0 | pub fn lerp(self, other: Point, t: f64) -> Point { |
57 | 0 | self.to_vec2().lerp(other.to_vec2(), t).to_point() |
58 | 0 | } |
59 | | |
60 | | /// Determine the midpoint of two points. |
61 | | #[inline] |
62 | 0 | pub fn midpoint(self, other: Point) -> Point { |
63 | 0 | Point::new(0.5 * (self.x + other.x), 0.5 * (self.y + other.y)) |
64 | 0 | } Unexecuted instantiation: <kurbo::point::Point>::midpoint Unexecuted instantiation: <kurbo::point::Point>::midpoint |
65 | | |
66 | | /// Euclidean distance. |
67 | | /// |
68 | | /// See [`Vec2::hypot`] for the same operation on [`Vec2`]. |
69 | | #[inline] |
70 | 0 | pub fn distance(self, other: Point) -> f64 { |
71 | 0 | (self - other).hypot() |
72 | 0 | } |
73 | | |
74 | | /// Squared Euclidean distance. |
75 | | /// |
76 | | /// See [`Vec2::hypot2`] for the same operation on [`Vec2`]. |
77 | | #[inline] |
78 | 0 | pub fn distance_squared(self, other: Point) -> f64 { |
79 | 0 | (self - other).hypot2() |
80 | 0 | } |
81 | | |
82 | | /// Returns a new `Point`, with `x` and `y` [rounded] to the nearest integer. |
83 | | /// |
84 | | /// # Examples |
85 | | /// |
86 | | /// ``` |
87 | | /// use kurbo::Point; |
88 | | /// let a = Point::new(3.3, 3.6).round(); |
89 | | /// let b = Point::new(3.0, -3.1).round(); |
90 | | /// assert_eq!(a.x, 3.0); |
91 | | /// assert_eq!(a.y, 4.0); |
92 | | /// assert_eq!(b.x, 3.0); |
93 | | /// assert_eq!(b.y, -3.0); |
94 | | /// ``` |
95 | | /// |
96 | | /// [rounded]: f64::round |
97 | | #[inline] |
98 | 0 | pub fn round(self) -> Point { |
99 | 0 | Point::new(self.x.round(), self.y.round()) |
100 | 0 | } |
101 | | |
102 | | /// Returns a new `Point`, |
103 | | /// with `x` and `y` [rounded up] to the nearest integer, |
104 | | /// unless they are already an integer. |
105 | | /// |
106 | | /// # Examples |
107 | | /// |
108 | | /// ``` |
109 | | /// use kurbo::Point; |
110 | | /// let a = Point::new(3.3, 3.6).ceil(); |
111 | | /// let b = Point::new(3.0, -3.1).ceil(); |
112 | | /// assert_eq!(a.x, 4.0); |
113 | | /// assert_eq!(a.y, 4.0); |
114 | | /// assert_eq!(b.x, 3.0); |
115 | | /// assert_eq!(b.y, -3.0); |
116 | | /// ``` |
117 | | /// |
118 | | /// [rounded up]: f64::ceil |
119 | | #[inline] |
120 | 0 | pub fn ceil(self) -> Point { |
121 | 0 | Point::new(self.x.ceil(), self.y.ceil()) |
122 | 0 | } |
123 | | |
124 | | /// Returns a new `Point`, |
125 | | /// with `x` and `y` [rounded down] to the nearest integer, |
126 | | /// unless they are already an integer. |
127 | | /// |
128 | | /// # Examples |
129 | | /// |
130 | | /// ``` |
131 | | /// use kurbo::Point; |
132 | | /// let a = Point::new(3.3, 3.6).floor(); |
133 | | /// let b = Point::new(3.0, -3.1).floor(); |
134 | | /// assert_eq!(a.x, 3.0); |
135 | | /// assert_eq!(a.y, 3.0); |
136 | | /// assert_eq!(b.x, 3.0); |
137 | | /// assert_eq!(b.y, -4.0); |
138 | | /// ``` |
139 | | /// |
140 | | /// [rounded down]: f64::floor |
141 | | #[inline] |
142 | 0 | pub fn floor(self) -> Point { |
143 | 0 | Point::new(self.x.floor(), self.y.floor()) |
144 | 0 | } |
145 | | |
146 | | /// Returns a new `Point`, |
147 | | /// with `x` and `y` [rounded away] from zero to the nearest integer, |
148 | | /// unless they are already an integer. |
149 | | /// |
150 | | /// # Examples |
151 | | /// |
152 | | /// ``` |
153 | | /// use kurbo::Point; |
154 | | /// let a = Point::new(3.3, 3.6).expand(); |
155 | | /// let b = Point::new(3.0, -3.1).expand(); |
156 | | /// assert_eq!(a.x, 4.0); |
157 | | /// assert_eq!(a.y, 4.0); |
158 | | /// assert_eq!(b.x, 3.0); |
159 | | /// assert_eq!(b.y, -4.0); |
160 | | /// ``` |
161 | | /// |
162 | | /// [rounded away]: FloatExt::expand |
163 | | #[inline] |
164 | 0 | pub fn expand(self) -> Point { |
165 | 0 | Point::new(self.x.expand(), self.y.expand()) |
166 | 0 | } |
167 | | |
168 | | /// Returns a new `Point`, |
169 | | /// with `x` and `y` [rounded towards] zero to the nearest integer, |
170 | | /// unless they are already an integer. |
171 | | /// |
172 | | /// # Examples |
173 | | /// |
174 | | /// ``` |
175 | | /// use kurbo::Point; |
176 | | /// let a = Point::new(3.3, 3.6).trunc(); |
177 | | /// let b = Point::new(3.0, -3.1).trunc(); |
178 | | /// assert_eq!(a.x, 3.0); |
179 | | /// assert_eq!(a.y, 3.0); |
180 | | /// assert_eq!(b.x, 3.0); |
181 | | /// assert_eq!(b.y, -3.0); |
182 | | /// ``` |
183 | | /// |
184 | | /// [rounded towards]: f64::trunc |
185 | | #[inline] |
186 | 0 | pub fn trunc(self) -> Point { |
187 | 0 | Point::new(self.x.trunc(), self.y.trunc()) |
188 | 0 | } |
189 | | |
190 | | /// Is this point [finite]? |
191 | | /// |
192 | | /// [finite]: f64::is_finite |
193 | | #[inline] |
194 | 0 | pub fn is_finite(self) -> bool { |
195 | 0 | self.x.is_finite() && self.y.is_finite() |
196 | 0 | } |
197 | | |
198 | | /// Is this point [`NaN`]? |
199 | | /// |
200 | | /// [`NaN`]: f64::is_nan |
201 | | #[inline] |
202 | 0 | pub fn is_nan(self) -> bool { |
203 | 0 | self.x.is_nan() || self.y.is_nan() |
204 | 0 | } |
205 | | } |
206 | | |
207 | | impl From<(f32, f32)> for Point { |
208 | | #[inline(always)] |
209 | 0 | fn from(v: (f32, f32)) -> Point { |
210 | 0 | Point { |
211 | 0 | x: v.0 as f64, |
212 | 0 | y: v.1 as f64, |
213 | 0 | } |
214 | 0 | } |
215 | | } |
216 | | |
217 | | impl From<(f64, f64)> for Point { |
218 | | #[inline(always)] |
219 | 0 | fn from(v: (f64, f64)) -> Point { |
220 | 0 | Point { x: v.0, y: v.1 } |
221 | 0 | } |
222 | | } |
223 | | |
224 | | impl From<Point> for (f64, f64) { |
225 | | #[inline(always)] |
226 | 0 | fn from(v: Point) -> (f64, f64) { |
227 | 0 | (v.x, v.y) |
228 | 0 | } |
229 | | } |
230 | | |
231 | | impl Add<Vec2> for Point { |
232 | | type Output = Point; |
233 | | |
234 | | #[inline] |
235 | 0 | fn add(self, other: Vec2) -> Self { |
236 | 0 | Point::new(self.x + other.x, self.y + other.y) |
237 | 0 | } |
238 | | } |
239 | | |
240 | | impl AddAssign<Vec2> for Point { |
241 | | #[inline] |
242 | 0 | fn add_assign(&mut self, other: Vec2) { |
243 | 0 | *self = Point::new(self.x + other.x, self.y + other.y); |
244 | 0 | } |
245 | | } |
246 | | |
247 | | impl Sub<Vec2> for Point { |
248 | | type Output = Point; |
249 | | |
250 | | #[inline] |
251 | 0 | fn sub(self, other: Vec2) -> Self { |
252 | 0 | Point::new(self.x - other.x, self.y - other.y) |
253 | 0 | } |
254 | | } |
255 | | |
256 | | impl SubAssign<Vec2> for Point { |
257 | | #[inline] |
258 | 0 | fn sub_assign(&mut self, other: Vec2) { |
259 | 0 | *self = Point::new(self.x - other.x, self.y - other.y); |
260 | 0 | } |
261 | | } |
262 | | |
263 | | impl Add<(f64, f64)> for Point { |
264 | | type Output = Point; |
265 | | |
266 | | #[inline] |
267 | 0 | fn add(self, (x, y): (f64, f64)) -> Self { |
268 | 0 | Point::new(self.x + x, self.y + y) |
269 | 0 | } |
270 | | } |
271 | | |
272 | | impl AddAssign<(f64, f64)> for Point { |
273 | | #[inline] |
274 | 0 | fn add_assign(&mut self, (x, y): (f64, f64)) { |
275 | 0 | *self = Point::new(self.x + x, self.y + y); |
276 | 0 | } |
277 | | } |
278 | | |
279 | | impl Sub<(f64, f64)> for Point { |
280 | | type Output = Point; |
281 | | |
282 | | #[inline] |
283 | 0 | fn sub(self, (x, y): (f64, f64)) -> Self { |
284 | 0 | Point::new(self.x - x, self.y - y) |
285 | 0 | } |
286 | | } |
287 | | |
288 | | impl SubAssign<(f64, f64)> for Point { |
289 | | #[inline] |
290 | 0 | fn sub_assign(&mut self, (x, y): (f64, f64)) { |
291 | 0 | *self = Point::new(self.x - x, self.y - y); |
292 | 0 | } |
293 | | } |
294 | | |
295 | | impl Sub<Point> for Point { |
296 | | type Output = Vec2; |
297 | | |
298 | | #[inline] |
299 | 0 | fn sub(self, other: Point) -> Vec2 { |
300 | 0 | Vec2::new(self.x - other.x, self.y - other.y) |
301 | 0 | } |
302 | | } |
303 | | |
304 | | impl fmt::Debug for Point { |
305 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
306 | 0 | write!(f, "({:?}, {:?})", self.x, self.y) |
307 | 0 | } |
308 | | } |
309 | | |
310 | | impl fmt::Display for Point { |
311 | 0 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
312 | 0 | write!(formatter, "(")?; |
313 | 0 | fmt::Display::fmt(&self.x, formatter)?; |
314 | 0 | write!(formatter, ", ")?; |
315 | 0 | fmt::Display::fmt(&self.y, formatter)?; |
316 | 0 | write!(formatter, ")") |
317 | 0 | } |
318 | | } |
319 | | |
320 | | #[cfg(feature = "mint")] |
321 | | impl From<Point> for mint::Point2<f64> { |
322 | | #[inline(always)] |
323 | | fn from(p: Point) -> mint::Point2<f64> { |
324 | | mint::Point2 { x: p.x, y: p.y } |
325 | | } |
326 | | } |
327 | | |
328 | | #[cfg(feature = "mint")] |
329 | | impl From<mint::Point2<f64>> for Point { |
330 | | #[inline(always)] |
331 | | fn from(p: mint::Point2<f64>) -> Point { |
332 | | Point { x: p.x, y: p.y } |
333 | | } |
334 | | } |
335 | | |
336 | | #[cfg(test)] |
337 | | mod tests { |
338 | | use super::*; |
339 | | #[test] |
340 | | fn point_arithmetic() { |
341 | | assert_eq!( |
342 | | Point::new(0., 0.) - Vec2::new(10., 0.), |
343 | | Point::new(-10., 0.) |
344 | | ); |
345 | | assert_eq!( |
346 | | Point::new(0., 0.) - Point::new(-5., 101.), |
347 | | Vec2::new(5., -101.) |
348 | | ); |
349 | | } |
350 | | |
351 | | #[test] |
352 | | #[allow(clippy::float_cmp)] |
353 | | fn distance() { |
354 | | let p1 = Point::new(0., 10.); |
355 | | let p2 = Point::new(0., 5.); |
356 | | assert_eq!(p1.distance(p2), 5.); |
357 | | |
358 | | let p1 = Point::new(-11., 1.); |
359 | | let p2 = Point::new(-7., -2.); |
360 | | assert_eq!(p1.distance(p2), 5.); |
361 | | } |
362 | | |
363 | | #[test] |
364 | | fn display() { |
365 | | let p = Point::new(0.12345, 9.87654); |
366 | | assert_eq!(format!("{p}"), "(0.12345, 9.87654)"); |
367 | | |
368 | | let p = Point::new(0.12345, 9.87654); |
369 | | assert_eq!(format!("{p:.2}"), "(0.12, 9.88)"); |
370 | | } |
371 | | } |