mirror of
https://github.com/bevyengine/bevy
synced 2024-09-20 14:32:06 +00:00
Impl TryFrom
vector for directions and add InvalidDirectionError
(#10884)
# Objective Implement `TryFrom<Vec2>`/`TryFrom<Vec3>` for direction primitives as considered in #10857. ## Solution Implement `TryFrom` for the direction primitives. These are all equivalent: ```rust let dir2d = Direction2d::try_from(Vec2::new(0.5, 0.5)).unwrap(); let dir2d = Vec2::new(0.5, 0.5).try_into().unwrap(); // (assumes that the type is inferred) let dir2d = Direction2d::new(Vec2::new(0.5, 0.5)).unwrap(); ``` For error cases, an `Err(InvalidDirectionError)` is returned. It contains the type of failure: ```rust /// An error indicating that a direction is invalid. #[derive(Debug, PartialEq)] pub enum InvalidDirectionError { /// The length of the direction vector is zero or very close to zero. Zero, /// The length of the direction vector is `std::f32::INFINITY`. Infinite, /// The length of the direction vector is `NaN`. NaN, } ```
This commit is contained in:
parent
5508915e9b
commit
f683b802f1
3 changed files with 127 additions and 10 deletions
|
@ -1,16 +1,30 @@
|
|||
use super::{Primitive2d, WindingOrder};
|
||||
use super::{InvalidDirectionError, Primitive2d, WindingOrder};
|
||||
use crate::Vec2;
|
||||
|
||||
/// A normalized vector pointing in a direction in 2D space
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Direction2d(Vec2);
|
||||
|
||||
impl Direction2d {
|
||||
/// Create a direction from a finite, nonzero [`Vec2`].
|
||||
///
|
||||
/// Returns `None` if the input is zero (or very close to zero), or non-finite.
|
||||
pub fn new(value: Vec2) -> Option<Self> {
|
||||
value.try_normalize().map(Self)
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
|
||||
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
|
||||
pub fn new(value: Vec2) -> Result<Self, InvalidDirectionError> {
|
||||
value.try_normalize().map(Self).map_or_else(
|
||||
|| {
|
||||
if value.is_nan() {
|
||||
Err(InvalidDirectionError::NaN)
|
||||
} else if !value.is_finite() {
|
||||
// If the direction is non-finite but also not NaN, it must be infinite
|
||||
Err(InvalidDirectionError::Infinite)
|
||||
} else {
|
||||
// If the direction is invalid but neither NaN nor infinite, it must be zero
|
||||
Err(InvalidDirectionError::Zero)
|
||||
}
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a direction from a [`Vec2`] that is already normalized
|
||||
|
@ -20,6 +34,14 @@ impl Direction2d {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec2> for Direction2d {
|
||||
type Error = InvalidDirectionError;
|
||||
|
||||
fn try_from(value: Vec2) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Direction2d {
|
||||
type Target = Vec2;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -324,6 +346,30 @@ impl RegularPolygon {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn direction_creation() {
|
||||
assert_eq!(
|
||||
Direction2d::new(Vec2::X * 12.5),
|
||||
Ok(Direction2d::from_normalized(Vec2::X))
|
||||
);
|
||||
assert_eq!(
|
||||
Direction2d::new(Vec2::new(0.0, 0.0)),
|
||||
Err(InvalidDirectionError::Zero)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction2d::new(Vec2::new(std::f32::INFINITY, 0.0)),
|
||||
Err(InvalidDirectionError::Infinite)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction2d::new(Vec2::new(std::f32::NEG_INFINITY, 0.0)),
|
||||
Err(InvalidDirectionError::Infinite)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction2d::new(Vec2::new(std::f32::NAN, 0.0)),
|
||||
Err(InvalidDirectionError::NaN)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn triangle_winding_order() {
|
||||
let mut cw_triangle = Triangle2d::new(
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
use super::Primitive3d;
|
||||
use super::{InvalidDirectionError, Primitive3d};
|
||||
use crate::Vec3;
|
||||
|
||||
/// A normalized vector pointing in a direction in 3D space
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Direction3d(Vec3);
|
||||
|
||||
impl Direction3d {
|
||||
/// Create a direction from a finite, nonzero [`Vec3`].
|
||||
///
|
||||
/// Returns `None` if the input is zero (or very close to zero), or non-finite.
|
||||
pub fn new(value: Vec3) -> Option<Self> {
|
||||
value.try_normalize().map(Self)
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
|
||||
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
|
||||
pub fn new(value: Vec3) -> Result<Self, InvalidDirectionError> {
|
||||
value.try_normalize().map(Self).map_or_else(
|
||||
|| {
|
||||
if value.is_nan() {
|
||||
Err(InvalidDirectionError::NaN)
|
||||
} else if !value.is_finite() {
|
||||
// If the direction is non-finite but also not NaN, it must be infinite
|
||||
Err(InvalidDirectionError::Infinite)
|
||||
} else {
|
||||
// If the direction is invalid but neither NaN nor infinite, it must be zero
|
||||
Err(InvalidDirectionError::Zero)
|
||||
}
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a direction from a [`Vec3`] that is already normalized
|
||||
|
@ -20,6 +34,14 @@ impl Direction3d {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec3> for Direction3d {
|
||||
type Error = InvalidDirectionError;
|
||||
|
||||
fn try_from(value: Vec3) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Direction3d {
|
||||
type Target = Vec3;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -332,3 +354,32 @@ impl Torus {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn direction_creation() {
|
||||
assert_eq!(
|
||||
Direction3d::new(Vec3::X * 12.5),
|
||||
Ok(Direction3d::from_normalized(Vec3::X))
|
||||
);
|
||||
assert_eq!(
|
||||
Direction3d::new(Vec3::new(0.0, 0.0, 0.0)),
|
||||
Err(InvalidDirectionError::Zero)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction3d::new(Vec3::new(std::f32::INFINITY, 0.0, 0.0)),
|
||||
Err(InvalidDirectionError::Infinite)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction3d::new(Vec3::new(std::f32::NEG_INFINITY, 0.0, 0.0)),
|
||||
Err(InvalidDirectionError::Infinite)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction3d::new(Vec3::new(std::f32::NAN, 0.0, 0.0)),
|
||||
Err(InvalidDirectionError::NaN)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,26 @@ pub trait Primitive2d {}
|
|||
/// A marker trait for 3D primitives
|
||||
pub trait Primitive3d {}
|
||||
|
||||
/// An error indicating that a direction is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InvalidDirectionError {
|
||||
/// The length of the direction vector is zero or very close to zero.
|
||||
Zero,
|
||||
/// The length of the direction vector is `std::f32::INFINITY`.
|
||||
Infinite,
|
||||
/// The length of the direction vector is `NaN`.
|
||||
NaN,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InvalidDirectionError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Direction can not be zero (or very close to zero), or non-finite."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The winding order for a set of points
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum WindingOrder {
|
||||
|
|
Loading…
Reference in a new issue