mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 15:14:50 +00:00
Add new_and_length
method to Direction2d
and Direction3d
(#11172)
# Objective When creating a normalized direction from a vector, it can be useful to get both the direction *and* the original length of the vector. This came up when I was recreating some Parry APIs using bevy_math, and doing it manually is quite painful. Nalgebra calls this method [`Unit::try_new_and_get`](https://docs.rs/nalgebra/latest/nalgebra/base/struct.Unit.html#method.try_new_and_get). ## Solution Add a `new_and_length` method to `Direction2d` and `Direction3d`. Usage: ```rust if let Ok((direction, length)) = Direction2d::new_and_length(Vec2::X * 10.0) { assert_eq!(direction, Vec2::X); assert_eq!(length, 10.0); } ``` I'm open to different names, couldn't come up with a perfectly clear one that isn't too long. My reasoning with the current name is that it's like using `new` and calling `length` on the original vector.
This commit is contained in:
parent
42e990861c
commit
bcbb7bb9dd
3 changed files with 51 additions and 28 deletions
|
@ -21,20 +21,20 @@ impl Direction2d {
|
|||
/// 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,
|
||||
)
|
||||
Self::new_and_length(value).map(|(dir, _)| dir)
|
||||
}
|
||||
|
||||
/// Create a direction from a finite, nonzero [`Vec2`], also returning its original length.
|
||||
///
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
|
||||
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
|
||||
pub fn new_and_length(value: Vec2) -> Result<(Self, f32), InvalidDirectionError> {
|
||||
let length = value.length();
|
||||
let direction = (length.is_finite() && length > 0.0).then_some(value / length);
|
||||
|
||||
direction
|
||||
.map(|dir| (Self(dir), length))
|
||||
.map_or(Err(InvalidDirectionError::from_length(length)), Ok)
|
||||
}
|
||||
|
||||
/// Create a direction from its `x` and `y` components.
|
||||
|
@ -404,6 +404,10 @@ mod tests {
|
|||
Direction2d::new(Vec2::new(f32::NAN, 0.0)),
|
||||
Err(InvalidDirectionError::NaN)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction2d::new_and_length(Vec2::X * 6.5),
|
||||
Ok((Direction2d::from_normalized(Vec2::X), 6.5))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -25,20 +25,20 @@ impl Direction3d {
|
|||
/// 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,
|
||||
)
|
||||
Self::new_and_length(value).map(|(dir, _)| dir)
|
||||
}
|
||||
|
||||
/// Create a direction from a finite, nonzero [`Vec3`], also returning its original length.
|
||||
///
|
||||
/// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length
|
||||
/// of the given vector is zero (or very close to zero), infinite, or `NaN`.
|
||||
pub fn new_and_length(value: Vec3) -> Result<(Self, f32), InvalidDirectionError> {
|
||||
let length = value.length();
|
||||
let direction = (length.is_finite() && length > 0.0).then_some(value / length);
|
||||
|
||||
direction
|
||||
.map(|dir| (Self(dir), length))
|
||||
.map_or(Err(InvalidDirectionError::from_length(length)), Ok)
|
||||
}
|
||||
|
||||
/// Create a direction from its `x`, `y`, and `z` components.
|
||||
|
@ -421,5 +421,9 @@ mod test {
|
|||
Direction3d::new(Vec3::new(f32::NAN, 0.0, 0.0)),
|
||||
Err(InvalidDirectionError::NaN)
|
||||
);
|
||||
assert_eq!(
|
||||
Direction3d::new_and_length(Vec3::X * 6.5),
|
||||
Ok((Direction3d::from_normalized(Vec3::X), 6.5))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,21 @@ pub enum InvalidDirectionError {
|
|||
NaN,
|
||||
}
|
||||
|
||||
impl InvalidDirectionError {
|
||||
/// Creates an [`InvalidDirectionError`] from the length of an invalid direction vector.
|
||||
pub fn from_length(length: f32) -> Self {
|
||||
if length.is_nan() {
|
||||
InvalidDirectionError::NaN
|
||||
} else if !length.is_finite() {
|
||||
// If the direction is non-finite but also not NaN, it must be infinite
|
||||
InvalidDirectionError::Infinite
|
||||
} else {
|
||||
// If the direction is invalid but neither NaN nor infinite, it must be zero
|
||||
InvalidDirectionError::Zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InvalidDirectionError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
|
|
Loading…
Reference in a new issue