kurbo/
vec2.rs

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