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}