Coverage Report

Created: 2025-07-23 06:50

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