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 | |