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 `hypot` method, which
74 /// in general will handle the case where `self.hypot2() > f64::MAX`.
75 #[inline]
76 pub fn hypot(self) -> f64 {
77 self.x.hypot(self.y)
78 }
79
80 /// Magnitude of vector.
81 ///
82 /// This is an alias for [`Vec2::hypot`].
83 #[inline]
84 pub fn length(self) -> f64 {
85 self.hypot()
86 }
87
88 /// Magnitude squared of vector.
89 #[inline]
90 pub fn hypot2(self) -> f64 {
91 self.dot(self)
92 }
93
94 /// Magnitude squared of vector.
95 ///
96 /// This is an alias for [`Vec2::hypot2`].
97 #[inline]
98 pub fn length_squared(self) -> f64 {
99 self.hypot2()
100 }
101
102 /// Find the angle in radians between this vector and the vector `Vec2 { x: 1.0, y: 0.0 }`
103 /// in the positive `y` direction.
104 ///
105 /// If the vector is interpreted as a complex number, this is the argument.
106 /// The angle is expressed in radians.
107 #[inline]
108 pub fn atan2(self) -> f64 {
109 self.y.atan2(self.x)
110 }
111
112 /// Find the angle in radians between this vector and the vector `Vec2 { x: 1.0, y: 0.0 }`
113 /// in the positive `y` direction.
114 ///
115 /// This is an alias for [`Vec2::atan2`].
116 #[inline]
117 pub fn angle(self) -> f64 {
118 self.atan2()
119 }
120
121 /// A unit vector of the given angle.
122 ///
123 /// With `th` at zero, the result is the positive X unit vector, and
124 /// at π/2, it is the positive Y unit vector. The angle is expressed
125 /// in radians.
126 ///
127 /// Thus, in a Y-down coordinate system (as is common for graphics),
128 /// it is a clockwise rotation, and in Y-up (traditional for math), it
129 /// is anti-clockwise. This convention is consistent with
130 /// [`Affine::rotate`].
131 ///
132 /// [`Affine::rotate`]: crate::Affine::rotate
133 #[inline]
134 pub fn from_angle(th: f64) -> Vec2 {
135 let (th_sin, th_cos) = th.sin_cos();
136 Vec2 {
137 x: th_cos,
138 y: th_sin,
139 }
140 }
141
142 /// Linearly interpolate between two vectors.
143 #[inline]
144 pub fn lerp(self, other: Vec2, t: f64) -> Vec2 {
145 self + t * (other - self)
146 }
147
148 /// Returns a vector of magnitude 1.0 with the same angle as `self`; i.e.
149 /// a unit/direction vector.
150 ///
151 /// This produces `NaN` values when the magnitude is `0`.
152 #[inline]
153 pub fn normalize(self) -> Vec2 {
154 self / self.hypot()
155 }
156
157 /// Returns a new `Vec2`,
158 /// with `x` and `y` rounded to the nearest integer.
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use kurbo::Vec2;
164 /// let a = Vec2::new(3.3, 3.6).round();
165 /// let b = Vec2::new(3.0, -3.1).round();
166 /// assert_eq!(a.x, 3.0);
167 /// assert_eq!(a.y, 4.0);
168 /// assert_eq!(b.x, 3.0);
169 /// assert_eq!(b.y, -3.0);
170 /// ```
171 #[inline]
172 pub fn round(self) -> Vec2 {
173 Vec2::new(self.x.round(), self.y.round())
174 }
175
176 /// Returns a new `Vec2`,
177 /// with `x` and `y` rounded up to the nearest integer,
178 /// unless they are already an integer.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use kurbo::Vec2;
184 /// let a = Vec2::new(3.3, 3.6).ceil();
185 /// let b = Vec2::new(3.0, -3.1).ceil();
186 /// assert_eq!(a.x, 4.0);
187 /// assert_eq!(a.y, 4.0);
188 /// assert_eq!(b.x, 3.0);
189 /// assert_eq!(b.y, -3.0);
190 /// ```
191 #[inline]
192 pub fn ceil(self) -> Vec2 {
193 Vec2::new(self.x.ceil(), self.y.ceil())
194 }
195
196 /// Returns a new `Vec2`,
197 /// with `x` and `y` rounded down to the nearest integer,
198 /// unless they are already an integer.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use kurbo::Vec2;
204 /// let a = Vec2::new(3.3, 3.6).floor();
205 /// let b = Vec2::new(3.0, -3.1).floor();
206 /// assert_eq!(a.x, 3.0);
207 /// assert_eq!(a.y, 3.0);
208 /// assert_eq!(b.x, 3.0);
209 /// assert_eq!(b.y, -4.0);
210 /// ```
211 #[inline]
212 pub fn floor(self) -> Vec2 {
213 Vec2::new(self.x.floor(), self.y.floor())
214 }
215
216 /// Returns a new `Vec2`,
217 /// with `x` and `y` rounded away from zero to the nearest integer,
218 /// unless they are already an integer.
219 ///
220 /// # Examples
221 ///
222 /// ```
223 /// use kurbo::Vec2;
224 /// let a = Vec2::new(3.3, 3.6).expand();
225 /// let b = Vec2::new(3.0, -3.1).expand();
226 /// assert_eq!(a.x, 4.0);
227 /// assert_eq!(a.y, 4.0);
228 /// assert_eq!(b.x, 3.0);
229 /// assert_eq!(b.y, -4.0);
230 /// ```
231 #[inline]
232 pub fn expand(self) -> Vec2 {
233 Vec2::new(self.x.expand(), self.y.expand())
234 }
235
236 /// Returns a new `Vec2`,
237 /// with `x` and `y` rounded towards zero to the nearest integer,
238 /// unless they are already an integer.
239 ///
240 /// # Examples
241 ///
242 /// ```
243 /// use kurbo::Vec2;
244 /// let a = Vec2::new(3.3, 3.6).trunc();
245 /// let b = Vec2::new(3.0, -3.1).trunc();
246 /// assert_eq!(a.x, 3.0);
247 /// assert_eq!(a.y, 3.0);
248 /// assert_eq!(b.x, 3.0);
249 /// assert_eq!(b.y, -3.0);
250 /// ```
251 #[inline]
252 pub fn trunc(self) -> Vec2 {
253 Vec2::new(self.x.trunc(), self.y.trunc())
254 }
255
256 /// Is this Vec2 finite?
257 #[inline]
258 pub fn is_finite(self) -> bool {
259 self.x.is_finite() && self.y.is_finite()
260 }
261
262 /// Is this Vec2 NaN?
263 #[inline]
264 pub fn is_nan(self) -> bool {
265 self.x.is_nan() || self.y.is_nan()
266 }
267
268 /// Divides this Vec2 by a scalar.
269 ///
270 /// Unlike the division by scalar operator, which multiplies by the
271 /// reciprocal for performance, this performs the division
272 /// per-component for consistent rounding behavior.
273 pub(crate) fn div_exact(self, divisor: f64) -> Vec2 {
274 Vec2 {
275 x: self.x / divisor,
276 y: self.y / divisor,
277 }
278 }
279}
280
281impl From<(f64, f64)> for Vec2 {
282 #[inline]
283 fn from(v: (f64, f64)) -> Vec2 {
284 Vec2 { x: v.0, y: v.1 }
285 }
286}
287
288impl From<Vec2> for (f64, f64) {
289 #[inline]
290 fn from(v: Vec2) -> (f64, f64) {
291 (v.x, v.y)
292 }
293}
294
295impl Add for Vec2 {
296 type Output = Vec2;
297
298 #[inline]
299 fn add(self, other: Vec2) -> Vec2 {
300 Vec2 {
301 x: self.x + other.x,
302 y: self.y + other.y,
303 }
304 }
305}
306
307impl AddAssign for Vec2 {
308 #[inline]
309 fn add_assign(&mut self, other: Vec2) {
310 *self = Vec2 {
311 x: self.x + other.x,
312 y: self.y + other.y,
313 }
314 }
315}
316
317impl Sub for Vec2 {
318 type Output = Vec2;
319
320 #[inline]
321 fn sub(self, other: Vec2) -> Vec2 {
322 Vec2 {
323 x: self.x - other.x,
324 y: self.y - other.y,
325 }
326 }
327}
328
329impl SubAssign for Vec2 {
330 #[inline]
331 fn sub_assign(&mut self, other: Vec2) {
332 *self = Vec2 {
333 x: self.x - other.x,
334 y: self.y - other.y,
335 }
336 }
337}
338
339impl Mul<f64> for Vec2 {
340 type Output = Vec2;
341
342 #[inline]
343 fn mul(self, other: f64) -> Vec2 {
344 Vec2 {
345 x: self.x * other,
346 y: self.y * other,
347 }
348 }
349}
350
351impl MulAssign<f64> for Vec2 {
352 #[inline]
353 fn mul_assign(&mut self, other: f64) {
354 *self = Vec2 {
355 x: self.x * other,
356 y: self.y * other,
357 };
358 }
359}
360
361impl Mul<Vec2> for f64 {
362 type Output = Vec2;
363
364 #[inline]
365 fn mul(self, other: Vec2) -> Vec2 {
366 other * self
367 }
368}
369
370impl Div<f64> for Vec2 {
371 type Output = Vec2;
372
373 /// Note: division by a scalar is implemented by multiplying by the reciprocal.
374 ///
375 /// This is more efficient but has different roundoff behavior than division.
376 #[inline]
377 #[allow(clippy::suspicious_arithmetic_impl)]
378 fn div(self, other: f64) -> Vec2 {
379 self * other.recip()
380 }
381}
382
383impl DivAssign<f64> for Vec2 {
384 #[inline]
385 fn div_assign(&mut self, other: f64) {
386 self.mul_assign(other.recip());
387 }
388}
389
390impl Neg for Vec2 {
391 type Output = Vec2;
392
393 #[inline]
394 fn neg(self) -> Vec2 {
395 Vec2 {
396 x: -self.x,
397 y: -self.y,
398 }
399 }
400}
401
402impl fmt::Display for Vec2 {
403 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
404 write!(formatter, "𝐯=(")?;
405 fmt::Display::fmt(&self.x, f:formatter)?;
406 write!(formatter, ", ")?;
407 fmt::Display::fmt(&self.y, f:formatter)?;
408 write!(formatter, ")")
409 }
410}
411
412// Conversions to and from mint
413#[cfg(feature = "mint")]
414impl From<Vec2> for mint::Vector2<f64> {
415 #[inline]
416 fn from(p: Vec2) -> mint::Vector2<f64> {
417 mint::Vector2 { x: p.x, y: p.y }
418 }
419}
420
421#[cfg(feature = "mint")]
422impl From<mint::Vector2<f64>> for Vec2 {
423 #[inline]
424 fn from(p: mint::Vector2<f64>) -> Vec2 {
425 Vec2 { x: p.x, y: p.y }
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432 #[test]
433 fn display() {
434 let v = Vec2::new(1.2332421, 532.10721213123);
435 let s = format!("{v:.2}");
436 assert_eq!(s.as_str(), "𝐯=(1.23, 532.11)");
437 }
438}
439