vector_traits/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2023 lacklustr@protonmail.com https://github.com/eadf
3
4// This file is part of vector-traits.
5
6//! # Vector-Traits Crate
7//!
8//! `vector-traits` is a Rust crate designed to provide a set of traits for abstracting over different vector
9//! implementations and scalar types, offering a unified interface for a basic set of vector operations. This crate facilitates
10//! seamless transitions between different vector libraries and scalar precisions (e.g., `f32` and `f64`) without
11//! requiring significant code modifications.
12//!
13//! ## Features
14//!
15//! - Abstract over two-dimensional and three-dimensional vectors with `GenericVector2` and `GenericVector3` traits.
16//! - Generic scalar trait `GenericScalar` for a flexible scalar type handling.
17//! - Basic vector traits `HasXY` and `HasXYZ` for down to metal, custom vector storage types, e.g., FFI types.
18//! - Seamless transition between different vector libraries like `cgmath` and `glam`.
19//! - Ability to switch between different scalar types (`f32`, `f64`) effortlessly.
20//!
21//! ## Supported Vector Implementations
22//!
23//! Currently, the following vector types from `cgmath` and `glam` libraries are supported:
24//!
25//! - `glam::Vec2`
26//! - `glam::DVec2`
27//! - `glam::Vec3`
28//! - `glam::DVec3`
29//! - `cgmath::Vector2`
30//! - `cgmath::Vector3`
31//!
32//! ## Usage
33//!
34//! Add `vector-traits` to your `Cargo.toml` dependencies along with the desired features:
35//!
36//! ```toml
37//! [dependencies]
38//! vector-traits = { version = "0.3.4", features = ["glam", "cgmath"] }  # only use what you need
39//! ```
40//!
41//! ## Contributing
42//!
43//! Contributions are welcome! Feel free to open an issue or submit a pull request.
44//!
45//! ## License
46//!
47//! Licensed under either of
48//!
49//! - Apache License, Version 2.0 ([LICENSE-APACHE](https://example.com/your-apache-license-link))
50//! - MIT license ([LICENSE-MIT](https://example.com/your-mit-license-link))
51//!
52//! at your option.
53
54#![deny(
55    rust_2018_compatibility,
56    rust_2018_idioms,
57    nonstandard_style,
58    unused,
59    future_incompatible,
60    non_camel_case_types,
61    unused_parens,
62    non_upper_case_globals,
63    unused_qualifications,
64    unused_results,
65    unused_imports,
66    unused_variables,
67    bare_trait_objects,
68    ellipsis_inclusive_range_patterns,
69    elided_lifetimes_in_paths
70)]
71#![warn(clippy::explicit_into_iter_loop)]
72
73use num_traits::{float::FloatCore, AsPrimitive, Float, FromPrimitive, Signed, ToPrimitive};
74use std::{
75    fmt::{Debug, Display, LowerExp},
76    hash::Hash,
77    ops::{Add, AddAssign, DivAssign, Index, MulAssign, Neg, Sub, SubAssign},
78};
79
80#[cfg(feature = "cgmath")]
81pub mod cgmath_impl;
82#[cfg(feature = "glam")]
83pub mod glam_impl;
84
85#[cfg(feature = "glam")]
86pub use glam_impl::Vec2A;
87
88#[cfg(test)]
89mod tests;
90
91/// A trait meant to to represent f32 or f64
92pub trait GenericScalar
93where
94    Self: Display
95        + Debug
96        + Float
97        + FloatCore
98        + FromPrimitive
99        + ToPrimitive
100        + MulAssign
101        + DivAssign
102        + AddAssign
103        + SubAssign
104        + Default
105        + std::str::FromStr
106        + Sync
107        + Send
108        + Into<f64>
109        + From<f32>
110        + From<u16>
111        + From<i16>
112        + From<i8>
113        + From<u8>
114        + Neg<Output = Self>
115        + Signed
116        + LowerExp
117        + AsPrimitive<f64>
118        + AsPrimitive<f32>
119        + AsPrimitive<usize>
120        + AsPrimitive<isize>
121        + AsPrimitive<u64>
122        + AsPrimitive<i64>
123        + AsPrimitive<u32>
124        + AsPrimitive<i32>
125        + AsPrimitive<u16>
126        + AsPrimitive<i16>
127        + AsPrimitive<u8>
128        + AsPrimitive<i8>
129        + approx::UlpsEq<Epsilon = Self>,
130{
131    /// The type of the to_bits() and from_bits() methods
132    type BitsType: Hash + Eq + Ord + Display + Debug;
133    const ZERO: Self;
134    const ONE: Self;
135    const TWO: Self;
136    const THREE: Self;
137    const INFINITY: Self;
138    const NEG_INFINITY: Self;
139    const EPSILON: Self;
140    fn to_bits(self) -> Self::BitsType;
141    fn from_bits(bits: Self::BitsType) -> Self;
142    fn clamp(self, min: Self, max: Self) -> Self;
143}
144
145/// A workaround for Rust's limitations where external traits cannot be implemented for external types.
146///
147/// The `Approx` trait provides methods for performing approximate equality comparisons on types.
148/// It serves as a workaround for Rust's limitations, allowing you to implement approximate
149/// equality checks for types not originally designed with this capability.
150///
151/// This trait leverages the `approx` crate and its traits to perform approximate equality
152/// comparisons. The methods in this trait are wrappers around the corresponding methods provided
153/// by the `approx` crate.
154///
155pub trait Approx: HasXY {
156    /// Checks if two instances are nearly equal within a specified tolerance in ULPs (Units in the Last Place).
157    ///
158    /// This method delegates to the `approx::UlpsEq::ulps_eq` method, performing approximate equality checks
159    /// one time per coordinate axis.
160    fn is_ulps_eq(
161        self,
162        other: Self,
163        epsilon: <Self::Scalar as approx::AbsDiffEq>::Epsilon,
164        max_ulps: u32,
165    ) -> bool;
166
167    /// Checks if two instances are nearly equal within a specified absolute difference tolerance.
168    ///
169    /// This method delegates to the `approx::AbsDiffEq::abs_diff_eq` method, performing approximate equality checks
170    /// one time per coordinate axis.
171    fn is_abs_diff_eq(
172        self,
173        other: Self,
174        epsilon: <Self::Scalar as approx::AbsDiffEq>::Epsilon,
175    ) -> bool;
176}
177
178/// A generic two-dimensional vector trait, designed for flexibility in precision.
179///
180/// The `GenericVector2` trait abstracts over two-dimensional vectors, allowing for easy
181/// transition between different precisions (e.g., `f32` and `f64`) without necessitating
182/// significant codebase modifications. It provides the common operations one would expect
183/// for 2D vectors, such as dot products, cross products, and normalization.
184///
185/// Implementors of this trait can benefit from the ability to switch between different
186/// precision representations seamlessly, making it ideal for applications where varying
187/// precision levels might be desirable at different stages or configurations.
188///
189/// The associated `Scalar` type represents the scalar type (e.g., `f32` or `f64`) used
190/// by the vector, and `Vector3` is the corresponding three-dimensional vector type.
191///
192/// Note: The actual trait functionality might vary based on the concrete implementations.
193pub trait GenericVector2:
194    HasXY
195    + Approx
196    + PartialEq
197    + AddAssign
198    + Neg<Output = Self>
199    + Sub<Self, Output = Self>
200    + std::ops::Mul<Self::Scalar, Output = Self>
201    + std::ops::Div<Self::Scalar, Output = Self>
202    + Add<Self, Output = Self>
203    + Index<usize, Output = Self::Scalar>
204{
205    type Vector3: GenericVector3<Scalar = Self::Scalar, Vector2 = Self>;
206    fn to_3d(self, z: Self::Scalar) -> Self::Vector3;
207    fn magnitude(self) -> Self::Scalar;
208    fn magnitude_sq(self) -> Self::Scalar;
209    fn dot(self, other: Self) -> Self::Scalar;
210    fn perp_dot(self, rhs: Self) -> Self::Scalar;
211    fn distance(self, rhs: Self) -> Self::Scalar;
212    fn distance_sq(self, rhs: Self) -> Self::Scalar;
213    fn normalize(self) -> Self;
214    fn safe_normalize(self) -> Option<Self>;
215}
216
217impl GenericScalar for f32 {
218    type BitsType = u32;
219    const ZERO: Self = 0.0;
220    const ONE: Self = 1.0;
221    const TWO: Self = 2.0;
222    const THREE: Self = 3.0;
223    const INFINITY: Self = <f32>::INFINITY;
224    const NEG_INFINITY: Self = <f32>::NEG_INFINITY;
225    const EPSILON: Self = <f32>::EPSILON;
226    #[inline(always)]
227    fn to_bits(self) -> Self::BitsType {
228        f32::to_bits(self)
229    }
230    #[inline(always)]
231    fn from_bits(bits: Self::BitsType) -> Self {
232        f32::from_bits(bits)
233    }
234    #[inline(always)]
235    fn clamp(self, min: Self, max: Self) -> Self {
236        f32::clamp(self, min, max)
237    }
238}
239
240impl GenericScalar for f64 {
241    type BitsType = u64;
242    const ZERO: Self = 0.0;
243    const ONE: Self = 1.0;
244    const TWO: Self = 2.0;
245    const THREE: Self = 3.0;
246    const INFINITY: Self = <f64>::INFINITY;
247    const NEG_INFINITY: Self = <f64>::NEG_INFINITY;
248    const EPSILON: Self = <f64>::EPSILON;
249    #[inline(always)]
250    fn to_bits(self) -> Self::BitsType {
251        f64::to_bits(self)
252    }
253    #[inline(always)]
254    fn from_bits(bits: Self::BitsType) -> Self {
255        f64::from_bits(bits)
256    }
257    #[inline(always)]
258    fn clamp(self, min: Self, max: Self) -> Self {
259        f64::clamp(self, min, max)
260    }
261}
262
263/// A basic two-dimensional vector trait, designed for flexibility in precision.
264///
265/// The `HasXY` trait abstracts over two-dimensional vectors, allowing for easy
266/// transition between different precisions (e.g., `f32` and `f64`) without necessitating
267/// significant codebase modifications. It only provides the most basic vector interface.
268/// It is intended to be used in situations where you need a custom storage type of vectors.
269/// For example a FFI type.
270///
271/// Implementors of this trait can benefit from the ability to switch between different
272/// precision representations seamlessly, making it ideal for applications where varying
273/// precision levels might be desirable at different stages or configurations.
274///
275/// The associated `Scalar` type represents the scalar type (e.g., `f32` or `f64`) used
276/// by the vector.
277///
278pub trait HasXY: Sync + Send + Copy + Debug + Sized {
279    type Scalar: GenericScalar;
280    /// create a new instance of Self, note that this
281    /// creates a 3d vector if the instanced type is a 3d type
282    fn new_2d(x: Self::Scalar, y: Self::Scalar) -> Self;
283    fn x(self) -> Self::Scalar;
284    fn x_mut(&mut self) -> &mut Self::Scalar;
285    fn set_x(&mut self, val: Self::Scalar);
286    fn y(self) -> Self::Scalar;
287    fn y_mut(&mut self) -> &mut Self::Scalar;
288    fn set_y(&mut self, val: Self::Scalar);
289}
290
291/// A basic three-dimensional vector trait, designed for flexibility in precision.
292///
293/// The `HasXYZ` trait abstracts over three-dimensional vectors, allowing for easy
294/// transition between different precisions (e.g., `f32` and `f64`) without necessitating
295/// significant codebase modifications. It only provides the most basic vector interface.
296/// It is intended to be used in situations where you need a custom storage type of vectors.
297/// For example a FFI type.
298///
299/// Implementors of this trait can benefit from the ability to switch between different
300/// precision representations seamlessly, making it ideal for applications where varying
301/// precision levels might be desirable at different stages or configurations.
302///
303pub trait HasXYZ: HasXY {
304    fn new_3d(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self;
305    fn z(self) -> Self::Scalar;
306    fn z_mut(&mut self) -> &mut Self::Scalar;
307    fn set_z(&mut self, val: Self::Scalar);
308}
309
310/// A generic three-dimensional vector trait, designed for flexibility in precision.
311///
312/// The `GenericVector3` trait abstracts over three-dimensional vectors, allowing for easy
313/// transition between different precisions (e.g., `f32` and `f64`) without necessitating
314/// significant codebase modifications. It provides the common operations one would expect
315/// for 3D vectors, such as dot products, cross products, and normalization.
316///
317/// Implementors of this trait can benefit from the ability to switch between different
318/// precision representations seamlessly, making it ideal for applications where varying
319/// precision levels might be desirable at different stages or configurations.
320///
321/// The associated `Scalar` type represents the scalar type (e.g., `f32` or `f64`) used
322/// by the vector, and `Vector2` is the corresponding two-dimensional vector type.
323///
324/// Note: The actual trait functionality might vary based on the concrete implementations.
325pub trait GenericVector3:
326    HasXYZ
327    + Approx
328    + PartialEq
329    + AddAssign
330    + Neg<Output = Self>
331    + Sub<Self, Output = Self>
332    + std::ops::Mul<Self::Scalar, Output = Self>
333    + std::ops::Div<Self::Scalar, Output = Self>
334    + Add<Self, Output = Self>
335    + Index<usize, Output = Self::Scalar>
336{
337    type Vector2: GenericVector2<Scalar = Self::Scalar, Vector3 = Self>;
338    fn to_2d(&self) -> Self::Vector2;
339    fn magnitude(self) -> Self::Scalar;
340    fn magnitude_sq(self) -> Self::Scalar;
341    fn dot(self, other: Self) -> Self::Scalar;
342    fn cross(self, rhs: Self) -> Self;
343    fn normalize(self) -> Self;
344    fn safe_normalize(self) -> Option<Self>;
345    fn distance(self, other: Self) -> Self::Scalar;
346    fn distance_sq(self, rhs: Self) -> Self::Scalar;
347}
348
349pub use approx;
350#[cfg(feature = "cgmath")]
351pub use cgmath;
352#[cfg(feature = "glam")]
353pub use glam;
354pub use num_traits;