rgeometry/data/
vector.rs

1use array_init::{array_init, try_array_init};
2use num_rational::BigRational;
3use num_traits::NumOps;
4use rand::distributions::{Distribution, Standard};
5use rand::Rng;
6use std::cmp::Ordering;
7use std::convert::TryFrom;
8use std::iter::Sum;
9use std::ops::AddAssign;
10use std::ops::Index;
11use std::ops::Mul;
12use std::ops::Neg;
13use std::ops::Sub;
14
15use crate::data::Point;
16use crate::{Orientation, PolygonScalar};
17
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
19#[repr(transparent)]
20pub struct Vector<T, const N: usize>(pub [T; N]);
21
22#[derive(Debug, Clone, Copy)]
23pub struct VectorView<'a, T, const N: usize>(pub &'a [T; N]);
24
25impl<T, const N: usize> Distribution<Vector<T, N>> for Standard
26where
27  Standard: Distribution<T>,
28{
29  fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vector<T, N> {
30    Vector(array_init(|_| rng.gen()))
31  }
32}
33
34impl<T, const N: usize> Vector<T, N>
35where
36  T: Clone,
37{
38  pub fn qda(&self) -> T {
39    unimplemented!();
40  }
41
42  pub fn squared_magnitude(&self) -> T
43  where
44    T: Sum + AddAssign,
45    T: Sub<T, Output = T> + Mul<Output = T>,
46  {
47    self.0.iter().map(|elt| elt.clone() * elt.clone()).sum()
48  }
49
50  pub fn map<U, F>(&self, f: F) -> Vector<U, N>
51  where
52    T: Clone,
53    F: Fn(T) -> U,
54  {
55    Vector(array_init(|i| f(self.0[i].clone())))
56  }
57
58  pub fn cast<U>(&self) -> Vector<U, N>
59  where
60    T: Clone + Into<U>,
61  {
62    Vector(array_init(|i| self.0[i].clone().into()))
63  }
64}
65
66impl<T, const N: usize> Index<usize> for Vector<T, N> {
67  type Output = T;
68  fn index(&self, index: usize) -> &T {
69    self.0.index(index)
70  }
71}
72
73impl<T, const N: usize> From<Point<T, N>> for Vector<T, N> {
74  fn from(point: Point<T, N>) -> Vector<T, N> {
75    Vector(point.array)
76  }
77}
78
79impl<'a, T, const N: usize> From<&'a Point<T, N>> for &'a Vector<T, N> {
80  fn from(point: &Point<T, N>) -> &Vector<T, N> {
81    unsafe { &*(point as *const Point<T, N> as *const Vector<T, N>) }
82  }
83}
84
85impl<'a, T, const N: usize> From<&'a Point<T, N>> for VectorView<'a, T, N> {
86  fn from(point: &Point<T, N>) -> VectorView<'_, T, N> {
87    VectorView(&point.array)
88  }
89}
90
91impl<const N: usize> TryFrom<Vector<f64, N>> for Vector<BigRational, N> {
92  type Error = ();
93  fn try_from(point: Vector<f64, N>) -> Result<Vector<BigRational, N>, ()> {
94    Ok(Vector(try_array_init(|i| {
95      BigRational::from_float(point[i]).ok_or(())
96    })?))
97  }
98}
99
100impl<T> Vector<T, 2> {
101  // Unit vector pointing to the right.
102  pub fn unit_right() -> Vector<T, 2>
103  where
104    T: PolygonScalar,
105  {
106    Vector([T::from_constant(1), T::from_constant(0)])
107  }
108
109  pub fn ccw_cmp_around(&self, p: &Vector<T, 2>, q: &Vector<T, 2>) -> Ordering
110  where
111    T: PolygonScalar,
112  {
113    self.ccw_cmp_around_with(&Vector([T::from_constant(1), T::from_constant(0)]), p, q)
114  }
115  pub fn ccw_cmp_around_with(
116    &self,
117    z: &Vector<T, 2>,
118    p: &Vector<T, 2>,
119    q: &Vector<T, 2>,
120  ) -> Ordering
121  where
122    T: PolygonScalar,
123  {
124    Orientation::ccw_cmp_around_with(z, &self.0, &p.0, &q.0)
125  }
126
127  // FIXME: rename to sort_around_origin
128  // FIXME: sort by magnitude if two vectors have the same angle.
129  pub fn sort_around(pts: &mut [Vector<T, 2>])
130  where
131    T: PolygonScalar,
132  {
133    let origin = [T::from_constant(0), T::from_constant(0)];
134    pts.sort_unstable_by(|a, b| {
135      Orientation::ccw_cmp_around_with(
136        &Vector([T::from_constant(1), T::from_constant(0)]),
137        &origin,
138        &a.0,
139        &b.0,
140      )
141    })
142    // L.sortBy (ccwCmpAround c <> cmpByDistanceTo c)
143  }
144
145  pub fn cmp_along(&self, p: &Point<T, 2>, q: &Point<T, 2>) -> Ordering
146  where
147    T: PolygonScalar,
148  {
149    // Rotate the vector 90 degrees counterclockwise.
150    match Orientation::along_perp_vector(&p.array, self, &q.array) {
151      Orientation::CounterClockWise => Ordering::Greater,
152      Orientation::ClockWise => Ordering::Less,
153      Orientation::CoLinear => Ordering::Equal,
154    }
155  }
156}
157
158mod add;
159mod div;
160mod mul;
161mod sub;
162
163impl<T, const N: usize> Sum for Vector<T, N>
164where
165  T: NumOps + AddAssign + Clone + Sum,
166{
167  fn sum<I>(iter: I) -> Vector<T, N>
168  where
169    I: Iterator<Item = Vector<T, N>>,
170  {
171    let mut acc = Vector(array_init(|_| std::iter::empty().sum()));
172    for vec in iter {
173      acc += vec;
174    }
175    acc
176  }
177}
178
179impl<T, const N: usize> Neg for Vector<T, N>
180where
181  T: NumOps + Neg<Output = T> + Clone,
182{
183  type Output = Self;
184  fn neg(self) -> Self {
185    Vector(array_init(|i| self.0.index(i).clone().neg()))
186  }
187}
188
189#[cfg(test)]
190#[cfg(not(tarpaulin_include))]
191mod tests {
192  use super::*;
193
194  use proptest::prelude::*;
195  use test_strategy::proptest;
196
197  #[test]
198  fn unit_1() {
199    let v = Vector([72i8, -113]);
200    let p1 = Point { array: [-84, 93] };
201    let p2 = Point { array: [114, -71] };
202    v.cmp_along(&p1, &p2);
203  }
204
205  #[proptest]
206  fn cmp_along_fuzz(v: Vector<i8, 2>, p1: Point<i8, 2>, p2: Point<i8, 2>) {
207    v.cmp_along(&p1, &p2);
208  }
209
210  #[proptest]
211  fn cmp_along_prop_x(p1: Point<i8, 2>, p2: Point<i8, 2>) {
212    let v = Vector([1, 0]);
213    prop_assert_eq!(v.cmp_along(&p1, &p2), p1.x_coord().cmp(p2.x_coord()));
214  }
215
216  #[proptest]
217  fn cmp_along_prop_y(p1: Point<i8, 2>, p2: Point<i8, 2>) {
218    let v = Vector([0, 1]);
219    prop_assert_eq!(v.cmp_along(&p1, &p2), p1.y_coord().cmp(p2.y_coord()));
220  }
221}