Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support rotating Direction3d by Quat #11649

Merged
merged 3 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 50 additions & 50 deletions crates/bevy_math/src/primitives/dim2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,31 +785,6 @@ mod tests {
use super::*;
use approx::assert_relative_eq;

#[test]
fn circle_math() {
let circle = Circle { radius: 3.0 };
assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
assert_eq!(circle.area(), 28.274334, "incorrect area");
assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
}

#[test]
fn ellipse_math() {
let ellipse = Ellipse::new(3.0, 1.0);
assert_eq!(ellipse.area(), 9.424778, "incorrect area");
}

#[test]
fn triangle_math() {
let triangle = Triangle2d::new(
Vec2::new(-2.0, -1.0),
Vec2::new(1.0, 4.0),
Vec2::new(7.0, 0.0),
);
assert_eq!(triangle.area(), 21.0, "incorrect area");
assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
}

#[test]
fn direction_creation() {
assert_eq!(Direction2d::new(Vec2::X * 12.5), Ok(Direction2d::X));
Expand All @@ -835,6 +810,56 @@ mod tests {
);
}

#[test]
fn rectangle_closest_point() {
let rectangle = Rectangle::new(2.0, 2.0);
assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
assert_eq!(
rectangle.closest_point(Vec2::new(0.25, 0.1)),
Vec2::new(0.25, 0.1)
);
}

#[test]
fn circle_closest_point() {
let circle = Circle { radius: 1.0 };
assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
assert_eq!(
circle.closest_point(Vec2::NEG_ONE * 10.0),
Vec2::NEG_ONE.normalize()
);
assert_eq!(
circle.closest_point(Vec2::new(0.25, 0.1)),
Vec2::new(0.25, 0.1)
);
}

#[test]
fn circle_math() {
let circle = Circle { radius: 3.0 };
assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
assert_eq!(circle.area(), 28.274334, "incorrect area");
assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
}

#[test]
fn ellipse_math() {
let ellipse = Ellipse::new(3.0, 1.0);
assert_eq!(ellipse.area(), 9.424778, "incorrect area");
}

#[test]
fn triangle_math() {
let triangle = Triangle2d::new(
Vec2::new(-2.0, -1.0),
Vec2::new(1.0, 4.0),
Vec2::new(7.0, 0.0),
);
assert_eq!(triangle.area(), 21.0, "incorrect area");
assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
}

#[test]
fn triangle_winding_order() {
let mut cw_triangle = Triangle2d::new(
Expand Down Expand Up @@ -936,29 +961,4 @@ mod tests {
< 1e-7,
);
}

#[test]
fn rectangle_closest_point() {
let rectangle = Rectangle::new(2.0, 2.0);
assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
assert_eq!(
rectangle.closest_point(Vec2::new(0.25, 0.1)),
Vec2::new(0.25, 0.1)
);
}

#[test]
fn circle_closest_point() {
let circle = Circle { radius: 1.0 };
assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
assert_eq!(
circle.closest_point(Vec2::NEG_ONE * 10.0),
Vec2::NEG_ONE.normalize()
);
assert_eq!(
circle.closest_point(Vec2::new(0.25, 0.1)),
Vec2::new(0.25, 0.1)
);
}
}
128 changes: 72 additions & 56 deletions crates/bevy_math/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::f32::consts::{FRAC_PI_3, PI};

use super::{Circle, InvalidDirectionError, Primitive3d};
use crate::Vec3;
use crate::{Quat, Vec3};

/// A normalized vector pointing in a direction in 3D space
#[derive(Clone, Copy, Debug, PartialEq)]
Expand Down Expand Up @@ -85,6 +85,21 @@ impl std::ops::Neg for Direction3d {
}
}

impl std::ops::Mul<Direction3d> for Quat {
type Output = Direction3d;

/// Rotates the [`Direction3d`] using a [`Quat`].
fn mul(self, direction: Direction3d) -> Self::Output {
let rotated = self * *direction;

// Make sure the result is normalized.
// This can fail for non-unit quaternions.
debug_assert!(rotated.is_normalized());

Direction3d::new_unchecked(rotated)
}
}

#[cfg(feature = "approx")]
impl approx::AbsDiffEq for Direction3d {
type Epsilon = f32;
Expand Down Expand Up @@ -687,6 +702,62 @@ mod tests {
use super::*;
use approx::assert_relative_eq;

#[test]
fn direction_creation() {
assert_eq!(Direction3d::new(Vec3::X * 12.5), Ok(Direction3d::X));
assert_eq!(
Direction3d::new(Vec3::new(0.0, 0.0, 0.0)),
Err(InvalidDirectionError::Zero)
);
assert_eq!(
Direction3d::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
Err(InvalidDirectionError::Infinite)
);
assert_eq!(
Direction3d::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
Err(InvalidDirectionError::Infinite)
);
assert_eq!(
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::X, 6.5))
);

// Test rotation
assert!(
(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2) * Direction3d::X)
.abs_diff_eq(Vec3::Y, 10e-6)
);
}

#[test]
fn cuboid_closest_point() {
let cuboid = Cuboid::new(2.0, 2.0, 2.0);
assert_eq!(cuboid.closest_point(Vec3::X * 10.0), Vec3::X);
assert_eq!(cuboid.closest_point(Vec3::NEG_ONE * 10.0), Vec3::NEG_ONE);
assert_eq!(
cuboid.closest_point(Vec3::new(0.25, 0.1, 0.3)),
Vec3::new(0.25, 0.1, 0.3)
);
}

#[test]
fn sphere_closest_point() {
let sphere = Sphere { radius: 1.0 };
assert_eq!(sphere.closest_point(Vec3::X * 10.0), Vec3::X);
assert_eq!(
sphere.closest_point(Vec3::NEG_ONE * 10.0),
Vec3::NEG_ONE.normalize()
);
assert_eq!(
sphere.closest_point(Vec3::new(0.25, 0.1, 0.3)),
Vec3::new(0.25, 0.1, 0.3)
);
}

#[test]
fn sphere_math() {
let sphere = Sphere { radius: 4.0 };
Expand Down Expand Up @@ -790,58 +861,3 @@ mod tests {
assert_relative_eq!(torus.volume(), 4.97428, epsilon = 0.00001);
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn direction_creation() {
assert_eq!(Direction3d::new(Vec3::X * 12.5), Ok(Direction3d::X));
assert_eq!(
Direction3d::new(Vec3::new(0.0, 0.0, 0.0)),
Err(InvalidDirectionError::Zero)
);
assert_eq!(
Direction3d::new(Vec3::new(f32::INFINITY, 0.0, 0.0)),
Err(InvalidDirectionError::Infinite)
);
assert_eq!(
Direction3d::new(Vec3::new(f32::NEG_INFINITY, 0.0, 0.0)),
Err(InvalidDirectionError::Infinite)
);
assert_eq!(
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::X, 6.5))
);
}

#[test]
fn cuboid_closest_point() {
let cuboid = Cuboid::new(2.0, 2.0, 2.0);
assert_eq!(cuboid.closest_point(Vec3::X * 10.0), Vec3::X);
assert_eq!(cuboid.closest_point(Vec3::NEG_ONE * 10.0), Vec3::NEG_ONE);
assert_eq!(
cuboid.closest_point(Vec3::new(0.25, 0.1, 0.3)),
Vec3::new(0.25, 0.1, 0.3)
);
}

#[test]
fn sphere_closest_point() {
let sphere = Sphere { radius: 1.0 };
assert_eq!(sphere.closest_point(Vec3::X * 10.0), Vec3::X);
assert_eq!(
sphere.closest_point(Vec3::NEG_ONE * 10.0),
Vec3::NEG_ONE.normalize()
);
assert_eq!(
sphere.closest_point(Vec3::new(0.25, 0.1, 0.3)),
Vec3::new(0.25, 0.1, 0.3)
);
}
}