1// Copyright 2018 the Kurbo Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! A simple 2D vector.
5
6use core::fmt;
7use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
8
9use crate::common::FloatExt;
10use crate::{Point, Size};
11
12#[cfg(not(feature = "std"))]
13use 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))]
23pub struct Vec2 {
24 /// The x-coordinate.
25 pub x: f64,
26 /// The y-coordinate.
27 pub y: f64,
28}
29
30impl 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
318impl 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
325impl From<Vec2> for (f64, f64) {
326 #[inline]
327 fn from(v: Vec2) -> (f64, f64) {
328 (v.x, v.y)
329 }
330}
331
332impl 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
344impl 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
354impl 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
366impl 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
376impl 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
388impl 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
398impl Mul<Vec2> for f64 {
399 type Output = Vec2;
400
401 #[inline]
402 fn mul(self, other: Vec2) -> Vec2 {
403 other * self
404 }
405}
406
407impl 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
420impl DivAssign<f64> for Vec2 {
421 #[inline]
422 fn div_assign(&mut self, other: f64) {
423 self.mul_assign(other.recip());
424 }
425}
426
427impl 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
439impl 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")]
451impl 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")]
459impl 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)]
467mod 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