| 1 | // Copyright 2018 the Kurbo Authors |
| 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
| 3 | |
| 4 | //! A simple 2D vector. |
| 5 | |
| 6 | use core::fmt; |
| 7 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; |
| 8 | |
| 9 | use crate::common::FloatExt; |
| 10 | use crate::{Point, Size}; |
| 11 | |
| 12 | #[cfg (not(feature = "std" ))] |
| 13 | use crate::common::FloatFuncs; |
| 14 | |
| 15 | /// A 2D vector. |
| 16 | /// |
| 17 | /// This is intended primarily for a vector in the mathematical sense, |
| 18 | /// but it can be interpreted as a translation, and converted to and |
| 19 | /// from a [`Point`] (vector relative to the origin) and [`Size`]. |
| 20 | #[derive (Clone, Copy, Default, Debug, PartialEq)] |
| 21 | #[cfg_attr (feature = "schemars" , derive(schemars::JsonSchema))] |
| 22 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
| 23 | pub struct Vec2 { |
| 24 | /// The x-coordinate. |
| 25 | pub x: f64, |
| 26 | /// The y-coordinate. |
| 27 | pub y: f64, |
| 28 | } |
| 29 | |
| 30 | impl Vec2 { |
| 31 | /// The vector (0, 0). |
| 32 | pub const ZERO: Vec2 = Vec2::new(0., 0.); |
| 33 | |
| 34 | /// Create a new vector. |
| 35 | #[inline ] |
| 36 | pub const fn new(x: f64, y: f64) -> Vec2 { |
| 37 | Vec2 { x, y } |
| 38 | } |
| 39 | |
| 40 | /// Convert this vector into a [`Point`]. |
| 41 | #[inline ] |
| 42 | pub const fn to_point(self) -> Point { |
| 43 | Point::new(self.x, self.y) |
| 44 | } |
| 45 | |
| 46 | /// Convert this vector into a [`Size`]. |
| 47 | #[inline ] |
| 48 | pub const fn to_size(self) -> Size { |
| 49 | Size::new(self.x, self.y) |
| 50 | } |
| 51 | |
| 52 | /// Create a `Vec2` with the same value for x and y |
| 53 | pub(crate) const fn splat(v: f64) -> Self { |
| 54 | Vec2 { x: v, y: v } |
| 55 | } |
| 56 | |
| 57 | /// Dot product of two vectors. |
| 58 | #[inline ] |
| 59 | pub fn dot(self, other: Vec2) -> f64 { |
| 60 | self.x * other.x + self.y * other.y |
| 61 | } |
| 62 | |
| 63 | /// Cross product of two vectors. |
| 64 | /// |
| 65 | /// This is signed so that `(0, 1) × (1, 0) = 1`. |
| 66 | #[inline ] |
| 67 | pub fn cross(self, other: Vec2) -> f64 { |
| 68 | self.x * other.y - self.y * other.x |
| 69 | } |
| 70 | |
| 71 | /// Magnitude of vector. |
| 72 | /// |
| 73 | /// This is similar to `self.hypot2().sqrt()` but defers to the platform |
| 74 | /// [`f64::hypot`] method, which in general will handle the case where |
| 75 | /// `self.hypot2() > f64::MAX`. |
| 76 | /// |
| 77 | /// See [`Point::distance`] for the same operation on [`Point`]. |
| 78 | /// |
| 79 | /// # Examples |
| 80 | /// |
| 81 | /// ``` |
| 82 | /// use kurbo::Vec2; |
| 83 | /// let v = Vec2::new(3.0, 4.0); |
| 84 | /// assert_eq!(v.hypot(), 5.0); |
| 85 | /// ``` |
| 86 | #[inline ] |
| 87 | pub fn hypot(self) -> f64 { |
| 88 | self.x.hypot(self.y) |
| 89 | } |
| 90 | |
| 91 | /// Magnitude of vector. |
| 92 | /// |
| 93 | /// This is an alias for [`Vec2::hypot`]. |
| 94 | #[inline ] |
| 95 | pub fn length(self) -> f64 { |
| 96 | self.hypot() |
| 97 | } |
| 98 | |
| 99 | /// Magnitude squared of vector. |
| 100 | /// |
| 101 | /// See [`Point::distance_squared`] for the same operation on [`Point`]. |
| 102 | /// |
| 103 | /// # Examples |
| 104 | /// |
| 105 | /// ``` |
| 106 | /// use kurbo::Vec2; |
| 107 | /// let v = Vec2::new(3.0, 4.0); |
| 108 | /// assert_eq!(v.hypot2(), 25.0); |
| 109 | /// ``` |
| 110 | #[inline ] |
| 111 | pub fn hypot2(self) -> f64 { |
| 112 | self.dot(self) |
| 113 | } |
| 114 | |
| 115 | /// Magnitude squared of vector. |
| 116 | /// |
| 117 | /// This is an alias for [`Vec2::hypot2`]. |
| 118 | #[inline ] |
| 119 | pub fn length_squared(self) -> f64 { |
| 120 | self.hypot2() |
| 121 | } |
| 122 | |
| 123 | /// Find the angle in radians between this vector and the vector `Vec2 { x: 1.0, y: 0.0 }` |
| 124 | /// in the positive `y` direction. |
| 125 | /// |
| 126 | /// If the vector is interpreted as a complex number, this is the argument. |
| 127 | /// The angle is expressed in radians. |
| 128 | #[inline ] |
| 129 | pub fn atan2(self) -> f64 { |
| 130 | self.y.atan2(self.x) |
| 131 | } |
| 132 | |
| 133 | /// Find the angle in radians between this vector and the vector `Vec2 { x: 1.0, y: 0.0 }` |
| 134 | /// in the positive `y` direction. |
| 135 | /// |
| 136 | /// This is an alias for [`Vec2::atan2`]. |
| 137 | #[inline ] |
| 138 | pub fn angle(self) -> f64 { |
| 139 | self.atan2() |
| 140 | } |
| 141 | |
| 142 | /// A unit vector of the given angle. |
| 143 | /// |
| 144 | /// With `th` at zero, the result is the positive X unit vector, and |
| 145 | /// at π/2, it is the positive Y unit vector. The angle is expressed |
| 146 | /// in radians. |
| 147 | /// |
| 148 | /// Thus, in a Y-down coordinate system (as is common for graphics), |
| 149 | /// it is a clockwise rotation, and in Y-up (traditional for math), it |
| 150 | /// is anti-clockwise. This convention is consistent with |
| 151 | /// [`Affine::rotate`]. |
| 152 | /// |
| 153 | /// [`Affine::rotate`]: crate::Affine::rotate |
| 154 | #[inline ] |
| 155 | pub fn from_angle(th: f64) -> Vec2 { |
| 156 | let (th_sin, th_cos) = th.sin_cos(); |
| 157 | Vec2 { |
| 158 | x: th_cos, |
| 159 | y: th_sin, |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /// Linearly interpolate between two vectors. |
| 164 | #[inline ] |
| 165 | pub fn lerp(self, other: Vec2, t: f64) -> Vec2 { |
| 166 | self + t * (other - self) |
| 167 | } |
| 168 | |
| 169 | /// Returns a vector of [magnitude] 1.0 with the same angle as `self`; i.e. |
| 170 | /// a unit/direction vector. |
| 171 | /// |
| 172 | /// This produces `NaN` values when the magnitude is `0`. |
| 173 | /// |
| 174 | /// [magnitude]: Self::hypot |
| 175 | #[inline ] |
| 176 | pub fn normalize(self) -> Vec2 { |
| 177 | self / self.hypot() |
| 178 | } |
| 179 | |
| 180 | /// Returns a new `Vec2`, |
| 181 | /// with `x` and `y` [rounded] to the nearest integer. |
| 182 | /// |
| 183 | /// # Examples |
| 184 | /// |
| 185 | /// ``` |
| 186 | /// use kurbo::Vec2; |
| 187 | /// let a = Vec2::new(3.3, 3.6).round(); |
| 188 | /// let b = Vec2::new(3.0, -3.1).round(); |
| 189 | /// assert_eq!(a.x, 3.0); |
| 190 | /// assert_eq!(a.y, 4.0); |
| 191 | /// assert_eq!(b.x, 3.0); |
| 192 | /// assert_eq!(b.y, -3.0); |
| 193 | /// ``` |
| 194 | /// |
| 195 | /// [rounded]: f64::round |
| 196 | #[inline ] |
| 197 | pub fn round(self) -> Vec2 { |
| 198 | Vec2::new(self.x.round(), self.y.round()) |
| 199 | } |
| 200 | |
| 201 | /// Returns a new `Vec2`, |
| 202 | /// with `x` and `y` [rounded up] to the nearest integer, |
| 203 | /// unless they are already an integer. |
| 204 | /// |
| 205 | /// # Examples |
| 206 | /// |
| 207 | /// ``` |
| 208 | /// use kurbo::Vec2; |
| 209 | /// let a = Vec2::new(3.3, 3.6).ceil(); |
| 210 | /// let b = Vec2::new(3.0, -3.1).ceil(); |
| 211 | /// assert_eq!(a.x, 4.0); |
| 212 | /// assert_eq!(a.y, 4.0); |
| 213 | /// assert_eq!(b.x, 3.0); |
| 214 | /// assert_eq!(b.y, -3.0); |
| 215 | /// ``` |
| 216 | /// |
| 217 | /// [rounded up]: f64::ceil |
| 218 | #[inline ] |
| 219 | pub fn ceil(self) -> Vec2 { |
| 220 | Vec2::new(self.x.ceil(), self.y.ceil()) |
| 221 | } |
| 222 | |
| 223 | /// Returns a new `Vec2`, |
| 224 | /// with `x` and `y` [rounded down] to the nearest integer, |
| 225 | /// unless they are already an integer. |
| 226 | /// |
| 227 | /// # Examples |
| 228 | /// |
| 229 | /// ``` |
| 230 | /// use kurbo::Vec2; |
| 231 | /// let a = Vec2::new(3.3, 3.6).floor(); |
| 232 | /// let b = Vec2::new(3.0, -3.1).floor(); |
| 233 | /// assert_eq!(a.x, 3.0); |
| 234 | /// assert_eq!(a.y, 3.0); |
| 235 | /// assert_eq!(b.x, 3.0); |
| 236 | /// assert_eq!(b.y, -4.0); |
| 237 | /// ``` |
| 238 | /// |
| 239 | /// [rounded down]: f64::floor |
| 240 | #[inline ] |
| 241 | pub fn floor(self) -> Vec2 { |
| 242 | Vec2::new(self.x.floor(), self.y.floor()) |
| 243 | } |
| 244 | |
| 245 | /// Returns a new `Vec2`, |
| 246 | /// with `x` and `y` [rounded away] from zero to the nearest integer, |
| 247 | /// unless they are already an integer. |
| 248 | /// |
| 249 | /// # Examples |
| 250 | /// |
| 251 | /// ``` |
| 252 | /// use kurbo::Vec2; |
| 253 | /// let a = Vec2::new(3.3, 3.6).expand(); |
| 254 | /// let b = Vec2::new(3.0, -3.1).expand(); |
| 255 | /// assert_eq!(a.x, 4.0); |
| 256 | /// assert_eq!(a.y, 4.0); |
| 257 | /// assert_eq!(b.x, 3.0); |
| 258 | /// assert_eq!(b.y, -4.0); |
| 259 | /// ``` |
| 260 | /// |
| 261 | /// [rounded away]: FloatExt::expand |
| 262 | #[inline ] |
| 263 | pub fn expand(self) -> Vec2 { |
| 264 | Vec2::new(self.x.expand(), self.y.expand()) |
| 265 | } |
| 266 | |
| 267 | /// Returns a new `Vec2`, |
| 268 | /// with `x` and `y` [rounded towards] zero to the nearest integer, |
| 269 | /// unless they are already an integer. |
| 270 | /// |
| 271 | /// # Examples |
| 272 | /// |
| 273 | /// ``` |
| 274 | /// use kurbo::Vec2; |
| 275 | /// let a = Vec2::new(3.3, 3.6).trunc(); |
| 276 | /// let b = Vec2::new(3.0, -3.1).trunc(); |
| 277 | /// assert_eq!(a.x, 3.0); |
| 278 | /// assert_eq!(a.y, 3.0); |
| 279 | /// assert_eq!(b.x, 3.0); |
| 280 | /// assert_eq!(b.y, -3.0); |
| 281 | /// ``` |
| 282 | /// |
| 283 | /// [rounded towards]: f64::trunc |
| 284 | #[inline ] |
| 285 | pub fn trunc(self) -> Vec2 { |
| 286 | Vec2::new(self.x.trunc(), self.y.trunc()) |
| 287 | } |
| 288 | |
| 289 | /// Is this `Vec2` [finite]? |
| 290 | /// |
| 291 | /// [finite]: f64::is_finite |
| 292 | #[inline ] |
| 293 | pub fn is_finite(self) -> bool { |
| 294 | self.x.is_finite() && self.y.is_finite() |
| 295 | } |
| 296 | |
| 297 | /// Is this `Vec2` [`NaN`]? |
| 298 | /// |
| 299 | /// [`NaN`]: f64::is_nan |
| 300 | #[inline ] |
| 301 | pub fn is_nan(self) -> bool { |
| 302 | self.x.is_nan() || self.y.is_nan() |
| 303 | } |
| 304 | |
| 305 | /// Divides this `Vec2` by a scalar. |
| 306 | /// |
| 307 | /// Unlike the division by scalar operator, which multiplies by the |
| 308 | /// reciprocal for performance, this performs the division |
| 309 | /// per-component for consistent rounding behavior. |
| 310 | pub(crate) fn div_exact(self, divisor: f64) -> Vec2 { |
| 311 | Vec2 { |
| 312 | x: self.x / divisor, |
| 313 | y: self.y / divisor, |
| 314 | } |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | impl From<(f64, f64)> for Vec2 { |
| 319 | #[inline ] |
| 320 | fn from(v: (f64, f64)) -> Vec2 { |
| 321 | Vec2 { x: v.0, y: v.1 } |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | impl From<Vec2> for (f64, f64) { |
| 326 | #[inline ] |
| 327 | fn from(v: Vec2) -> (f64, f64) { |
| 328 | (v.x, v.y) |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | impl Add for Vec2 { |
| 333 | type Output = Vec2; |
| 334 | |
| 335 | #[inline ] |
| 336 | fn add(self, other: Vec2) -> Vec2 { |
| 337 | Vec2 { |
| 338 | x: self.x + other.x, |
| 339 | y: self.y + other.y, |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | impl AddAssign for Vec2 { |
| 345 | #[inline ] |
| 346 | fn add_assign(&mut self, other: Vec2) { |
| 347 | *self = Vec2 { |
| 348 | x: self.x + other.x, |
| 349 | y: self.y + other.y, |
| 350 | } |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | impl Sub for Vec2 { |
| 355 | type Output = Vec2; |
| 356 | |
| 357 | #[inline ] |
| 358 | fn sub(self, other: Vec2) -> Vec2 { |
| 359 | Vec2 { |
| 360 | x: self.x - other.x, |
| 361 | y: self.y - other.y, |
| 362 | } |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | impl SubAssign for Vec2 { |
| 367 | #[inline ] |
| 368 | fn sub_assign(&mut self, other: Vec2) { |
| 369 | *self = Vec2 { |
| 370 | x: self.x - other.x, |
| 371 | y: self.y - other.y, |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | impl Mul<f64> for Vec2 { |
| 377 | type Output = Vec2; |
| 378 | |
| 379 | #[inline ] |
| 380 | fn mul(self, other: f64) -> Vec2 { |
| 381 | Vec2 { |
| 382 | x: self.x * other, |
| 383 | y: self.y * other, |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | impl MulAssign<f64> for Vec2 { |
| 389 | #[inline ] |
| 390 | fn mul_assign(&mut self, other: f64) { |
| 391 | *self = Vec2 { |
| 392 | x: self.x * other, |
| 393 | y: self.y * other, |
| 394 | }; |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | impl Mul<Vec2> for f64 { |
| 399 | type Output = Vec2; |
| 400 | |
| 401 | #[inline ] |
| 402 | fn mul(self, other: Vec2) -> Vec2 { |
| 403 | other * self |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | impl Div<f64> for Vec2 { |
| 408 | type Output = Vec2; |
| 409 | |
| 410 | /// Note: division by a scalar is implemented by multiplying by the reciprocal. |
| 411 | /// |
| 412 | /// This is more efficient but has different roundoff behavior than division. |
| 413 | #[inline ] |
| 414 | #[allow (clippy::suspicious_arithmetic_impl)] |
| 415 | fn div(self, other: f64) -> Vec2 { |
| 416 | self * other.recip() |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | impl DivAssign<f64> for Vec2 { |
| 421 | #[inline ] |
| 422 | fn div_assign(&mut self, other: f64) { |
| 423 | self.mul_assign(other.recip()); |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | impl Neg for Vec2 { |
| 428 | type Output = Vec2; |
| 429 | |
| 430 | #[inline ] |
| 431 | fn neg(self) -> Vec2 { |
| 432 | Vec2 { |
| 433 | x: -self.x, |
| 434 | y: -self.y, |
| 435 | } |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | impl fmt::Display for Vec2 { |
| 440 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| 441 | write!(formatter, "𝐯=(" )?; |
| 442 | fmt::Display::fmt(&self.x, f:formatter)?; |
| 443 | write!(formatter, ", " )?; |
| 444 | fmt::Display::fmt(&self.y, f:formatter)?; |
| 445 | write!(formatter, ")" ) |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | // Conversions to and from mint |
| 450 | #[cfg (feature = "mint" )] |
| 451 | impl From<Vec2> for mint::Vector2<f64> { |
| 452 | #[inline ] |
| 453 | fn from(p: Vec2) -> mint::Vector2<f64> { |
| 454 | mint::Vector2 { x: p.x, y: p.y } |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | #[cfg (feature = "mint" )] |
| 459 | impl From<mint::Vector2<f64>> for Vec2 { |
| 460 | #[inline ] |
| 461 | fn from(p: mint::Vector2<f64>) -> Vec2 { |
| 462 | Vec2 { x: p.x, y: p.y } |
| 463 | } |
| 464 | } |
| 465 | |
| 466 | #[cfg (test)] |
| 467 | mod tests { |
| 468 | use super::*; |
| 469 | #[test ] |
| 470 | fn display() { |
| 471 | let v = Vec2::new(1.2332421, 532.10721213123); |
| 472 | let s = format!("{v:.2}" ); |
| 473 | assert_eq!(s.as_str(), "𝐯=(1.23, 532.11)" ); |
| 474 | } |
| 475 | } |
| 476 | |