| 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 | /// |
| 68 | /// See [`Vec2::hypot`] for the same operation on [`Vec2`]. |
| 69 | #[inline ] |
| 70 | pub fn distance(self, other: Point) -> f64 { |
| 71 | (self - other).hypot() |
| 72 | } |
| 73 | |
| 74 | /// Squared Euclidean distance. |
| 75 | /// |
| 76 | /// See [`Vec2::hypot2`] for the same operation on [`Vec2`]. |
| 77 | #[inline ] |
| 78 | pub fn distance_squared(self, other: Point) -> f64 { |
| 79 | (self - other).hypot2() |
| 80 | } |
| 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 | pub fn round(self) -> Point { |
| 99 | Point::new(self.x.round(), self.y.round()) |
| 100 | } |
| 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 | pub fn ceil(self) -> Point { |
| 121 | Point::new(self.x.ceil(), self.y.ceil()) |
| 122 | } |
| 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 | pub fn floor(self) -> Point { |
| 143 | Point::new(self.x.floor(), self.y.floor()) |
| 144 | } |
| 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 | pub fn expand(self) -> Point { |
| 165 | Point::new(self.x.expand(), self.y.expand()) |
| 166 | } |
| 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 | pub fn trunc(self) -> Point { |
| 187 | Point::new(self.x.trunc(), self.y.trunc()) |
| 188 | } |
| 189 | |
| 190 | /// Is this point [finite]? |
| 191 | /// |
| 192 | /// [finite]: f64::is_finite |
| 193 | #[inline ] |
| 194 | pub fn is_finite(self) -> bool { |
| 195 | self.x.is_finite() && self.y.is_finite() |
| 196 | } |
| 197 | |
| 198 | /// Is this point [`NaN`]? |
| 199 | /// |
| 200 | /// [`NaN`]: f64::is_nan |
| 201 | #[inline ] |
| 202 | pub fn is_nan(self) -> bool { |
| 203 | self.x.is_nan() || self.y.is_nan() |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | impl From<(f32, f32)> for Point { |
| 208 | #[inline ] |
| 209 | fn from(v: (f32, f32)) -> Point { |
| 210 | Point { |
| 211 | x: v.0 as f64, |
| 212 | y: v.1 as f64, |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | impl From<(f64, f64)> for Point { |
| 218 | #[inline ] |
| 219 | fn from(v: (f64, f64)) -> Point { |
| 220 | Point { x: v.0, y: v.1 } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | impl From<Point> for (f64, f64) { |
| 225 | #[inline ] |
| 226 | fn from(v: Point) -> (f64, f64) { |
| 227 | (v.x, v.y) |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | impl Add<Vec2> for Point { |
| 232 | type Output = Point; |
| 233 | |
| 234 | #[inline ] |
| 235 | fn add(self, other: Vec2) -> Self { |
| 236 | Point::new(self.x + other.x, self.y + other.y) |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | impl AddAssign<Vec2> for Point { |
| 241 | #[inline ] |
| 242 | fn add_assign(&mut self, other: Vec2) { |
| 243 | *self = Point::new(self.x + other.x, self.y + other.y); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | impl Sub<Vec2> for Point { |
| 248 | type Output = Point; |
| 249 | |
| 250 | #[inline ] |
| 251 | fn sub(self, other: Vec2) -> Self { |
| 252 | Point::new(self.x - other.x, self.y - other.y) |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | impl SubAssign<Vec2> for Point { |
| 257 | #[inline ] |
| 258 | fn sub_assign(&mut self, other: Vec2) { |
| 259 | *self = Point::new(self.x - other.x, self.y - other.y); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | impl Add<(f64, f64)> for Point { |
| 264 | type Output = Point; |
| 265 | |
| 266 | #[inline ] |
| 267 | fn add(self, (x: f64, y: f64): (f64, f64)) -> Self { |
| 268 | Point::new(self.x + x, self.y + y) |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | impl AddAssign<(f64, f64)> for Point { |
| 273 | #[inline ] |
| 274 | fn add_assign(&mut self, (x: f64, y: f64): (f64, f64)) { |
| 275 | *self = Point::new(self.x + x, self.y + y); |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | impl Sub<(f64, f64)> for Point { |
| 280 | type Output = Point; |
| 281 | |
| 282 | #[inline ] |
| 283 | fn sub(self, (x: f64, y: f64): (f64, f64)) -> Self { |
| 284 | Point::new(self.x - x, self.y - y) |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | impl SubAssign<(f64, f64)> for Point { |
| 289 | #[inline ] |
| 290 | fn sub_assign(&mut self, (x: f64, y: f64): (f64, f64)) { |
| 291 | *self = Point::new(self.x - x, self.y - y); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | impl Sub<Point> for Point { |
| 296 | type Output = Vec2; |
| 297 | |
| 298 | #[inline ] |
| 299 | fn sub(self, other: Point) -> Vec2 { |
| 300 | Vec2::new(self.x - other.x, self.y - other.y) |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | impl fmt::Debug for Point { |
| 305 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 306 | write!(f, "( {:?}, {:?})" , self.x, self.y) |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | impl fmt::Display for Point { |
| 311 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| 312 | write!(formatter, "(" )?; |
| 313 | fmt::Display::fmt(&self.x, f:formatter)?; |
| 314 | write!(formatter, ", " )?; |
| 315 | fmt::Display::fmt(&self.y, f:formatter)?; |
| 316 | write!(formatter, ")" ) |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | #[cfg (feature = "mint" )] |
| 321 | impl From<Point> for mint::Point2<f64> { |
| 322 | #[inline ] |
| 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 ] |
| 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 | } |
| 372 | |