1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub mod bounds;
10pub mod cylindrical;
11pub mod euler;
12pub mod mat;
13pub mod plane;
14pub mod quat;
15pub mod ray;
16pub mod spherical;
17pub mod transform;
18pub mod vec;
19
20pub use bounds::{Aabb, Sphere};
21pub use cylindrical::Cylindrical;
22pub use euler::{Euler, RotationOrder};
23pub use mat::{Mat3, Mat4};
24pub use plane::Plane;
25pub use quat::Quat;
26pub use ray::Ray3;
27pub use spherical::Spherical;
28pub use transform::Transform;
29pub use vec::{Vec2, Vec3, Vec4};
30
31pub(crate) const EPSILON: f32 = 1.0e-6;
32#[cfg(all(not(feature = "libm"), not(feature = "std")))]
33pub(crate) const PI: f32 = core::f32::consts::PI;
34#[cfg(all(not(feature = "libm"), not(feature = "std")))]
35pub(crate) const FRAC_PI_2: f32 = core::f32::consts::FRAC_PI_2;
36
37#[inline]
38pub(crate) fn clamp(value: f32, min: f32, max: f32) -> f32 {
39 value.max(min).min(max)
40}
41
42#[inline]
43pub(crate) fn sqrt(value: f32) -> f32 {
44 #[cfg(feature = "libm")]
45 {
46 libm::sqrtf(value)
47 }
48 #[cfg(all(not(feature = "libm"), feature = "std"))]
49 {
50 value.sqrt()
51 }
52 #[cfg(all(not(feature = "libm"), not(feature = "std")))]
53 {
54 if value <= 0.0 {
55 return 0.0;
56 }
57 let mut x = if value >= 1.0 { value } else { 1.0 };
58 let mut i = 0;
59 while i < 8 {
60 x = 0.5 * (x + value / x);
61 i += 1;
62 }
63 x
64 }
65}
66
67#[inline]
68pub(crate) fn sin(value: f32) -> f32 {
69 #[cfg(feature = "libm")]
70 {
71 libm::sinf(value)
72 }
73 #[cfg(all(not(feature = "libm"), feature = "std"))]
74 {
75 value.sin()
76 }
77 #[cfg(all(not(feature = "libm"), not(feature = "std")))]
78 {
79 let x = reduce_half_pi(value);
80 let x2 = x * x;
81 x * (1.0 - x2 / 6.0 + (x2 * x2) / 120.0 - (x2 * x2 * x2) / 5040.0
82 + (x2 * x2 * x2 * x2) / 362_880.0)
83 }
84}
85
86#[inline]
87pub(crate) fn cos(value: f32) -> f32 {
88 #[cfg(feature = "libm")]
89 {
90 libm::cosf(value)
91 }
92 #[cfg(all(not(feature = "libm"), feature = "std"))]
93 {
94 value.cos()
95 }
96 #[cfg(all(not(feature = "libm"), not(feature = "std")))]
97 {
98 let mut x = reduce_pi(value);
99 let mut sign = 1.0;
100 if x > FRAC_PI_2 {
101 x = PI - x;
102 sign = -1.0;
103 } else if x < -FRAC_PI_2 {
104 x = -PI - x;
105 sign = -1.0;
106 }
107 let x2 = x * x;
108 sign * (1.0 - x2 / 2.0 + (x2 * x2) / 24.0 - (x2 * x2 * x2) / 720.0
109 + (x2 * x2 * x2 * x2) / 40_320.0)
110 }
111}
112
113#[inline]
114pub(crate) fn tan(value: f32) -> f32 {
115 #[cfg(feature = "libm")]
116 {
117 libm::tanf(value)
118 }
119 #[cfg(all(not(feature = "libm"), feature = "std"))]
120 {
121 value.tan()
122 }
123 #[cfg(all(not(feature = "libm"), not(feature = "std")))]
124 {
125 let c = cos(value);
126 if c.abs() <= EPSILON {
127 if value >= 0.0 {
128 f32::INFINITY
129 } else {
130 -f32::INFINITY
131 }
132 } else {
133 sin(value) / c
134 }
135 }
136}
137
138#[inline]
139pub(crate) fn acos(value: f32) -> f32 {
140 #[cfg(feature = "libm")]
141 {
142 libm::acosf(value)
143 }
144 #[cfg(all(not(feature = "libm"), feature = "std"))]
145 {
146 value.acos()
147 }
148 #[cfg(all(not(feature = "libm"), not(feature = "std")))]
149 {
150 FRAC_PI_2 - asin(value)
151 }
152}
153
154#[inline]
155pub(crate) fn asin(value: f32) -> f32 {
156 #[cfg(feature = "libm")]
157 {
158 libm::asinf(value)
159 }
160 #[cfg(all(not(feature = "libm"), feature = "std"))]
161 {
162 value.asin()
163 }
164 #[cfg(all(not(feature = "libm"), not(feature = "std")))]
165 {
166 let x = clamp(value, -1.0, 1.0);
167 atan2(x, sqrt((1.0 - x * x).max(0.0)))
168 }
169}
170
171#[inline]
172pub(crate) fn atan2(y: f32, x: f32) -> f32 {
173 #[cfg(feature = "libm")]
174 {
175 libm::atan2f(y, x)
176 }
177 #[cfg(all(not(feature = "libm"), feature = "std"))]
178 {
179 y.atan2(x)
180 }
181 #[cfg(all(not(feature = "libm"), not(feature = "std")))]
182 {
183 if x > 0.0 {
184 atan_approx(y / x)
185 } else if x < 0.0 && y >= 0.0 {
186 atan_approx(y / x) + PI
187 } else if x < 0.0 && y < 0.0 {
188 atan_approx(y / x) - PI
189 } else if y > 0.0 {
190 FRAC_PI_2
191 } else if y < 0.0 {
192 -FRAC_PI_2
193 } else {
194 0.0
195 }
196 }
197}
198
199#[cfg(all(not(feature = "libm"), not(feature = "std")))]
200#[inline]
201fn reduce_pi(mut value: f32) -> f32 {
202 let two_pi = 2.0 * PI;
203 while value > PI {
204 value -= two_pi;
205 }
206 while value < -PI {
207 value += two_pi;
208 }
209 value
210}
211
212#[cfg(all(not(feature = "libm"), not(feature = "std")))]
213#[inline]
214fn reduce_half_pi(value: f32) -> f32 {
215 let value = reduce_pi(value);
216 if value > FRAC_PI_2 {
217 PI - value
218 } else if value < -FRAC_PI_2 {
219 -PI - value
220 } else {
221 value
222 }
223}
224
225#[cfg(all(not(feature = "libm"), not(feature = "std")))]
226#[inline]
227fn atan_approx(value: f32) -> f32 {
228 let sign = value.signum();
229 let abs = value.abs();
230 if abs > 1.0 {
231 sign * FRAC_PI_2 - sign * atan_approx(1.0 / abs)
232 } else if abs > 0.414_213_57 {
233 sign * (core::f32::consts::FRAC_PI_4 + atan_approx((abs - 1.0) / (abs + 1.0)))
234 } else {
235 let x2 = value * value;
236 value
237 * (1.0 - x2 / 3.0 + (x2 * x2) / 5.0 - (x2 * x2 * x2) / 7.0 + (x2 * x2 * x2 * x2) / 9.0
238 - (x2 * x2 * x2 * x2 * x2) / 11.0)
239 }
240}
241
242#[cfg(test)]
243pub(crate) fn assert_close(a: f32, b: f32) {
244 assert!((a - b).abs() <= 1.0e-4, "{a} != {b}");
245}