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 `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 |
|
281 | impl 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 |
|
288 | impl From<Vec2> for (f64, f64) {
|
289 | #[inline ]
|
290 | fn from(v: Vec2) -> (f64, f64) {
|
291 | (v.x, v.y)
|
292 | }
|
293 | }
|
294 |
|
295 | impl 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 |
|
307 | impl 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 |
|
317 | impl 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 |
|
329 | impl 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 |
|
339 | impl 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 |
|
351 | impl 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 |
|
361 | impl Mul<Vec2> for f64 {
|
362 | type Output = Vec2;
|
363 |
|
364 | #[inline ]
|
365 | fn mul(self, other: Vec2) -> Vec2 {
|
366 | other * self
|
367 | }
|
368 | }
|
369 |
|
370 | impl 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 |
|
383 | impl DivAssign<f64> for Vec2 {
|
384 | #[inline ]
|
385 | fn div_assign(&mut self, other: f64) {
|
386 | self.mul_assign(other.recip());
|
387 | }
|
388 | }
|
389 |
|
390 | impl 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 |
|
402 | impl 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" )]
|
414 | impl 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" )]
|
422 | impl 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)]
|
430 | mod 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 | |