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