Skip to main content

rs_math3d/
scalar.rs

1//! Scalar trait definitions for generic numeric operations.
2//!
3//! This module defines traits that extend `num-traits` for use in
4//! mathematical operations throughout the library. It provides a
5//! unified interface for both integer and floating-point types.
6//!
7//! The module leverages the `num-traits` crate for basic numeric
8//! operations while adding specialized methods needed for 3D math.
9
10// Copyright 2020-Present (c) Raja Lehtihet & Wael El Oraiby
11//
12// Redistribution and use in source and binary forms, with or without
13// modification, are permitted provided that the following conditions are met:
14//
15// 1. Redistributions of source code must retain the above copyright notice,
16// this list of conditions and the following disclaimer.
17//
18// 2. Redistributions in binary form must reproduce the above copyright notice,
19// this list of conditions and the following disclaimer in the documentation
20// and/or other materials provided with the distribution.
21//
22// 3. Neither the name of the copyright holder nor the names of its contributors
23// may be used to endorse or promote products derived from this software without
24// specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
30// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36// POSSIBILITY OF SUCH DAMAGE.
37
38use crate::math::Math;
39use core::cmp::PartialOrd;
40use core::ops::Neg;
41use num_traits::{Num, NumAssignOps};
42
43// Re-export for convenience
44pub use num_traits::{One, Zero};
45
46/// Default epsilon for f32 comparisons.
47pub const EPS_F32: f32 = 1.0 / (1024.0 * 1024.0);
48/// Default epsilon for f64 comparisons.
49pub const EPS_F64: f64 = 1.0 / (1024.0 * 1024.0 * 1024.0 * 1024.0);
50
51/// Core scalar trait for numeric types used in the library.
52///
53/// This trait is intentionally limited to operations that make sense for both
54/// integer and floating-point types. Analytic and fractional operations live on
55/// [`FloatScalar`].
56///
57/// It is implemented for `i32`, `i64`, `f32`, and `f64`.
58///
59/// # Required Methods
60///
61/// Types implementing this trait must provide:
62/// - Basic arithmetic operations (via `Num` and `NumAssignOps`)
63/// - Comparison operations (via `PartialOrd`)
64/// - Additional constants and utility methods
65pub trait Scalar:
66    Num + NumAssignOps + Neg<Output = Self> + PartialOrd + Clone + Copy + Sized
67{
68    // Additional methods not provided by num-traits
69    /// Returns the constant 2 for the scalar type.
70    fn two() -> Self;
71    /// Returns the minimum of two values.
72    fn min(l: Self, r: Self) -> Self;
73    /// Returns the maximum of two values.
74    fn max(l: Self, r: Self) -> Self;
75    /// Returns the squared value (self * self).
76    fn squared(self) -> Self {
77        self * self
78    }
79    /// Returns the absolute value.
80    fn tabs(self) -> Self;
81}
82
83/// Trait for floating-point scalars with transcendental functions.
84///
85/// Extends the base `Scalar` trait with operations specific to
86/// floating-point numbers, including trigonometric functions and
87/// square root.
88///
89/// # Implementation Note
90///
91/// These functions are routed through the crate's selected math backend:
92/// `std`, `libm`, or `system-libm`.
93pub trait FloatScalar: Scalar {
94    /// Returns a reasonable epsilon for comparisons.
95    fn epsilon() -> Self;
96    /// Returns 1/2 for the scalar type.
97    fn half() -> Self;
98    /// Returns 1/4 for the scalar type.
99    fn quarter() -> Self;
100    /// Returns positive infinity.
101    fn infinity() -> Self;
102    /// Returns the square root.
103    fn tsqrt(self) -> Self;
104    /// Returns the sine (radians).
105    fn tsin(self) -> Self;
106    /// Returns the cosine (radians).
107    fn tcos(self) -> Self;
108    /// Returns the tangent (radians).
109    fn ttan(self) -> Self;
110    /// Returns the arc cosine (radians).
111    fn tacos(self) -> Self;
112}
113
114// Implementation for i32
115impl Scalar for i32 {
116    fn two() -> Self {
117        2
118    }
119    fn min(l: Self, r: Self) -> Self {
120        if l < r {
121            l
122        } else {
123            r
124        }
125    }
126    fn max(l: Self, r: Self) -> Self {
127        if l > r {
128            l
129        } else {
130            r
131        }
132    }
133    fn tabs(self) -> Self {
134        self.abs()
135    }
136}
137
138// Implementation for i64
139impl Scalar for i64 {
140    fn two() -> Self {
141        2
142    }
143    fn min(l: Self, r: Self) -> Self {
144        if l < r {
145            l
146        } else {
147            r
148        }
149    }
150    fn max(l: Self, r: Self) -> Self {
151        if l > r {
152            l
153        } else {
154            r
155        }
156    }
157    fn tabs(self) -> Self {
158        self.abs()
159    }
160}
161
162// Implementation for f32
163impl Scalar for f32 {
164    fn two() -> Self {
165        2.0
166    }
167    fn min(l: Self, r: Self) -> Self {
168        if l < r {
169            l
170        } else {
171            r
172        }
173    }
174    fn max(l: Self, r: Self) -> Self {
175        if l > r {
176            l
177        } else {
178            r
179        }
180    }
181    fn tabs(self) -> Self {
182        self.abs()
183    }
184}
185
186// Implementation for f64
187impl Scalar for f64 {
188    fn two() -> Self {
189        2.0
190    }
191    fn min(l: Self, r: Self) -> Self {
192        if l < r {
193            l
194        } else {
195            r
196        }
197    }
198    fn max(l: Self, r: Self) -> Self {
199        if l > r {
200            l
201        } else {
202            r
203        }
204    }
205    fn tabs(self) -> Self {
206        self.abs()
207    }
208}
209
210impl FloatScalar for f32 {
211    fn epsilon() -> Self {
212        EPS_F32
213    }
214    fn half() -> Self {
215        0.5
216    }
217    fn quarter() -> Self {
218        0.25
219    }
220    fn infinity() -> Self {
221        f32::INFINITY
222    }
223    fn tsqrt(self) -> Self {
224        Math::sqrt_f32(self)
225    }
226    fn tsin(self) -> Self {
227        Math::sin_f32(self)
228    }
229    fn tcos(self) -> Self {
230        Math::cos_f32(self)
231    }
232    fn ttan(self) -> Self {
233        Math::tan_f32(self)
234    }
235    fn tacos(self) -> Self {
236        Math::acos_f32(self)
237    }
238}
239
240impl FloatScalar for f64 {
241    fn epsilon() -> Self {
242        EPS_F64
243    }
244    fn half() -> Self {
245        0.5
246    }
247    fn quarter() -> Self {
248        0.25
249    }
250    fn infinity() -> Self {
251        f64::INFINITY
252    }
253    fn tsqrt(self) -> Self {
254        Math::sqrt_f64(self)
255    }
256    fn tsin(self) -> Self {
257        Math::sin_f64(self)
258    }
259    fn tcos(self) -> Self {
260        Math::cos_f64(self)
261    }
262    fn ttan(self) -> Self {
263        Math::tan_f64(self)
264    }
265    fn tacos(self) -> Self {
266        Math::acos_f64(self)
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273    #[test]
274    pub fn test() {
275        let out = -1.0;
276        let f = out.tabs();
277        assert_eq!(f, 1.0);
278    }
279
280    #[test]
281    fn test_float_scalar_ops_f32() {
282        let v = 4.0f32;
283        assert!((v.tsqrt() - 2.0).abs() < 0.0001);
284        assert!(0.0f32.tsin().abs() < 0.0001);
285        assert!((0.0f32.tcos() - 1.0).abs() < 0.0001);
286        assert!(0.0f32.ttan().abs() < 0.0001);
287        assert!(1.0f32.tacos().abs() < 0.0001);
288    }
289
290    #[test]
291    fn test_float_scalar_ops_f64() {
292        let v = 4.0f64;
293        assert!((v.tsqrt() - 2.0).abs() < 0.0000001);
294        assert!(0.0f64.tsin().abs() < 0.0000001);
295        assert!((0.0f64.tcos() - 1.0).abs() < 0.0000001);
296        assert!(0.0f64.ttan().abs() < 0.0000001);
297        assert!(1.0f64.tacos().abs() < 0.0000001);
298    }
299}