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;