math_utils/
traits.rs

1//! Abstract traits
2
3use either::Either;
4#[cfg(feature = "derive_serdes")]
5use serde;
6
7use crate::num_traits as num;
8use crate::types::{Affinity, Projectivity, Sign};
9
10/// Projective completion (homogeneous coordinates)
11pub trait ProjectiveSpace <S, V> : VectorSpace <S> where
12  V : VectorSpace <S> + std::fmt::Display,
13  S : Field + std::fmt::Display
14{
15  /// Construct an augmented matrix for the affinity
16  fn homography <A> (
17    affinity : Affinity <S, A, A, <A::Vector as Module <S>>::LinearEndo>
18  ) -> Projectivity <S, V, V, Self, Self, Self::LinearEndo> where
19    A : AffineSpace <S, Vector=V>;
20  /// Return the projective completion (homogeneous coordinates) of the affine point or
21  /// vector
22  fn homogeneous <A> (point_or_vector : Either <A::Point, A::Vector>) -> Self where
23    A : AffineSpace <S, Vector=V>,
24    V : GroupAction <A::Point>;
25}
26
27/// `AffineSpace` with translations in a (Euclidean) real inner product space
28pub trait EuclideanSpace <S : Real> : AffineSpace <S> where
29  Self::Vector : InnerProductSpace <S>
30{
31  fn origin() -> Self::Point {
32    use num::Zero;
33    Self::Point::from_vector (Self::Vector::zero())
34  }
35}
36
37/// Space of `Point` and `Vector` (translations)
38pub trait AffineSpace <S : Field> {
39  type Point  : Point <Self::Vector>;
40  type Vector : VectorSpace <S> + GroupAction <Self::Point>;
41}
42
43impl <S, V> AffineSpace <S> for V where
44  S : Field,
45  V : VectorSpace <S> + GroupAction <V> + Point <V>
46{
47  type Point  = Self;
48  type Vector = Self;
49}
50
51/// Point types convertible to and from a vector type, with difference function that
52/// follows from the free and transitive group action
53pub trait Point <V> : Sized + std::ops::Sub <Self, Output=V> where
54  V : AdditiveGroup + GroupAction <Self>
55{
56  fn to_vector (self) -> V;
57  fn from_vector (vector : V) -> Self;
58  fn origin() -> Self {
59    Self::from_vector (V::zero())
60  }
61}
62
63/// (Right) action of a group on a set
64pub trait GroupAction <X> : Group {
65  fn action (self, x : X) -> X;
66}
67
68impl <G> GroupAction <G> for G where G : Group {
69  #[expect(clippy::renamed_function_params)]
70  fn action (self, g : Self) -> Self {
71    Self::operation (g, self)
72  }
73}
74
75/// Bilinear form on a `VectorSpace`
76pub trait InnerProductSpace <S : Field> : VectorSpace <S> + Dot <S> {
77  fn inner_product (self, other : Self) -> S {
78    self.dot (other)
79  }
80}
81
82impl <V, S> NormedVectorSpace <S> for V where
83  V : InnerProductSpace <S>,
84  S : Field
85{
86  fn norm_squared (self) -> S {
87    self.self_dot()
88  }
89}
90
91/// Scalar product (bilinear form) on a `Module`
92pub trait Dot <S : Ring> : Module <S> {
93  fn dot (self, other : Self) -> S;
94  /// Self dot product
95  fn self_dot (self) -> S {
96    self.dot (self)
97  }
98}
99
100/// `VectorSpace` with vector length/magnitude function
101pub trait NormedVectorSpace <S : Field> : VectorSpace <S> {
102  fn norm_squared (self) -> S;
103  fn norm (self) -> S where S : Sqrt {
104    self.norm_squared().sqrt()
105  }
106  fn normalize (self) -> Self where S : Sqrt {
107    self / self.norm()
108  }
109  fn unit_sigvec (self) -> Self where S : SignedExt + Sqrt {
110    let v = self.sigvec();
111    if v.is_zero() {
112      v
113    } else {
114      v.normalize()
115    }
116  }
117}
118
119impl <V, S> MetricSpace <S> for V where
120  V : NormedVectorSpace <S>,
121  S : Field
122{
123  fn distance_squared (self, other : Self) -> S {
124    (self - other).norm_squared()
125  }
126}
127
128/// Set of points with distance function
129pub trait MetricSpace <S> : Sized {
130  fn distance_squared (self, other : Self) -> S;
131  fn distance (self, other : Self) -> S where S : Sqrt {
132    self.distance_squared (other).sqrt()
133  }
134}
135
136/// Module with scalars taken from a `Field`
137pub trait VectorSpace <S : Field> : Module <S> + std::ops::Div <S, Output=Self> + Copy {
138  // required
139  fn map <F> (self, f : F) -> Self where F : FnMut (S) -> S;
140  // provided
141  /// Map `signum_or_zero` over each element of the given vector
142  fn sigvec (self) -> Self where S : SignedExt {
143    self.map (SignedExt::signum_or_zero)
144  }
145}
146
147/// Additive group combined with scalar multiplication
148pub trait Module <S : Ring> : AdditiveGroup + std::ops::Mul <S, Output=Self> + Copy {
149  /// Linear endomorphism represented by a square matrix type
150  type LinearEndo : LinearMap <S, Self, Self> + MultiplicativeMonoid;
151}
152
153/// Module homomorphism
154pub trait LinearMap <S, V, W> : std::ops::Mul <V, Output=W> + Copy where
155  S : Ring,
156  V : Module <S>,
157  W : Module <S>
158{
159  fn determinant (self) -> S;
160  fn transpose   (self) -> Self;
161}
162
163/// `Field` with special functions and partial ordering
164pub trait Real : Field + SignedExt + Exp + Sqrt + Trig + MinMax {
165  // TODO: more constants
166  fn pi() -> Self;
167  fn frac_pi_3() -> Self;
168  fn sqrt_3() -> Self;
169  fn frac_1_sqrt_3() -> Self;
170  fn floor (self) -> Self;
171  fn ceil (self) -> Self;
172  fn trunc (self) -> Self;
173  fn fract (self) -> Self;
174}
175
176/// A (commutative) `Ring` where $1 \neq 0$ and all non-zero elements are invertible
177pub trait Field : Ring + MultiplicativeGroup + Powi + Powf {
178  // some generic constants
179  fn half() -> Self {
180    Self::one() / Self::two()
181  }
182  fn two() -> Self {
183    Self::one() + Self::one()
184  }
185  fn three() -> Self {
186    Self::one() + Self::one() + Self::one()
187  }
188  fn four() -> Self {
189    Self::two() * Self::two()
190  }
191  fn five() -> Self {
192    Self::two() + Self::three()
193  }
194  fn six() -> Self {
195    Self::two() * Self::three()
196  }
197  fn seven() -> Self {
198    Self::three() + Self::four()
199  }
200  fn eight() -> Self {
201    Self::two() * Self::two() * Self::two()
202  }
203  fn nine() -> Self {
204    Self::three() * Self::three()
205  }
206  fn ten() -> Self {
207    Self::two() * Self::five()
208  }
209}
210
211/// Interface for a group with identity represented by `one`, operation defined by `*`
212/// and `/`
213pub trait MultiplicativeGroup : MultiplicativeMonoid +
214  std::ops::Div <Self, Output=Self> + std::ops::DivAssign + num::Inv <Output=Self>
215{ }
216
217/// Ring of integers
218pub trait Integer : Ring + num::PrimInt + num::Signed { }
219
220/// Interface for a (partially ordered) ring as a combination of an additive group and a
221/// distributive multiplication operation
222pub trait Ring : Copy + PartialOrd + MinMax + AdditiveGroup +
223  std::ops::Mul <Self, Output=Self> + std::ops::MulAssign + num::Signed +
224  num::MulAdd <Self, Self, Output=Self> + num::MulAddAssign <Self, Self>
225{
226  fn squared (self) -> Self {
227    self * self
228  }
229  fn cubed (self) -> Self {
230    self * self * self
231  }
232}
233
234/// Interface for a group with identity represented by `zero`, and operation defined by
235/// `+` and `-`
236pub trait AdditiveGroup : AdditiveMonoid +
237  std::ops::Sub <Self, Output=Self> + std::ops::SubAssign + std::ops::Neg <Output=Self>
238{ }
239
240/// Set with identity, inverses, and (associative) binary operation
241pub trait Group : PartialEq + std::ops::Neg <Output=Self> {
242  fn identity() -> Self;
243  fn operation (a : Self, b : Self) -> Self;
244}
245
246/// Set with identity represented by `one` and (associative) binary operation defined by
247/// `*`
248pub trait MultiplicativeMonoid : Sized + PartialEq +
249  std::ops::Mul <Self, Output=Self> + std::ops::MulAssign + num::One
250{ }
251
252/// Set with identity represented by `zero` and (associative) binary operation defined
253/// by `+`
254pub trait AdditiveMonoid : Sized + PartialEq +
255  std::ops::Add <Self, Output=Self> + std::ops::AddAssign + num::Zero
256{ }
257
258/// Interface for angle units
259pub trait Angle <S : Real> : Clone + Copy + PartialEq + PartialOrd + Sized +
260  AdditiveGroup + std::ops::Div <Self, Output=S> + std::ops::Mul <S, Output=Self> +
261  std::ops::Div <S, Output=Self> + std::ops::Rem <Self, Output=Self>
262{
263  // required
264  /// Full rotation
265  fn full_turn() -> Self;
266  // provided
267  /// Half rotation
268  fn half_turn() -> Self {
269    Self::full_turn() / S::two()
270  }
271  /// Restrict to `(-half_turn, half_turn]`
272  fn wrap_signed (self) -> Self {
273    let out = (self + Self::half_turn()).wrap_unsigned() - Self::half_turn();
274    if out == -Self::half_turn() {
275      Self::half_turn()
276    } else {
277      out
278    }
279  }
280  /// Restrict to `[0, full_turn)`
281  fn wrap_unsigned (self) -> Self {
282    if self >= Self::full_turn() {
283      self % Self::full_turn()
284    } else if self < Self::zero() {
285      self + Self::full_turn() * ((self / Self::full_turn()).trunc().abs() + S::one())
286    } else {
287      self
288    }
289  }
290}
291
292/// Unsigned integer power function
293pub trait Pow {
294  fn pow (self, exp : u32) -> Self;
295}
296/// Signed integer power function
297pub trait Powi {
298  fn powi (self, n : i32) -> Self;
299}
300/// Fractional power function
301pub trait Powf {
302  fn powf (self, n : Self) -> Self;
303}
304/// Exponential function
305pub trait Exp {
306  fn exp (self) -> Self;
307}
308/// Square root function
309pub trait Sqrt {
310  fn sqrt (self) -> Self;
311}
312/// Trigonometric functions
313pub trait Trig : Sized {
314  fn sin      (self) -> Self;
315  fn sin_cos  (self) -> (Self, Self);
316  fn cos      (self) -> Self;
317  fn tan      (self) -> Self;
318  fn asin     (self) -> Self;
319  fn acos     (self) -> Self;
320  fn atan     (self) -> Self;
321  fn atan2    (self, other : Self) -> Self;
322}
323
324/// Provides `min`, `max`, and `clamp` that are not necessarily consistent with those
325/// from `Ord`. This is provided because `f32` and `f64` do not implement `Ord`, so this
326/// trait is defined to give a uniform interface with `Ord` types.
327pub trait MinMax {
328  fn min   (self, other : Self) -> Self;
329  fn max   (self, other : Self) -> Self;
330  fn clamp (self, min : Self, max : Self) -> Self;
331}
332
333/// Function returning number representing sign of self
334pub trait SignedExt : num::Signed {
335  #[inline]
336  fn sign (self) -> Sign {
337    if self.is_zero() {
338      Sign::Zero
339    } else if self.is_positive() {
340      Sign::Positive
341    } else {
342      debug_assert!(self.is_negative());
343      Sign::Negative
344    }
345  }
346  /// Maps `0.0` to `0.0`, otherwise equal to `S::signum` (which would otherwise map
347  /// `+0.0 -> 1.0` and `-0.0 -> -1.0`)
348  #[inline]
349  fn signum_or_zero (self) -> Self where Self : num::Zero {
350    if self.is_zero() {
351      Self::zero()
352    } else {
353      self.signum()
354    }
355  }
356}
357
358/// Adds serde `Serialize` and `DeserializeOwned` constraints.
359///
360/// This makes it easier to conditionally add these constraints to type definitions when
361/// `derive_serdes` feature is enabled.
362#[cfg(not(feature = "derive_serdes"))]
363pub trait MaybeSerDes { }
364#[cfg(feature = "derive_serdes")]
365pub trait MaybeSerDes : serde::Serialize + serde::de::DeserializeOwned { }
366
367impl <
368  #[cfg(not(feature = "derive_serdes"))]
369  T,
370  #[cfg(feature = "derive_serdes")]
371  T : serde::Serialize + serde::de::DeserializeOwned
372> MaybeSerDes for T { }
373
374macro impl_integer ($type:ty) {
375  impl Integer        for $type { }
376  impl Ring           for $type { }
377  impl AdditiveGroup  for $type { }
378  impl AdditiveMonoid for $type { }
379  impl SignedExt      for $type { }
380  impl MinMax         for $type {
381    fn min (self, other : Self) -> Self {
382      Ord::min (self, other)
383    }
384    fn max (self, other : Self) -> Self {
385      Ord::max (self, other)
386    }
387    fn clamp (self, min : Self, max : Self) -> Self {
388      Ord::clamp (self, min, max)
389    }
390  }
391  impl Pow for $type {
392    fn pow (self, exp : u32) -> Self {
393      self.pow (exp)
394    }
395  }
396}
397impl_integer!(i8);
398impl_integer!(i16);
399impl_integer!(i32);
400impl_integer!(i64);
401
402macro impl_real_float ($type:ident) {
403  impl VectorSpace <Self> for $type {
404    fn map <F> (self, mut f : F) -> Self where F : FnMut (Self) -> Self {
405      f (self)
406    }
407  }
408  impl Module <Self> for $type {
409    type LinearEndo = Self;
410  }
411  impl LinearMap <Self, Self, Self> for $type {
412    fn determinant (self) -> Self {
413      self
414    }
415    fn transpose (self) -> Self {
416      self
417    }
418  }
419  impl Real for $type {
420    fn pi() -> Self {
421      std::$type::consts::PI
422    }
423    fn frac_pi_3() -> Self {
424      std::$type::consts::FRAC_PI_3
425    }
426    #[expect(clippy::excessive_precision)]
427    #[allow(clippy::allow_attributes)]
428    #[allow(clippy::cast_possible_truncation)]
429    fn sqrt_3() -> Self {
430      1.732050807568877293527446341505872366942805253810380628055f64 as $type
431    }
432    #[expect(clippy::excessive_precision)]
433    #[allow(clippy::allow_attributes)]
434    #[allow(clippy::cast_possible_truncation)]
435    fn frac_1_sqrt_3() -> Self {
436      (1.0f64 / 1.732050807568877293527446341505872366942805253810380628055f64) as $type
437    }
438    fn floor (self) -> Self {
439      self.floor()
440    }
441    fn ceil (self) -> Self {
442      self.ceil()
443    }
444    fn trunc (self) -> Self {
445      self.trunc()
446    }
447    fn fract (self) -> Self {
448      self.fract()
449    }
450  }
451  impl Field                for $type { }
452  impl MultiplicativeGroup  for $type { }
453  impl MultiplicativeMonoid for $type { }
454  impl Ring                 for $type { }
455  impl AdditiveGroup        for $type { }
456  impl AdditiveMonoid       for $type { }
457  impl SignedExt            for $type { }
458  impl MinMax               for $type {
459    fn min (self, other : Self) -> Self {
460      self.min (other)
461    }
462    fn max (self, other : Self) -> Self {
463      self.max (other)
464    }
465    fn clamp (self, min : Self, max : Self) -> Self {
466      self.clamp (min, max)
467    }
468  }
469  impl Powi for $type {
470    fn powi (self, n : i32) -> Self {
471      self.powi (n as i32)
472    }
473  }
474  impl Powf for $type {
475    fn powf (self, n : Self) -> Self {
476      self.powf (n)
477    }
478  }
479  impl Exp for $type {
480    fn exp (self) -> Self {
481      self.exp()
482    }
483  }
484  impl Sqrt for $type {
485    fn sqrt (self) -> Self {
486      self.sqrt()
487    }
488  }
489  impl Trig for $type {
490    fn sin (self) -> Self {
491      self.sin()
492    }
493    fn sin_cos (self) -> (Self, Self) {
494      self.sin_cos()
495    }
496    fn cos (self) -> Self {
497      self.cos()
498    }
499    fn tan (self) -> Self {
500      self.tan()
501    }
502    fn asin (self) -> Self {
503      self.asin()
504    }
505    fn acos (self) -> Self {
506      self.acos()
507    }
508    fn atan (self) -> Self {
509      self.atan()
510    }
511    fn atan2 (self, other : Self) -> Self {
512      self.atan2 (other)
513    }
514  }
515}
516
517impl_real_float!(f32);
518impl_real_float!(f64);