Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_math/src/direction.rs
6595 views
1
use crate::{
2
primitives::{Primitive2d, Primitive3d},
3
Quat, Rot2, Vec2, Vec3, Vec3A, Vec4,
4
};
5
6
use core::f32::consts::FRAC_1_SQRT_2;
7
use core::fmt;
8
use derive_more::derive::Into;
9
10
#[cfg(feature = "bevy_reflect")]
11
use bevy_reflect::Reflect;
12
13
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
14
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
15
16
#[cfg(all(debug_assertions, feature = "std"))]
17
use std::eprintln;
18
19
use thiserror::Error;
20
21
/// An error indicating that a direction is invalid.
22
#[derive(Debug, PartialEq, Error)]
23
pub enum InvalidDirectionError {
24
/// The length of the direction vector is zero or very close to zero.
25
#[error("The length of the direction vector is zero or very close to zero")]
26
Zero,
27
/// The length of the direction vector is `std::f32::INFINITY`.
28
#[error("The length of the direction vector is `std::f32::INFINITY`")]
29
Infinite,
30
/// The length of the direction vector is `NaN`.
31
#[error("The length of the direction vector is `NaN`")]
32
NaN,
33
}
34
35
impl InvalidDirectionError {
36
/// Creates an [`InvalidDirectionError`] from the length of an invalid direction vector.
37
pub fn from_length(length: f32) -> Self {
38
if length.is_nan() {
39
InvalidDirectionError::NaN
40
} else if !length.is_finite() {
41
// If the direction is non-finite but also not NaN, it must be infinite
42
InvalidDirectionError::Infinite
43
} else {
44
// If the direction is invalid but neither NaN nor infinite, it must be zero
45
InvalidDirectionError::Zero
46
}
47
}
48
}
49
50
/// Checks that a vector with the given squared length is normalized.
51
///
52
/// Warns for small error with a length threshold of approximately `1e-4`,
53
/// and panics for large error with a length threshold of approximately `1e-2`.
54
///
55
/// The format used for the logged warning is `"Warning: {warning} The length is {length}`,
56
/// and similarly for the error.
57
#[cfg(debug_assertions)]
58
fn assert_is_normalized(message: &str, length_squared: f32) {
59
use crate::ops;
60
61
let length_error_squared = ops::abs(length_squared - 1.0);
62
63
// Panic for large error and warn for slight error.
64
if length_error_squared > 2e-2 || length_error_squared.is_nan() {
65
// Length error is approximately 1e-2 or more.
66
panic!(
67
"Error: {message} The length is {}.",
68
ops::sqrt(length_squared)
69
);
70
} else if length_error_squared > 2e-4 {
71
// Length error is approximately 1e-4 or more.
72
#[cfg(feature = "std")]
73
#[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")]
74
{
75
eprintln!(
76
"Warning: {message} The length is {}.",
77
ops::sqrt(length_squared)
78
);
79
}
80
}
81
}
82
83
/// A normalized vector pointing in a direction in 2D space
84
#[derive(Clone, Copy, Debug, PartialEq)]
85
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
86
#[cfg_attr(
87
feature = "bevy_reflect",
88
derive(Reflect),
89
reflect(Debug, PartialEq, Clone)
90
)]
91
#[cfg_attr(
92
all(feature = "serialize", feature = "bevy_reflect"),
93
reflect(Serialize, Deserialize)
94
)]
95
#[doc(alias = "Direction2d")]
96
pub struct Dir2(Vec2);
97
impl Primitive2d for Dir2 {}
98
99
impl Dir2 {
100
/// A unit vector pointing along the positive X axis.
101
pub const X: Self = Self(Vec2::X);
102
/// A unit vector pointing along the positive Y axis.
103
pub const Y: Self = Self(Vec2::Y);
104
/// A unit vector pointing along the negative X axis.
105
pub const NEG_X: Self = Self(Vec2::NEG_X);
106
/// A unit vector pointing along the negative Y axis.
107
pub const NEG_Y: Self = Self(Vec2::NEG_Y);
108
/// The directional axes.
109
pub const AXES: [Self; 2] = [Self::X, Self::Y];
110
111
/// The "north" direction, equivalent to [`Dir2::Y`].
112
pub const NORTH: Self = Self(Vec2::Y);
113
/// The "south" direction, equivalent to [`Dir2::NEG_Y`].
114
pub const SOUTH: Self = Self(Vec2::NEG_Y);
115
/// The "east" direction, equivalent to [`Dir2::X`].
116
pub const EAST: Self = Self(Vec2::X);
117
/// The "west" direction, equivalent to [`Dir2::NEG_X`].
118
pub const WEST: Self = Self(Vec2::NEG_X);
119
/// The "north-east" direction, between [`Dir2::NORTH`] and [`Dir2::EAST`].
120
pub const NORTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2));
121
/// The "north-west" direction, between [`Dir2::NORTH`] and [`Dir2::WEST`].
122
pub const NORTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, FRAC_1_SQRT_2));
123
/// The "south-east" direction, between [`Dir2::SOUTH`] and [`Dir2::EAST`].
124
pub const SOUTH_EAST: Self = Self(Vec2::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
125
/// The "south-west" direction, between [`Dir2::SOUTH`] and [`Dir2::WEST`].
126
pub const SOUTH_WEST: Self = Self(Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2));
127
128
/// Create a direction from a finite, nonzero [`Vec2`], normalizing it.
129
///
130
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
131
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
132
pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
133
Self::new_and_length(value).map(|(dir, _)| dir)
134
}
135
136
/// Create a [`Dir2`] from a [`Vec2`] that is already normalized.
137
///
138
/// # Warning
139
///
140
/// `value` must be normalized, i.e its length must be `1.0`.
141
pub fn new_unchecked(value: Vec2) -> Self {
142
#[cfg(debug_assertions)]
143
assert_is_normalized(
144
"The vector given to `Dir2::new_unchecked` is not normalized.",
145
value.length_squared(),
146
);
147
148
Self(value)
149
}
150
151
/// Create a direction from a finite, nonzero [`Vec2`], normalizing it and
152
/// also returning its original length.
153
///
154
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
155
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
156
pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> {
157
let length = value.length();
158
let direction = (length.is_finite() && length > 0.0).then_some(value / length);
159
160
direction
161
.map(|dir| (Self(dir), length))
162
.ok_or(InvalidDirectionError::from_length(length))
163
}
164
165
/// Create a direction from its `x` and `y` components.
166
///
167
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
168
/// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
169
pub fn from_xy(x: f32, y: f32) -> Result<Self, InvalidDirectionError> {
170
Self::new(Vec2::new(x, y))
171
}
172
173
/// Create a direction from its `x` and `y` components, assuming the resulting vector is normalized.
174
///
175
/// # Warning
176
///
177
/// The vector produced from `x` and `y` must be normalized, i.e its length must be `1.0`.
178
pub fn from_xy_unchecked(x: f32, y: f32) -> Self {
179
Self::new_unchecked(Vec2::new(x, y))
180
}
181
182
/// Returns the inner [`Vec2`]
183
pub const fn as_vec2(&self) -> Vec2 {
184
self.0
185
}
186
187
/// Performs a spherical linear interpolation between `self` and `rhs`
188
/// based on the value `s`.
189
///
190
/// This corresponds to interpolating between the two directions at a constant angular velocity.
191
///
192
/// When `s == 0.0`, the result will be equal to `self`.
193
/// When `s == 1.0`, the result will be equal to `rhs`.
194
///
195
/// # Example
196
///
197
/// ```
198
/// # use bevy_math::Dir2;
199
/// # use approx::{assert_relative_eq, RelativeEq};
200
/// #
201
/// let dir1 = Dir2::X;
202
/// let dir2 = Dir2::Y;
203
///
204
/// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
205
/// #[cfg(feature = "approx")]
206
/// assert_relative_eq!(result1, Dir2::from_xy(0.75_f32.sqrt(), 0.5).unwrap());
207
///
208
/// let result2 = dir1.slerp(dir2, 0.5);
209
/// #[cfg(feature = "approx")]
210
/// assert_relative_eq!(result2, Dir2::from_xy(0.5_f32.sqrt(), 0.5_f32.sqrt()).unwrap());
211
/// ```
212
#[inline]
213
pub fn slerp(self, rhs: Self, s: f32) -> Self {
214
let angle = self.angle_to(rhs.0);
215
Rot2::radians(angle * s) * self
216
}
217
218
/// Get the rotation that rotates this direction to `other`.
219
#[inline]
220
pub fn rotation_to(self, other: Self) -> Rot2 {
221
// Rotate `self` to X-axis, then X-axis to `other`:
222
other.rotation_from_x() * self.rotation_to_x()
223
}
224
225
/// Get the rotation that rotates `other` to this direction.
226
#[inline]
227
pub fn rotation_from(self, other: Self) -> Rot2 {
228
other.rotation_to(self)
229
}
230
231
/// Get the rotation that rotates the X-axis to this direction.
232
#[inline]
233
pub fn rotation_from_x(self) -> Rot2 {
234
Rot2::from_sin_cos(self.0.y, self.0.x)
235
}
236
237
/// Get the rotation that rotates this direction to the X-axis.
238
#[inline]
239
pub fn rotation_to_x(self) -> Rot2 {
240
// (This is cheap, it just negates one component.)
241
self.rotation_from_x().inverse()
242
}
243
244
/// Get the rotation that rotates the Y-axis to this direction.
245
#[inline]
246
pub fn rotation_from_y(self) -> Rot2 {
247
// `x <- y`, `y <- -x` correspond to rotating clockwise by pi/2;
248
// this transforms the Y-axis into the X-axis, maintaining the relative position
249
// of our direction. Then we just use the same technique as `rotation_from_x`.
250
Rot2::from_sin_cos(-self.0.x, self.0.y)
251
}
252
253
/// Get the rotation that rotates this direction to the Y-axis.
254
#[inline]
255
pub fn rotation_to_y(self) -> Rot2 {
256
self.rotation_from_y().inverse()
257
}
258
259
/// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
260
/// Useful for preventing numerical error accumulation.
261
/// See [`Dir3::fast_renormalize`] for an example of when such error accumulation might occur.
262
#[inline]
263
pub fn fast_renormalize(self) -> Self {
264
let length_squared = self.0.length_squared();
265
// Based on a Taylor approximation of the inverse square root, see [`Dir3::fast_renormalize`] for more details.
266
Self(self * (0.5 * (3.0 - length_squared)))
267
}
268
}
269
270
impl TryFrom<Vec2> for Dir2 {
271
type Error = InvalidDirectionError;
272
273
fn try_from(value: Vec2) -> Result<Self, Self::Error> {
274
Self::new(value)
275
}
276
}
277
278
impl From<Dir2> for Vec2 {
279
fn from(value: Dir2) -> Self {
280
value.as_vec2()
281
}
282
}
283
284
impl core::ops::Deref for Dir2 {
285
type Target = Vec2;
286
fn deref(&self) -> &Self::Target {
287
&self.0
288
}
289
}
290
291
impl core::ops::Neg for Dir2 {
292
type Output = Self;
293
fn neg(self) -> Self::Output {
294
Self(-self.0)
295
}
296
}
297
298
impl core::ops::Mul<f32> for Dir2 {
299
type Output = Vec2;
300
fn mul(self, rhs: f32) -> Self::Output {
301
self.0 * rhs
302
}
303
}
304
305
impl core::ops::Mul<Dir2> for f32 {
306
type Output = Vec2;
307
fn mul(self, rhs: Dir2) -> Self::Output {
308
self * rhs.0
309
}
310
}
311
312
impl core::ops::Mul<Dir2> for Rot2 {
313
type Output = Dir2;
314
315
/// Rotates the [`Dir2`] using a [`Rot2`].
316
fn mul(self, direction: Dir2) -> Self::Output {
317
let rotated = self * *direction;
318
319
#[cfg(debug_assertions)]
320
assert_is_normalized(
321
"`Dir2` is denormalized after rotation.",
322
rotated.length_squared(),
323
);
324
325
Dir2(rotated)
326
}
327
}
328
329
impl fmt::Display for Dir2 {
330
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331
write!(f, "{}", self.0)
332
}
333
}
334
335
#[cfg(any(feature = "approx", test))]
336
impl approx::AbsDiffEq for Dir2 {
337
type Epsilon = f32;
338
fn default_epsilon() -> f32 {
339
f32::EPSILON
340
}
341
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
342
self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
343
}
344
}
345
346
#[cfg(any(feature = "approx", test))]
347
impl approx::RelativeEq for Dir2 {
348
fn default_max_relative() -> f32 {
349
f32::EPSILON
350
}
351
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
352
self.as_ref()
353
.relative_eq(other.as_ref(), epsilon, max_relative)
354
}
355
}
356
357
#[cfg(any(feature = "approx", test))]
358
impl approx::UlpsEq for Dir2 {
359
fn default_max_ulps() -> u32 {
360
4
361
}
362
fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
363
self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
364
}
365
}
366
367
/// A normalized vector pointing in a direction in 3D space
368
#[derive(Clone, Copy, Debug, PartialEq, Into)]
369
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
370
#[cfg_attr(
371
feature = "bevy_reflect",
372
derive(Reflect),
373
reflect(Debug, PartialEq, Clone)
374
)]
375
#[cfg_attr(
376
all(feature = "serialize", feature = "bevy_reflect"),
377
reflect(Serialize, Deserialize)
378
)]
379
#[doc(alias = "Direction3d")]
380
pub struct Dir3(Vec3);
381
impl Primitive3d for Dir3 {}
382
383
impl Dir3 {
384
/// A unit vector pointing along the positive X axis.
385
pub const X: Self = Self(Vec3::X);
386
/// A unit vector pointing along the positive Y axis.
387
pub const Y: Self = Self(Vec3::Y);
388
/// A unit vector pointing along the positive Z axis.
389
pub const Z: Self = Self(Vec3::Z);
390
/// A unit vector pointing along the negative X axis.
391
pub const NEG_X: Self = Self(Vec3::NEG_X);
392
/// A unit vector pointing along the negative Y axis.
393
pub const NEG_Y: Self = Self(Vec3::NEG_Y);
394
/// A unit vector pointing along the negative Z axis.
395
pub const NEG_Z: Self = Self(Vec3::NEG_Z);
396
/// The directional axes.
397
pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
398
399
/// Create a direction from a finite, nonzero [`Vec3`], normalizing it.
400
///
401
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
402
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
403
pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
404
Self::new_and_length(value).map(|(dir, _)| dir)
405
}
406
407
/// Create a [`Dir3`] from a [`Vec3`] that is already normalized.
408
///
409
/// # Warning
410
///
411
/// `value` must be normalized, i.e its length must be `1.0`.
412
pub fn new_unchecked(value: Vec3) -> Self {
413
#[cfg(debug_assertions)]
414
assert_is_normalized(
415
"The vector given to `Dir3::new_unchecked` is not normalized.",
416
value.length_squared(),
417
);
418
419
Self(value)
420
}
421
422
/// Create a direction from a finite, nonzero [`Vec3`], normalizing it and
423
/// also returning its original length.
424
///
425
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
426
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
427
pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> {
428
let length = value.length();
429
let direction = (length.is_finite() && length > 0.0).then_some(value / length);
430
431
direction
432
.map(|dir| (Self(dir), length))
433
.ok_or(InvalidDirectionError::from_length(length))
434
}
435
436
/// Create a direction from its `x`, `y`, and `z` components.
437
///
438
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
439
/// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
440
pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
441
Self::new(Vec3::new(x, y, z))
442
}
443
444
/// Create a direction from its `x`, `y`, and `z` components, assuming the resulting vector is normalized.
445
///
446
/// # Warning
447
///
448
/// The vector produced from `x`, `y`, and `z` must be normalized, i.e its length must be `1.0`.
449
pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
450
Self::new_unchecked(Vec3::new(x, y, z))
451
}
452
453
/// Returns the inner [`Vec3`]
454
pub const fn as_vec3(&self) -> Vec3 {
455
self.0
456
}
457
458
/// Performs a spherical linear interpolation between `self` and `rhs`
459
/// based on the value `s`.
460
///
461
/// This corresponds to interpolating between the two directions at a constant angular velocity.
462
///
463
/// When `s == 0.0`, the result will be equal to `self`.
464
/// When `s == 1.0`, the result will be equal to `rhs`.
465
///
466
/// # Example
467
///
468
/// ```
469
/// # use bevy_math::Dir3;
470
/// # use approx::{assert_relative_eq, RelativeEq};
471
/// #
472
/// let dir1 = Dir3::X;
473
/// let dir2 = Dir3::Y;
474
///
475
/// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
476
/// #[cfg(feature = "approx")]
477
/// assert_relative_eq!(
478
/// result1,
479
/// Dir3::from_xyz(0.75_f32.sqrt(), 0.5, 0.0).unwrap(),
480
/// epsilon = 0.000001
481
/// );
482
///
483
/// let result2 = dir1.slerp(dir2, 0.5);
484
/// #[cfg(feature = "approx")]
485
/// assert_relative_eq!(result2, Dir3::from_xyz(0.5_f32.sqrt(), 0.5_f32.sqrt(), 0.0).unwrap());
486
/// ```
487
#[inline]
488
pub fn slerp(self, rhs: Self, s: f32) -> Self {
489
let quat = Quat::IDENTITY.slerp(Quat::from_rotation_arc(self.0, rhs.0), s);
490
Dir3(quat.mul_vec3(self.0))
491
}
492
493
/// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
494
/// Useful for preventing numerical error accumulation.
495
///
496
/// # Example
497
/// The following seemingly benign code would start accumulating errors over time,
498
/// leading to `dir` eventually not being normalized anymore.
499
/// ```
500
/// # use bevy_math::prelude::*;
501
/// # let N: usize = 200;
502
/// let mut dir = Dir3::X;
503
/// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
504
/// for i in 0..N {
505
/// dir = quaternion * dir;
506
/// }
507
/// ```
508
/// Instead, do the following.
509
/// ```
510
/// # use bevy_math::prelude::*;
511
/// # let N: usize = 200;
512
/// let mut dir = Dir3::X;
513
/// let quaternion = Quat::from_euler(EulerRot::XYZ, 1.0, 2.0, 3.0);
514
/// for i in 0..N {
515
/// dir = quaternion * dir;
516
/// dir = dir.fast_renormalize();
517
/// }
518
/// ```
519
#[inline]
520
pub fn fast_renormalize(self) -> Self {
521
// We numerically approximate the inverse square root by a Taylor series around 1
522
// As we expect the error (x := length_squared - 1) to be small
523
// inverse_sqrt(length_squared) = (1 + x)^(-1/2) = 1 - 1/2 x + O(x²)
524
// inverse_sqrt(length_squared) ≈ 1 - 1/2 (length_squared - 1) = 1/2 (3 - length_squared)
525
526
// Iterative calls to this method quickly converge to a normalized value,
527
// so long as the denormalization is not large ~ O(1/10).
528
// One iteration can be described as:
529
// l_sq <- l_sq * (1 - 1/2 (l_sq - 1))²;
530
// Rewriting in terms of the error x:
531
// 1 + x <- (1 + x) * (1 - 1/2 x)²
532
// 1 + x <- (1 + x) * (1 - x + 1/4 x²)
533
// 1 + x <- 1 - x + 1/4 x² + x - x² + 1/4 x³
534
// x <- -1/4 x² (3 - x)
535
// If the error is small, say in a range of (-1/2, 1/2), then:
536
// |-1/4 x² (3 - x)| <= (3/4 + 1/4 * |x|) * x² <= (3/4 + 1/4 * 1/2) * x² < x² < 1/2 x
537
// Therefore the sequence of iterates converges to 0 error as a second order method.
538
539
let length_squared = self.0.length_squared();
540
Self(self * (0.5 * (3.0 - length_squared)))
541
}
542
}
543
544
impl TryFrom<Vec3> for Dir3 {
545
type Error = InvalidDirectionError;
546
547
fn try_from(value: Vec3) -> Result<Self, Self::Error> {
548
Self::new(value)
549
}
550
}
551
552
impl core::ops::Deref for Dir3 {
553
type Target = Vec3;
554
fn deref(&self) -> &Self::Target {
555
&self.0
556
}
557
}
558
559
impl core::ops::Neg for Dir3 {
560
type Output = Self;
561
fn neg(self) -> Self::Output {
562
Self(-self.0)
563
}
564
}
565
566
impl core::ops::Mul<f32> for Dir3 {
567
type Output = Vec3;
568
fn mul(self, rhs: f32) -> Self::Output {
569
self.0 * rhs
570
}
571
}
572
573
impl core::ops::Mul<Dir3> for f32 {
574
type Output = Vec3;
575
fn mul(self, rhs: Dir3) -> Self::Output {
576
self * rhs.0
577
}
578
}
579
580
impl core::ops::Mul<Dir3> for Quat {
581
type Output = Dir3;
582
583
/// Rotates the [`Dir3`] using a [`Quat`].
584
fn mul(self, direction: Dir3) -> Self::Output {
585
let rotated = self * *direction;
586
587
#[cfg(debug_assertions)]
588
assert_is_normalized(
589
"`Dir3` is denormalized after rotation.",
590
rotated.length_squared(),
591
);
592
593
Dir3(rotated)
594
}
595
}
596
597
impl fmt::Display for Dir3 {
598
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599
write!(f, "{}", self.0)
600
}
601
}
602
603
#[cfg(feature = "approx")]
604
impl approx::AbsDiffEq for Dir3 {
605
type Epsilon = f32;
606
fn default_epsilon() -> f32 {
607
f32::EPSILON
608
}
609
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
610
self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
611
}
612
}
613
614
#[cfg(feature = "approx")]
615
impl approx::RelativeEq for Dir3 {
616
fn default_max_relative() -> f32 {
617
f32::EPSILON
618
}
619
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
620
self.as_ref()
621
.relative_eq(other.as_ref(), epsilon, max_relative)
622
}
623
}
624
625
#[cfg(feature = "approx")]
626
impl approx::UlpsEq for Dir3 {
627
fn default_max_ulps() -> u32 {
628
4
629
}
630
fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
631
self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
632
}
633
}
634
635
/// A normalized SIMD vector pointing in a direction in 3D space.
636
///
637
/// This type stores a 16 byte aligned [`Vec3A`].
638
/// This may or may not be faster than [`Dir3`]: make sure to benchmark!
639
#[derive(Clone, Copy, Debug, PartialEq)]
640
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
641
#[cfg_attr(
642
feature = "bevy_reflect",
643
derive(Reflect),
644
reflect(Debug, PartialEq, Clone)
645
)]
646
#[cfg_attr(
647
all(feature = "serialize", feature = "bevy_reflect"),
648
reflect(Serialize, Deserialize)
649
)]
650
#[doc(alias = "Direction3dA")]
651
pub struct Dir3A(Vec3A);
652
impl Primitive3d for Dir3A {}
653
654
impl Dir3A {
655
/// A unit vector pointing along the positive X axis.
656
pub const X: Self = Self(Vec3A::X);
657
/// A unit vector pointing along the positive Y axis.
658
pub const Y: Self = Self(Vec3A::Y);
659
/// A unit vector pointing along the positive Z axis.
660
pub const Z: Self = Self(Vec3A::Z);
661
/// A unit vector pointing along the negative X axis.
662
pub const NEG_X: Self = Self(Vec3A::NEG_X);
663
/// A unit vector pointing along the negative Y axis.
664
pub const NEG_Y: Self = Self(Vec3A::NEG_Y);
665
/// A unit vector pointing along the negative Z axis.
666
pub const NEG_Z: Self = Self(Vec3A::NEG_Z);
667
/// The directional axes.
668
pub const AXES: [Self; 3] = [Self::X, Self::Y, Self::Z];
669
670
/// Create a direction from a finite, nonzero [`Vec3A`], normalizing it.
671
///
672
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
673
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
674
pub fn new(value: Vec3A) -> Result<Self, InvalidDirectionError> {
675
Self::new_and_length(value).map(|(dir, _)| dir)
676
}
677
678
/// Create a [`Dir3A`] from a [`Vec3A`] that is already normalized.
679
///
680
/// # Warning
681
///
682
/// `value` must be normalized, i.e its length must be `1.0`.
683
pub fn new_unchecked(value: Vec3A) -> Self {
684
#[cfg(debug_assertions)]
685
assert_is_normalized(
686
"The vector given to `Dir3A::new_unchecked` is not normalized.",
687
value.length_squared(),
688
);
689
690
Self(value)
691
}
692
693
/// Create a direction from a finite, nonzero [`Vec3A`], normalizing it and
694
/// also returning its original length.
695
///
696
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
697
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
698
pub fn new_and_length(value: Vec3A) -> Result<(Self, f32), InvalidDirectionError> {
699
let length = value.length();
700
let direction = (length.is_finite() && length > 0.0).then_some(value / length);
701
702
direction
703
.map(|dir| (Self(dir), length))
704
.ok_or(InvalidDirectionError::from_length(length))
705
}
706
707
/// Create a direction from its `x`, `y`, and `z` components.
708
///
709
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
710
/// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
711
pub fn from_xyz(x: f32, y: f32, z: f32) -> Result<Self, InvalidDirectionError> {
712
Self::new(Vec3A::new(x, y, z))
713
}
714
715
/// Create a direction from its `x`, `y`, and `z` components, assuming the resulting vector is normalized.
716
///
717
/// # Warning
718
///
719
/// The vector produced from `x`, `y`, and `z` must be normalized, i.e its length must be `1.0`.
720
pub fn from_xyz_unchecked(x: f32, y: f32, z: f32) -> Self {
721
Self::new_unchecked(Vec3A::new(x, y, z))
722
}
723
724
/// Returns the inner [`Vec3A`]
725
pub const fn as_vec3a(&self) -> Vec3A {
726
self.0
727
}
728
729
/// Performs a spherical linear interpolation between `self` and `rhs`
730
/// based on the value `s`.
731
///
732
/// This corresponds to interpolating between the two directions at a constant angular velocity.
733
///
734
/// When `s == 0.0`, the result will be equal to `self`.
735
/// When `s == 1.0`, the result will be equal to `rhs`.
736
///
737
/// # Example
738
///
739
/// ```
740
/// # use bevy_math::Dir3A;
741
/// # use approx::{assert_relative_eq, RelativeEq};
742
/// #
743
/// let dir1 = Dir3A::X;
744
/// let dir2 = Dir3A::Y;
745
///
746
/// let result1 = dir1.slerp(dir2, 1.0 / 3.0);
747
/// #[cfg(feature = "approx")]
748
/// assert_relative_eq!(
749
/// result1,
750
/// Dir3A::from_xyz(0.75_f32.sqrt(), 0.5, 0.0).unwrap(),
751
/// epsilon = 0.000001
752
/// );
753
///
754
/// let result2 = dir1.slerp(dir2, 0.5);
755
/// #[cfg(feature = "approx")]
756
/// assert_relative_eq!(result2, Dir3A::from_xyz(0.5_f32.sqrt(), 0.5_f32.sqrt(), 0.0).unwrap());
757
/// ```
758
#[inline]
759
pub fn slerp(self, rhs: Self, s: f32) -> Self {
760
let quat = Quat::IDENTITY.slerp(
761
Quat::from_rotation_arc(Vec3::from(self.0), Vec3::from(rhs.0)),
762
s,
763
);
764
Dir3A(quat.mul_vec3a(self.0))
765
}
766
767
/// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
768
/// Useful for preventing numerical error accumulation.
769
///
770
/// See [`Dir3::fast_renormalize`] for an example of when such error accumulation might occur.
771
#[inline]
772
pub fn fast_renormalize(self) -> Self {
773
let length_squared = self.0.length_squared();
774
// Based on a Taylor approximation of the inverse square root, see [`Dir3::fast_renormalize`] for more details.
775
Self(self * (0.5 * (3.0 - length_squared)))
776
}
777
}
778
779
impl From<Dir3> for Dir3A {
780
fn from(value: Dir3) -> Self {
781
Self(value.0.into())
782
}
783
}
784
785
impl From<Dir3A> for Dir3 {
786
fn from(value: Dir3A) -> Self {
787
Self(value.0.into())
788
}
789
}
790
791
impl TryFrom<Vec3A> for Dir3A {
792
type Error = InvalidDirectionError;
793
794
fn try_from(value: Vec3A) -> Result<Self, Self::Error> {
795
Self::new(value)
796
}
797
}
798
799
impl From<Dir3A> for Vec3A {
800
fn from(value: Dir3A) -> Self {
801
value.0
802
}
803
}
804
805
impl core::ops::Deref for Dir3A {
806
type Target = Vec3A;
807
fn deref(&self) -> &Self::Target {
808
&self.0
809
}
810
}
811
812
impl core::ops::Neg for Dir3A {
813
type Output = Self;
814
fn neg(self) -> Self::Output {
815
Self(-self.0)
816
}
817
}
818
819
impl core::ops::Mul<f32> for Dir3A {
820
type Output = Vec3A;
821
fn mul(self, rhs: f32) -> Self::Output {
822
self.0 * rhs
823
}
824
}
825
826
impl core::ops::Mul<Dir3A> for f32 {
827
type Output = Vec3A;
828
fn mul(self, rhs: Dir3A) -> Self::Output {
829
self * rhs.0
830
}
831
}
832
833
impl core::ops::Mul<Dir3A> for Quat {
834
type Output = Dir3A;
835
836
/// Rotates the [`Dir3A`] using a [`Quat`].
837
fn mul(self, direction: Dir3A) -> Self::Output {
838
let rotated = self * *direction;
839
840
#[cfg(debug_assertions)]
841
assert_is_normalized(
842
"`Dir3A` is denormalized after rotation.",
843
rotated.length_squared(),
844
);
845
846
Dir3A(rotated)
847
}
848
}
849
850
impl fmt::Display for Dir3A {
851
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
852
write!(f, "{}", self.0)
853
}
854
}
855
856
#[cfg(feature = "approx")]
857
impl approx::AbsDiffEq for Dir3A {
858
type Epsilon = f32;
859
fn default_epsilon() -> f32 {
860
f32::EPSILON
861
}
862
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
863
self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
864
}
865
}
866
867
#[cfg(feature = "approx")]
868
impl approx::RelativeEq for Dir3A {
869
fn default_max_relative() -> f32 {
870
f32::EPSILON
871
}
872
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
873
self.as_ref()
874
.relative_eq(other.as_ref(), epsilon, max_relative)
875
}
876
}
877
878
#[cfg(feature = "approx")]
879
impl approx::UlpsEq for Dir3A {
880
fn default_max_ulps() -> u32 {
881
4
882
}
883
fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
884
self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
885
}
886
}
887
888
/// A normalized vector pointing in a direction in 4D space
889
#[derive(Clone, Copy, Debug, PartialEq, Into)]
890
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
891
#[cfg_attr(
892
feature = "bevy_reflect",
893
derive(Reflect),
894
reflect(Debug, PartialEq, Clone)
895
)]
896
#[cfg_attr(
897
all(feature = "serialize", feature = "bevy_reflect"),
898
reflect(Serialize, Deserialize)
899
)]
900
#[doc(alias = "Direction4d")]
901
pub struct Dir4(Vec4);
902
903
impl Dir4 {
904
/// A unit vector pointing along the positive X axis
905
pub const X: Self = Self(Vec4::X);
906
/// A unit vector pointing along the positive Y axis.
907
pub const Y: Self = Self(Vec4::Y);
908
/// A unit vector pointing along the positive Z axis.
909
pub const Z: Self = Self(Vec4::Z);
910
/// A unit vector pointing along the positive W axis.
911
pub const W: Self = Self(Vec4::W);
912
/// A unit vector pointing along the negative X axis.
913
pub const NEG_X: Self = Self(Vec4::NEG_X);
914
/// A unit vector pointing along the negative Y axis.
915
pub const NEG_Y: Self = Self(Vec4::NEG_Y);
916
/// A unit vector pointing along the negative Z axis.
917
pub const NEG_Z: Self = Self(Vec4::NEG_Z);
918
/// A unit vector pointing along the negative W axis.
919
pub const NEG_W: Self = Self(Vec4::NEG_W);
920
/// The directional axes.
921
pub const AXES: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W];
922
923
/// Create a direction from a finite, nonzero [`Vec4`], normalizing it.
924
///
925
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
926
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
927
pub fn new(value: Vec4) -> Result<Self, InvalidDirectionError> {
928
Self::new_and_length(value).map(|(dir, _)| dir)
929
}
930
931
/// Create a [`Dir4`] from a [`Vec4`] that is already normalized.
932
///
933
/// # Warning
934
///
935
/// `value` must be normalized, i.e its length must be `1.0`.
936
pub fn new_unchecked(value: Vec4) -> Self {
937
#[cfg(debug_assertions)]
938
assert_is_normalized(
939
"The vector given to `Dir4::new_unchecked` is not normalized.",
940
value.length_squared(),
941
);
942
Self(value)
943
}
944
945
/// Create a direction from a finite, nonzero [`Vec4`], normalizing it and
946
/// also returning its original length.
947
///
948
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
949
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
950
pub fn new_and_length(value: Vec4) -> Result<(Self, f32), InvalidDirectionError> {
951
let length = value.length();
952
let direction = (length.is_finite() && length > 0.0).then_some(value / length);
953
954
direction
955
.map(|dir| (Self(dir), length))
956
.ok_or(InvalidDirectionError::from_length(length))
957
}
958
959
/// Create a direction from its `x`, `y`, `z`, and `w` components.
960
///
961
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
962
/// of the vector formed by the components is zero (or very close to zero), infinite, or `NaN`.
963
pub fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Result<Self, InvalidDirectionError> {
964
Self::new(Vec4::new(x, y, z, w))
965
}
966
967
/// Create a direction from its `x`, `y`, `z`, and `w` components, assuming the resulting vector is normalized.
968
///
969
/// # Warning
970
///
971
/// The vector produced from `x`, `y`, `z`, and `w` must be normalized, i.e its length must be `1.0`.
972
pub fn from_xyzw_unchecked(x: f32, y: f32, z: f32, w: f32) -> Self {
973
Self::new_unchecked(Vec4::new(x, y, z, w))
974
}
975
976
/// Returns the inner [`Vec4`]
977
pub const fn as_vec4(&self) -> Vec4 {
978
self.0
979
}
980
981
/// Returns `self` after an approximate normalization, assuming the value is already nearly normalized.
982
/// Useful for preventing numerical error accumulation.
983
#[inline]
984
pub fn fast_renormalize(self) -> Self {
985
// We numerically approximate the inverse square root by a Taylor series around 1
986
// As we expect the error (x := length_squared - 1) to be small
987
// inverse_sqrt(length_squared) = (1 + x)^(-1/2) = 1 - 1/2 x + O(x²)
988
// inverse_sqrt(length_squared) ≈ 1 - 1/2 (length_squared - 1) = 1/2 (3 - length_squared)
989
990
// Iterative calls to this method quickly converge to a normalized value,
991
// so long as the denormalization is not large ~ O(1/10).
992
// One iteration can be described as:
993
// l_sq <- l_sq * (1 - 1/2 (l_sq - 1))²;
994
// Rewriting in terms of the error x:
995
// 1 + x <- (1 + x) * (1 - 1/2 x)²
996
// 1 + x <- (1 + x) * (1 - x + 1/4 x²)
997
// 1 + x <- 1 - x + 1/4 x² + x - x² + 1/4 x³
998
// x <- -1/4 x² (3 - x)
999
// If the error is small, say in a range of (-1/2, 1/2), then:
1000
// |-1/4 x² (3 - x)| <= (3/4 + 1/4 * |x|) * x² <= (3/4 + 1/4 * 1/2) * x² < x² < 1/2 x
1001
// Therefore the sequence of iterates converges to 0 error as a second order method.
1002
1003
let length_squared = self.0.length_squared();
1004
Self(self * (0.5 * (3.0 - length_squared)))
1005
}
1006
}
1007
1008
impl TryFrom<Vec4> for Dir4 {
1009
type Error = InvalidDirectionError;
1010
1011
fn try_from(value: Vec4) -> Result<Self, Self::Error> {
1012
Self::new(value)
1013
}
1014
}
1015
1016
impl core::ops::Deref for Dir4 {
1017
type Target = Vec4;
1018
fn deref(&self) -> &Self::Target {
1019
&self.0
1020
}
1021
}
1022
1023
impl core::ops::Neg for Dir4 {
1024
type Output = Self;
1025
fn neg(self) -> Self::Output {
1026
Self(-self.0)
1027
}
1028
}
1029
1030
impl core::ops::Mul<f32> for Dir4 {
1031
type Output = Vec4;
1032
fn mul(self, rhs: f32) -> Self::Output {
1033
self.0 * rhs
1034
}
1035
}
1036
1037
impl core::ops::Mul<Dir4> for f32 {
1038
type Output = Vec4;
1039
fn mul(self, rhs: Dir4) -> Self::Output {
1040
self * rhs.0
1041
}
1042
}
1043
1044
impl fmt::Display for Dir4 {
1045
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1046
write!(f, "{}", self.0)
1047
}
1048
}
1049
1050
#[cfg(feature = "approx")]
1051
impl approx::AbsDiffEq for Dir4 {
1052
type Epsilon = f32;
1053
fn default_epsilon() -> f32 {
1054
f32::EPSILON
1055
}
1056
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
1057
self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
1058
}
1059
}
1060
1061
#[cfg(feature = "approx")]
1062
impl approx::RelativeEq for Dir4 {
1063
fn default_max_relative() -> f32 {
1064
f32::EPSILON
1065
}
1066
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
1067
self.as_ref()
1068
.relative_eq(other.as_ref(), epsilon, max_relative)
1069
}
1070
}
1071
1072
#[cfg(feature = "approx")]
1073
impl approx::UlpsEq for Dir4 {
1074
fn default_max_ulps() -> u32 {
1075
4
1076
}
1077
1078
fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
1079
self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
1080
}
1081
}
1082
1083
#[cfg(test)]
1084
#[cfg(feature = "approx")]
1085
mod tests {
1086
use crate::ops;
1087
1088
use super::*;
1089
use approx::assert_relative_eq;
1090
1091
#[test]
1092
fn dir2_creation() {
1093
assert_eq!(Dir2::new(Vec2::X * 12.5), Ok(Dir2::X));
1094
assert_eq!(
1095
Dir2::new(Vec2::new(0.0, 0.0)),
1096
Err(InvalidDirectionError::Zero)
1097
);
1098
assert_eq!(
1099
Dir2::new(Vec2::new(f32::INFINITY, 0.0)),
1100
Err(InvalidDirectionError::Infinite)
1101
);
1102
assert_eq!(
1103
Dir2::new(Vec2::new(f32::NEG_INFINITY, 0.0)),
1104
Err(InvalidDirectionError::Infinite)
1105
);
1106
assert_eq!(
1107
Dir2::new(Vec2::new(f32::NAN, 0.0)),
1108
Err(InvalidDirectionError::NaN)
1109
);
1110
assert_eq!(Dir2::new_and_length(Vec2::X * 6.5), Ok((Dir2::X, 6.5)));
1111
}
1112
1113
#[test]
1114
fn dir2_slerp() {
1115
assert_relative_eq!(
1116
Dir2::X.slerp(Dir2::Y, 0.5),
1117
Dir2::from_xy(ops::sqrt(0.5_f32), ops::sqrt(0.5_f32)).unwrap()
1118
);
1119
assert_eq!(Dir2::Y.slerp(Dir2::X, 0.0), Dir2::Y);
1120
assert_relative_eq!(Dir2::X.slerp(Dir2::Y, 1.0), Dir2::Y);
1121
assert_relative_eq!(
1122
Dir2::Y.slerp(Dir2::X, 1.0 / 3.0),
1123
Dir2::from_xy(0.5, ops::sqrt(0.75_f32)).unwrap()
1124
);
1125
assert_relative_eq!(
1126
Dir2::X.slerp(Dir2::Y, 2.0 / 3.0),
1127
Dir2::from_xy(0.5, ops::sqrt(0.75_f32)).unwrap()
1128
);
1129
}
1130
1131
#[test]
1132
fn dir2_to_rotation2d() {
1133
assert_relative_eq!(Dir2::EAST.rotation_to(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
1134
assert_relative_eq!(Dir2::NORTH.rotation_from(Dir2::NORTH_EAST), Rot2::FRAC_PI_4);
1135
assert_relative_eq!(Dir2::SOUTH.rotation_to_x(), Rot2::FRAC_PI_2);
1136
assert_relative_eq!(Dir2::SOUTH.rotation_to_y(), Rot2::PI);
1137
assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_x(), Rot2::degrees(135.0));
1138
assert_relative_eq!(Dir2::NORTH_WEST.rotation_from_y(), Rot2::FRAC_PI_4);
1139
}
1140
1141
#[test]
1142
fn dir2_renorm() {
1143
// Evil denormalized Rot2
1144
let (sin, cos) = ops::sin_cos(1.0_f32);
1145
let rot2 = Rot2::from_sin_cos(sin * (1.0 + 1e-5), cos * (1.0 + 1e-5));
1146
let mut dir_a = Dir2::X;
1147
let mut dir_b = Dir2::X;
1148
1149
// We test that renormalizing an already normalized dir doesn't do anything
1150
assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1151
1152
for _ in 0..50 {
1153
dir_a = rot2 * dir_a;
1154
dir_b = rot2 * dir_b;
1155
dir_b = dir_b.fast_renormalize();
1156
}
1157
1158
// `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1159
assert!(
1160
!dir_a.is_normalized(),
1161
"Denormalization doesn't work, test is faulty"
1162
);
1163
assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1164
}
1165
1166
#[test]
1167
fn dir3_creation() {
1168
assert_eq!(Dir3::new(Vec3::X * 12.5), Ok(Dir3::X));
1169
assert_eq!(
1170
Dir3::new(Vec3::new(0.0, 0.0, 0.0)),
1171
Err(InvalidDirectionError::Zero)
1172
);
1173
assert_eq!(
1174
Dir3::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
1175
Err(InvalidDirectionError::Infinite)
1176
);
1177
assert_eq!(
1178
Dir3::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
1179
Err(InvalidDirectionError::Infinite)
1180
);
1181
assert_eq!(
1182
Dir3::new(Vec3::new(f32::NAN, 0.0, 0.0)),
1183
Err(InvalidDirectionError::NaN)
1184
);
1185
assert_eq!(Dir3::new_and_length(Vec3::X * 6.5), Ok((Dir3::X, 6.5)));
1186
1187
// Test rotation
1188
assert!(
1189
(Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3::X)
1190
.abs_diff_eq(Vec3::Y, 10e-6)
1191
);
1192
}
1193
1194
#[test]
1195
fn dir3_slerp() {
1196
assert_relative_eq!(
1197
Dir3::X.slerp(Dir3::Y, 0.5),
1198
Dir3::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1199
);
1200
assert_relative_eq!(Dir3::Y.slerp(Dir3::Z, 0.0), Dir3::Y);
1201
assert_relative_eq!(Dir3::Z.slerp(Dir3::X, 1.0), Dir3::X, epsilon = 0.000001);
1202
assert_relative_eq!(
1203
Dir3::X.slerp(Dir3::Z, 1.0 / 3.0),
1204
Dir3::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1205
epsilon = 0.000001
1206
);
1207
assert_relative_eq!(
1208
Dir3::Z.slerp(Dir3::Y, 2.0 / 3.0),
1209
Dir3::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1210
);
1211
}
1212
1213
#[test]
1214
fn dir3_renorm() {
1215
// Evil denormalized quaternion
1216
let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1217
let mut dir_a = Dir3::X;
1218
let mut dir_b = Dir3::X;
1219
1220
// We test that renormalizing an already normalized dir doesn't do anything
1221
assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1222
1223
for _ in 0..50 {
1224
dir_a = rot3 * dir_a;
1225
dir_b = rot3 * dir_b;
1226
dir_b = dir_b.fast_renormalize();
1227
}
1228
1229
// `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1230
assert!(
1231
!dir_a.is_normalized(),
1232
"Denormalization doesn't work, test is faulty"
1233
);
1234
assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1235
}
1236
1237
#[test]
1238
fn dir3a_creation() {
1239
assert_eq!(Dir3A::new(Vec3A::X * 12.5), Ok(Dir3A::X));
1240
assert_eq!(
1241
Dir3A::new(Vec3A::new(0.0, 0.0, 0.0)),
1242
Err(InvalidDirectionError::Zero)
1243
);
1244
assert_eq!(
1245
Dir3A::new(Vec3A::new(f32::INFINITY, 0.0, 0.0)),
1246
Err(InvalidDirectionError::Infinite)
1247
);
1248
assert_eq!(
1249
Dir3A::new(Vec3A::new(f32::NEG_INFINITY, 0.0, 0.0)),
1250
Err(InvalidDirectionError::Infinite)
1251
);
1252
assert_eq!(
1253
Dir3A::new(Vec3A::new(f32::NAN, 0.0, 0.0)),
1254
Err(InvalidDirectionError::NaN)
1255
);
1256
assert_eq!(Dir3A::new_and_length(Vec3A::X * 6.5), Ok((Dir3A::X, 6.5)));
1257
1258
// Test rotation
1259
assert!(
1260
(Quat::from_rotation_z(core::f32::consts::FRAC_PI_2) * Dir3A::X)
1261
.abs_diff_eq(Vec3A::Y, 10e-6)
1262
);
1263
}
1264
1265
#[test]
1266
fn dir3a_slerp() {
1267
assert_relative_eq!(
1268
Dir3A::X.slerp(Dir3A::Y, 0.5),
1269
Dir3A::from_xyz(ops::sqrt(0.5f32), ops::sqrt(0.5f32), 0.0).unwrap()
1270
);
1271
assert_relative_eq!(Dir3A::Y.slerp(Dir3A::Z, 0.0), Dir3A::Y);
1272
assert_relative_eq!(Dir3A::Z.slerp(Dir3A::X, 1.0), Dir3A::X, epsilon = 0.000001);
1273
assert_relative_eq!(
1274
Dir3A::X.slerp(Dir3A::Z, 1.0 / 3.0),
1275
Dir3A::from_xyz(ops::sqrt(0.75f32), 0.0, 0.5).unwrap(),
1276
epsilon = 0.000001
1277
);
1278
assert_relative_eq!(
1279
Dir3A::Z.slerp(Dir3A::Y, 2.0 / 3.0),
1280
Dir3A::from_xyz(0.0, ops::sqrt(0.75f32), 0.5).unwrap()
1281
);
1282
}
1283
1284
#[test]
1285
fn dir3a_renorm() {
1286
// Evil denormalized quaternion
1287
let rot3 = Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0) * (1.0 + 1e-5);
1288
let mut dir_a = Dir3A::X;
1289
let mut dir_b = Dir3A::X;
1290
1291
// We test that renormalizing an already normalized dir doesn't do anything
1292
assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1293
1294
for _ in 0..50 {
1295
dir_a = rot3 * dir_a;
1296
dir_b = rot3 * dir_b;
1297
dir_b = dir_b.fast_renormalize();
1298
}
1299
1300
// `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1301
assert!(
1302
!dir_a.is_normalized(),
1303
"Denormalization doesn't work, test is faulty"
1304
);
1305
assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1306
}
1307
1308
#[test]
1309
fn dir4_creation() {
1310
assert_eq!(Dir4::new(Vec4::X * 12.5), Ok(Dir4::X));
1311
assert_eq!(
1312
Dir4::new(Vec4::new(0.0, 0.0, 0.0, 0.0)),
1313
Err(InvalidDirectionError::Zero)
1314
);
1315
assert_eq!(
1316
Dir4::new(Vec4::new(f32::INFINITY, 0.0, 0.0, 0.0)),
1317
Err(InvalidDirectionError::Infinite)
1318
);
1319
assert_eq!(
1320
Dir4::new(Vec4::new(f32::NEG_INFINITY, 0.0, 0.0, 0.0)),
1321
Err(InvalidDirectionError::Infinite)
1322
);
1323
assert_eq!(
1324
Dir4::new(Vec4::new(f32::NAN, 0.0, 0.0, 0.0)),
1325
Err(InvalidDirectionError::NaN)
1326
);
1327
assert_eq!(Dir4::new_and_length(Vec4::X * 6.5), Ok((Dir4::X, 6.5)));
1328
}
1329
1330
#[test]
1331
fn dir4_renorm() {
1332
// Evil denormalized matrix
1333
let mat4 = bevy_math::Mat4::from_quat(Quat::from_euler(glam::EulerRot::XYZ, 1.0, 2.0, 3.0))
1334
* (1.0 + 1e-5);
1335
let mut dir_a = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1336
let mut dir_b = Dir4::from_xyzw(1., 1., 0., 0.).unwrap();
1337
// We test that renormalizing an already normalized dir doesn't do anything
1338
assert_relative_eq!(dir_b, dir_b.fast_renormalize(), epsilon = 0.000001);
1339
for _ in 0..50 {
1340
dir_a = Dir4(mat4 * *dir_a);
1341
dir_b = Dir4(mat4 * *dir_b);
1342
dir_b = dir_b.fast_renormalize();
1343
}
1344
// `dir_a` should've gotten denormalized, meanwhile `dir_b` should stay normalized.
1345
assert!(
1346
!dir_a.is_normalized(),
1347
"Denormalization doesn't work, test is faulty"
1348
);
1349
assert!(dir_b.is_normalized(), "Renormalisation did not work.");
1350
}
1351
}
1352
1353