wlambda/
nvec.rs

1// Copyright (c) 2020-2022 Weird Constructor <weirdconstructor@gmail.com>
2// This is a part of WLambda. See README.md and COPYING for details.
3
4/*!
5Numeric vector implementation for WLambda.
6*/
7
8use crate::vval::VVal;
9use std::ops::{Neg, Add, Sub, Div, Mul};
10
11
12/// WLambda supports Integer and Float vectors in two, three, and four dimensions.
13/// See also [VVal::nvec] and functions like [VVal::ivec2], [VVal::fvec2], ...
14#[derive(Debug, Copy, Clone, PartialEq)]
15pub enum NVec<N: NVecNum> {
16    Vec2(N, N),
17    Vec3(N, N, N),
18    Vec4(N, N, N, N),
19}
20
21/// Dimensionality of a numeric vector ([NVec])
22#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
23pub enum NVecDim {
24    Two   = 2,
25    Three = 3,
26    Four  = 4,
27}
28
29#[allow(clippy::len_without_is_empty)]
30impl NVecDim {
31    /// The number of dimensions, either 2, 3 or 4.
32    pub fn len(self) -> usize {
33        match self {
34            NVecDim::Two   => 2,
35            NVecDim::Three => 3,
36            NVecDim::Four  => 4,
37        }
38    }
39}
40
41/*
42impl<N: NVecNum> PartialEq for NVec<N> {
43    fn eq(&self, o: &Self) -> bool {
44        let (lx, ly, lz, lw) = self.clone().into_tpl();
45        let (rx, ry, rz, rw) = o.clone().into_tpl();
46        (lx, ly, lz.unwrap_or(N::zero()), lw.unwrap_or(N::zero()))
47         == (rx, ry, rz.unwrap_or(N::zero()), rw.unwrap_or(N::zero()))
48    }
49}*/
50use NVec::*;
51
52/// Number vector trait for type conversions.
53pub trait NVecNum: Sized + Copy + Clone + PartialEq {
54    /// Returns a letter representing this type
55    fn sign() -> char;
56
57    fn from_vval(v: &VVal) -> Self;
58    fn into_vval(self)     -> VVal;
59
60    fn into_f64(self)      -> f64;
61    fn from_f64(f: f64)    -> Self;
62
63    fn into_f32(self)      -> f32;
64    fn from_f32(f: f32)    -> Self;
65
66    fn into_i64(self)      -> i64;
67    fn from_i64(i: i64)    -> Self;
68
69    fn into_i32(self)      -> i32;
70    fn from_i32(i: i32)    -> Self;
71
72    /// When added/subtracted to something, has no effect
73    fn zero() -> Self;
74
75    fn add(self, o: Self) -> Self;
76    fn sub(self, o: Self) -> Self;
77    fn mul(self, o: Self) -> Self;
78    fn div(self, o: Self) -> Self;
79
80    fn from_ivec(ivec: NVec<i64>)       -> NVec<Self>;
81    fn into_fvec(s: NVec<Self>)         -> NVec<f64>;
82    fn from_fvec(fvec: NVec<f64>)       -> NVec<Self>;
83    fn from_fvec_round(fvec: NVec<f64>) -> NVec<Self>;
84}
85
86impl NVecNum for i64 {
87    #[inline]
88    fn sign()              -> char { 'i' }
89
90    #[inline]
91    fn from_vval(v: &VVal) -> Self { v.i() }
92    #[inline]
93    fn into_vval(self)     -> VVal { VVal::Int(self) }
94
95    #[inline]
96    fn into_f64(self)      -> f64  { self as f64 }
97    #[inline]
98    fn from_f64(f: f64)    -> Self { f as i64 }
99
100    #[inline]
101    fn into_f32(self)      -> f32  { self as f32 }
102    #[inline]
103    fn from_f32(f: f32)    -> Self { f as i64 }
104
105    #[inline]
106    fn into_i64(self)      -> i64  { self }
107    #[inline]
108    fn from_i64(i: i64)    -> Self { i }
109
110    #[inline]
111    fn into_i32(self)      -> i32  { self as i32 }
112    #[inline]
113    fn from_i32(i: i32)    -> Self { i as i64 }
114
115    #[inline]
116    fn zero()              -> Self { 0 }
117
118    #[inline]
119    fn add(self, o: Self)  -> Self { self + o }
120    #[inline]
121    fn sub(self, o: Self)  -> Self { self - o }
122    #[inline]
123    fn mul(self, o: Self)  -> Self { self * o }
124    #[inline]
125    fn div(self, o: Self)  -> Self { self / o }
126
127    #[inline]
128    fn from_ivec(i: NVec<i64>)       -> NVec<Self> { i }
129    #[inline]
130    fn into_fvec(i: NVec<Self>)      -> NVec<f64>  { NVec::from_vval_tpl(i.into_vval_tpl()).unwrap() }
131    #[inline]
132    fn from_fvec(f: NVec<f64>)       -> NVec<Self> { NVec::from_vval_tpl(f.into_vval_tpl()).unwrap() }
133    #[inline]
134    #[allow(clippy::many_single_char_names)]
135    fn from_fvec_round(f: NVec<f64>) -> NVec<Self> {
136        let (x, y, z, w) = f.into_tpl();
137        NVec::from_tpl((
138            x.round() as i64,
139            y.round() as i64,
140            z.map(|z| z.round() as i64),
141            w.map(|w| w.round() as i64)
142        )).unwrap()
143    }
144}
145
146impl NVecNum for f64 {
147    #[inline]
148    fn sign()              -> char { 'f' }
149
150    #[inline]
151    fn from_vval(v: &VVal) -> Self { v.f() }
152    #[inline]
153    fn into_vval(self)     -> VVal { VVal::Flt(self) }
154
155    #[inline]
156    fn into_f64(self)      -> f64  { self }
157    #[inline]
158    fn from_f64(f: f64)    -> Self { f }
159
160    #[inline]
161    fn into_f32(self)      -> f32  { self as f32 }
162    #[inline]
163    fn from_f32(f: f32)    -> Self { f as f64 }
164
165    #[inline]
166    fn into_i64(self)      -> i64  { self as i64 }
167    #[inline]
168    fn from_i64(f: i64)    -> Self { f as f64 }
169
170    #[inline]
171    fn into_i32(self)      -> i32  { self as i32 }
172    #[inline]
173    fn from_i32(f: i32)    -> Self { f as f64 }
174
175    #[inline]
176    fn zero()              -> Self { 0.0 }
177
178    #[inline]
179    fn add(self, o: Self)  -> Self { self + o }
180    #[inline]
181    fn sub(self, o: Self)  -> Self { self - o }
182    #[inline]
183    fn mul(self, o: Self)  -> Self { self * o }
184    #[inline]
185    fn div(self, o: Self)  -> Self { self / o }
186
187    #[inline]
188    fn from_ivec(i: NVec<i64>)       -> NVec<Self> { NVec::from_vval_tpl(i.into_vval_tpl()).unwrap() }
189    #[inline]
190    fn into_fvec(f: NVec<Self>)      -> NVec<f64>  { f }
191    #[inline]
192    fn from_fvec(f: NVec<f64>)       -> NVec<Self> { f }
193    #[inline]
194    fn from_fvec_round(f: NVec<f64>) -> NVec<Self> { f }
195}
196
197impl AsRef<VVal> for VVal {
198    fn as_ref(&self) -> &Self {
199        self
200    }
201}
202
203impl<N: NVecNum> NVec<N> {
204    /// The x component of the number vector as [VVal].
205    #[inline]
206    pub fn x(&self) -> VVal {
207        self.x_raw().into_vval()
208    }
209
210    /// The x component of the number vector as faw integer/float type.
211    #[inline]
212    pub fn x_raw(&self) -> N {
213        match self {
214            Vec2(x, _)       => *x,
215            Vec3(x, _, _)    => *x,
216            Vec4(x, _, _, _) => *x,
217        }
218    }
219
220    /// The y component of the number vector as [VVal].
221    #[inline]
222    pub fn y(&self) -> VVal {
223        self.y_raw().into_vval()
224    }
225
226    /// The y component of the number vector as faw integer/float type.
227    #[inline]
228    pub fn y_raw(&self) -> N {
229        match self {
230            Vec2(_, y)       => *y,
231            Vec3(_, y, _)    => *y,
232            Vec4(_, y, _, _) => *y,
233        }
234    }
235
236    /// The z component of the number vector as [VVal].
237    #[inline]
238    pub fn z(&self) -> Option<VVal> {
239        self.z_raw().map(|v| v.into_vval())
240    }
241
242    /// The z component of the number vector as faw integer/float type.
243    #[inline]
244    pub fn z_raw(&self) -> Option<N> {
245        match self {
246            Vec2(_, _)       => None,
247            Vec3(_, _, z)    => Some(*z),
248            Vec4(_, _, z, _) => Some(*z),
249        }
250    }
251
252    /// The w component of the number vector as [VVal].
253    #[inline]
254    pub fn w(&self) -> Option<VVal> {
255        self.w_raw().map(|v| v.into_vval())
256    }
257
258    /// The w component of the number vector as faw integer/float type.
259    #[inline]
260    pub fn w_raw(&self) -> Option<N> {
261        match self {
262            Vec2(_, _)       => None,
263            Vec3(_, _, _)    => None,
264            Vec4(_, _, _, w) => Some(*w),
265        }
266    }
267
268    /// Returns the dimensionality of this number vector.
269    #[inline]
270    pub fn dims(&self) -> NVecDim {
271        match self {
272            Vec2(_, _)       => NVecDim::Two,
273            Vec3(_, _, _)    => NVecDim::Three,
274            Vec4(_, _, _, _) => NVecDim::Four,
275        }
276    }
277
278    /// Convert this numeric vector into a tuple of raw integer/float types.
279    /// All vectors contain x and y components. The z and w components are optional.
280    #[inline]
281    pub fn into_tpl(self) -> (N, N, Option<N>, Option<N>) {
282        match self {
283            Vec2(x, y)       => (x, y, None   , None),
284            Vec3(x, y, z)    => (x, y, Some(z), None),
285            Vec4(x, y, z, w) => (x, y, Some(z), Some(w)),
286        }
287    }
288
289    #[inline]
290    /// A tuple of four elements representing the components of this vector.
291    /// If a component wasn't available, a `0` takes its place.
292    pub fn into_zero_tpl(self) -> (N, N, N, N) {
293        let zero = N::zero();
294        match self {
295            Vec2(x, y)       => (x, y, zero, zero),
296            Vec3(x, y, z)    => (x, y, z,    zero),
297            Vec4(x, y, z, w) => (x, y, z,    w),
298        }
299    }
300
301    /// Convert this numeric vector into a tuple of [VVal].
302    /// All vectors contain x and y components. The z and w components are optional.
303    #[inline]
304    pub fn into_vval_tpl(self) -> (VVal, VVal, Option<VVal>, Option<VVal>) {
305        match self {
306            Vec2(x, y)       => (x.into_vval(), y.into_vval(), None   , None),
307            Vec3(x, y, z)    => (x.into_vval(), y.into_vval(), Some(z.into_vval()), None),
308            Vec4(x, y, z, w) => (x.into_vval(), y.into_vval(), Some(z.into_vval()), Some(w.into_vval())),
309        }
310    }
311
312    /// Convert a tuple of raw integer/float types into an NVec.
313    #[inline]
314    pub fn from_tpl(tpl: (N, N, Option<N>, Option<N>)) -> Option<Self> {
315        Some(match tpl {
316            (x, y, None   , None)    => Vec2(x, y),
317            (x, y, Some(z), None)    => Vec3(x, y, z),
318            (x, y, Some(z), Some(w)) => Vec4(x, y, z, w),
319            _ => return None
320        })
321    }
322
323    /// Convert a tuple of [VVal] into an NVec.
324    #[inline]
325    pub fn from_vval_tpl<W: AsRef<VVal>>((x, y, z, w): (W, W, Option<W>, Option<W>)) -> Option<Self> {
326        Some(match (x.as_ref(), y.as_ref(), z, w) {
327            (x, y, None   , None)    =>
328                Vec2(N::from_vval(x), N::from_vval(y)),
329            (x, y, Some(z), None)    =>
330                Vec3(N::from_vval(x), N::from_vval(y), N::from_vval(z.as_ref())),
331            (x, y, Some(z), Some(w)) =>
332                Vec4(N::from_vval(x), N::from_vval(y), N::from_vval(z.as_ref()), N::from_vval(w.as_ref())),
333            _ => return None
334        })
335    }
336
337    /// Returns a string representation of this numeric vector.
338    #[inline]
339    pub fn s(&self) -> String {
340        match self.into_vval_tpl() {
341            (x, y, None,    None)    => format!("${}({},{})", N::sign(), x.s(), y.s()),
342            (x, y, Some(z), None)    => format!("${}({},{},{})", N::sign(), x.s(), y.s(), z.s()),
343            (x, y, Some(z), Some(w)) => format!("${}({},{},{},{})", N::sign(), x.s(), y.s(), z.s(), w.s()),
344            _ => unreachable!()
345        }
346    }
347
348    #[inline]
349    /// Converts this vector into one with three dimensions, discarding the unnecessary values.
350    pub fn vec2(self) -> Self {
351        match self {
352            Vec2(_, _)       => self,
353            Vec3(x, y, _)    => Vec2(x, y),
354            Vec4(x, y, _, _) => Vec2(x, y),
355        }
356    }
357
358    #[inline]
359    /// Converts this vector into one with three dimensions, discarding the unnecessary values
360    /// and filling in the missing values with 0s if necessary.
361    pub fn vec3(self) -> Self {
362        match self {
363            Vec2(x, y)       => Vec3(x, y, N::zero()),
364            Vec3(_, _, _)    => self,
365            Vec4(x, y, z, _) => Vec3(x, y, z),
366        }
367    }
368
369    /// Converts this vector into one with four dimensions,
370    /// filling in the missing values with 0s if necessary.
371    #[inline]
372    pub fn vec4(self) -> Self {
373        let o = N::zero();
374        match self {
375            Vec2(x, y)       => Vec4(x, y, o, o),
376            Vec3(x, y, z)    => Vec4(x, y, z, o),
377            Vec4(_, _, _, _) => self,
378        }
379    }
380
381    /// Returns the squared magnitude of this vector like `x^2 + y^2`.
382    /// See also [NVec::mag].
383    #[inline]
384    pub fn mag2(&self) -> f64 {
385        match self {
386            Vec2(x, y)       =>
387                x.into_f64().powi(2) + y.into_f64().powi(2),
388            Vec3(x, y, z)    =>
389                x.into_f64().powi(2) + y.into_f64().powi(2) + z.into_f64().powi(2),
390            Vec4(x, y, z, w) =>
391                x.into_f64().powi(2) + y.into_f64().powi(2) + z.into_f64().powi(2) + w.into_f64().powi(2),
392        }
393    }
394
395    /// Returns the magnitude of this vector also known as the mathematical
396    /// length of the vector: `sqrt(x^2 + y^2)` for instance for a 2 dimensional
397    /// vector.
398    #[inline]
399    pub fn mag(&self) -> f64 {
400        self.mag2().sqrt()
401    }
402
403    /// Convert this NVec into a normalized (unit length) vector.
404    #[inline]
405    pub fn norm(self) -> Self {
406        let m = N::from_f64(self.mag());
407        if m == N::zero() {
408            self
409        } else {
410            self / m
411        }
412    }
413
414    /// Calculates the dot product of two numerical vectors.
415    #[inline]
416    pub fn dot(self, o: NVec<N>) -> N {
417        let max_dims = self.dims().max(o.dims());
418        let (lx, ly, lz, lw) = self.into_zero_tpl();
419        let (rx, ry, rz, rw) = o.into_zero_tpl();
420
421        match max_dims {
422            NVecDim::Two   => lx.mul(rx).add(ly.mul(ry)),
423            NVecDim::Three => lx.mul(rx).add(ly.mul(ry)).add(lz.mul(rz)),
424            NVecDim::Four  => lx.mul(rx).add(ly.mul(ry)).add(lz.mul(rz)).add(lw.mul(rw)),
425        }
426    }
427
428    /// Calculates the cross product of two numerical vectors.
429    #[inline]
430    pub fn cross(self, o: NVec<N>) -> Self {
431        let a = self.into_zero_tpl();
432        let b = o.into_zero_tpl();
433
434        Vec3(
435            a.1.mul(b.2).sub(a.2.mul(b.1)),
436            a.2.mul(b.0).sub(a.0.mul(b.2)),
437            a.0.mul(b.1).sub(a.1.mul(b.0)),
438        )
439    }
440
441    /// Linear interpolation from this vector to the given vector \a o.
442    /// The parameter \a t should be between 0.0 and 1.0.
443    #[inline]
444    pub fn lerp(self, o: NVec<N>, t: f64) -> Self {
445        N::from_fvec_round(
446            (N::into_fvec(self) * (1.0 - t))
447            + (N::into_fvec(o) * t)
448        )
449    }
450
451    /// Turns the first two components of this vector into an angle in radians.
452    pub fn vec2rad(self) -> f64 {
453        N::into_f64(self.y_raw()).atan2(N::into_f64(self.x_raw()))
454    }
455
456    /// Produces a Vec2 based on the radians provided to this function as a float.
457    pub fn rad2vec(f: f64) -> Self {
458        let (y, x) = f.sin_cos();
459        N::from_fvec(Vec2(x, y))
460    }
461
462    #[inline]
463    /// The resulting NVec will almost always have a length of 1,
464    /// except for in cases where `o` and `self` are collinear opposites.
465    /// To work around this, the resulting vector may be normalized.
466    /// 
467    /// # Panics
468    /// Panics if input vectors aren't unit vectors.
469    pub fn slerp(self, o: NVec<N>, t: f64) -> Self {
470        let (p0, p1) = {
471            let (p0, p1) = (N::into_fvec(self), N::into_fvec(o));
472            // work around the edge case wherein p0 and p1 are collinear opposites
473            if (p0 + p1).mag2() == 0.0 {
474                ((p0 + Vec2(1e-5, 1e-5)).norm(), p1)
475            } else {
476                (p0, p1)
477            }
478        };
479        let omega = p0.dot(p1).acos();
480        let sin_omega = omega.sin();
481        N::from_fvec_round(
482            (p0 * (((1.0 - t) * omega).sin() / sin_omega))
483            + (p1 * ((t * omega).sin() / sin_omega))
484        )
485    }
486}
487
488#[cfg(feature = "mint")]
489macro_rules! mint_vec_to_from { ( $( $ty:ident : $to:ident -> $from:ident ; )* ) => { $(
490impl<N: NVecNum> From<mint::Vector2<$ty>> for NVec<N> {
491    fn from(o: mint::Vector2<$ty>) -> Self {
492        Vec2(N::$from(o.x), N::$from(o.y))
493    }
494}
495impl<N: NVecNum> From<mint::Vector3<$ty>> for NVec<N> {
496    fn from(o: mint::Vector3<$ty>) -> Self {
497        Vec3(N::$from(o.x), N::$from(o.y), N::$from(o.z))
498    }
499}
500impl<N: NVecNum> From<mint::Vector4<$ty>> for NVec<N> {
501    fn from(o: mint::Vector4<$ty>) -> Self {
502        Vec4(N::$from(o.x), N::$from(o.y), N::$from(o.z), N::$from(o.w))
503    }
504}
505
506impl<N: NVecNum> Into<mint::Vector2<$ty>> for NVec<N> {
507    fn into(self) -> mint::Vector2<$ty> {
508        let (x, y, _, _) = self.into_zero_tpl();
509        mint::Vector2 { x: N::$to(x), y: N::$to(y) }
510    }
511}
512impl<N: NVecNum> Into<mint::Vector3<$ty>> for NVec<N> {
513    fn into(self) -> mint::Vector3<$ty> {
514        let (x, y, z, _) = self.into_zero_tpl();
515        mint::Vector3 { x: N::$to(x), y: N::$to(y), z: N::$to(z) }
516    }
517}
518impl<N: NVecNum> Into<mint::Vector4<$ty>> for NVec<N> {
519    fn into(self) -> mint::Vector4<$ty> {
520        let (x, y, z, w) = self.into_zero_tpl();
521        mint::Vector4 { x: N::$to(x), y: N::$to(y), z: N::$to(z), w: N::$to(w) }
522    }
523}
524)* } }
525
526#[cfg(feature = "mint")]
527mint_vec_to_from! {
528    f64 : into_f64 -> from_f64;
529    f32 : into_f32 -> from_f32;
530    i64 : into_i64 -> from_i64;
531    i32 : into_i32 -> from_i32;
532}
533
534impl<N: NVecNum> Neg for NVec<N> {
535    type Output = NVec<N>;
536
537    fn neg(self) -> Self::Output {
538        self * N::from_f64(-1.0)
539    }
540}
541
542macro_rules! euler_binop { ( $( $trait:ident | $fn:ident ; )* ) => { $(
543    impl<N: NVecNum> $trait for NVec<N> {
544        type Output = Self;
545
546        #[inline]
547        fn $fn(self, o: NVec<N>) -> NVec<N> {
548            let max_dims = self.dims().max(o.dims());
549            let (lx, ly, lz, lw) = self.into_zero_tpl();
550            let (rx, ry, rz, rw) = o.into_zero_tpl();
551
552            match max_dims {
553                NVecDim::Two   => Vec2(lx.$fn(rx), ly.$fn(ry)),
554                NVecDim::Three => Vec3(lx.$fn(rx), ly.$fn(ry), lz.$fn(rz)),
555                NVecDim::Four  => Vec4(lx.$fn(rx), ly.$fn(ry), lz.$fn(rz), lw.$fn(rw)),
556            }
557        }
558    }
559)* } }
560euler_binop! {
561    Add | add;
562    Sub | sub;
563}
564
565macro_rules! scalar_binop { ( $( $trait:ident | $fn:ident ; )* ) => { $(
566    impl<N: NVecNum> $trait<N> for NVec<N> {
567        type Output = Self;
568
569        #[inline]
570        fn $fn(self, o: N) -> NVec<N> {
571            match self {
572                Vec2(x, y)       => Vec2(x.$fn(o), y.$fn(o)),
573                Vec3(x, y, z)    => Vec3(x.$fn(o), y.$fn(o), z.$fn(o)),
574                Vec4(x, y, z, w) => Vec4(x.$fn(o), y.$fn(o), z.$fn(o), w.$fn(o)),
575            }
576        }
577    }
578)* } }
579scalar_binop! {
580    Mul | mul;
581    Div | div;
582}