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