diff --git a/crates/bevy_math/src/bounding/bounded2d/mod.rs b/crates/bevy_math/src/bounding/bounded2d/mod.rs index 87123075a9..631e5559d8 100644 --- a/crates/bevy_math/src/bounding/bounded2d/mod.rs +++ b/crates/bevy_math/src/bounding/bounded2d/mod.rs @@ -1,7 +1,10 @@ mod primitive_impls; use super::{BoundingVolume, IntersectsVolume}; -use crate::prelude::{Mat2, Rot2, Vec2}; +use crate::{ + prelude::{Mat2, Rot2, Vec2}, + Isometry2d, +}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; @@ -18,14 +21,12 @@ fn point_cloud_2d_center(points: &[Vec2]) -> Vec2 { points.iter().fold(Vec2::ZERO, |acc, point| acc + *point) * denom } -/// A trait with methods that return 2D bounded volumes for a shape +/// A trait with methods that return 2D bounding volumes for a shape. pub trait Bounded2d { - /// Get an axis-aligned bounding box for the shape with the given translation and rotation. - /// The rotation is in radians, counterclockwise, with 0 meaning no rotation. - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d; - /// Get a bounding circle for the shape - /// The rotation is in radians, counterclockwise, with 0 meaning no rotation. - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle; + /// Get an axis-aligned bounding box for the shape translated and rotated by the given isometry. + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d; + /// Get a bounding circle for the shape translated and rotated by the given isometry. + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle; } /// A 2D axis-aligned bounding box, or bounding rectangle @@ -51,20 +52,15 @@ impl Aabb2d { } /// Computes the smallest [`Aabb2d`] containing the given set of points, - /// transformed by `translation` and `rotation`. + /// transformed by the rotation and translation of the given isometry. /// /// # Panics /// /// Panics if the given set of points is empty. #[inline(always)] - pub fn from_point_cloud( - translation: Vec2, - rotation: impl Into, - points: &[Vec2], - ) -> Aabb2d { + pub fn from_point_cloud(isometry: Isometry2d, points: &[Vec2]) -> Aabb2d { // Transform all points by rotation - let rotation: Rot2 = rotation.into(); - let mut iter = points.iter().map(|point| rotation * *point); + let mut iter = points.iter().map(|point| isometry.rotation * *point); let first = iter .next() @@ -75,8 +71,8 @@ impl Aabb2d { }); Aabb2d { - min: min + translation, - max: max + translation, + min: min + isometry.translation, + max: max + isometry.translation, } } @@ -472,16 +468,11 @@ impl BoundingCircle { } /// Computes a [`BoundingCircle`] containing the given set of points, - /// transformed by `translation` and `rotation`. + /// transformed by the rotation and translation of the given isometry. /// /// The bounding circle is not guaranteed to be the smallest possible. #[inline(always)] - pub fn from_point_cloud( - translation: Vec2, - rotation: impl Into, - points: &[Vec2], - ) -> BoundingCircle { - let rotation: Rot2 = rotation.into(); + pub fn from_point_cloud(isometry: Isometry2d, points: &[Vec2]) -> BoundingCircle { let center = point_cloud_2d_center(points); let mut radius_squared = 0.0; @@ -493,7 +484,7 @@ impl BoundingCircle { } } - BoundingCircle::new(rotation * center + translation, radius_squared.sqrt()) + BoundingCircle::new(isometry * center, radius_squared.sqrt()) } /// Get the radius of the bounding circle diff --git a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs index 871df86def..4452b87c69 100644 --- a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs @@ -6,7 +6,7 @@ use crate::{ CircularSegment, Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d, }, - Dir2, Mat2, Rot2, Vec2, + Dir2, Isometry2d, Mat2, Rot2, Vec2, }; use std::f32::consts::{FRAC_PI_2, PI, TAU}; @@ -15,12 +15,12 @@ use smallvec::SmallVec; use super::{Aabb2d, Bounded2d, BoundingCircle}; impl Bounded2d for Circle { - fn aabb_2d(&self, translation: Vec2, _rotation: impl Into) -> Aabb2d { - Aabb2d::new(translation, Vec2::splat(self.radius)) + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + Aabb2d::new(isometry.translation, Vec2::splat(self.radius)) } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, self.radius) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, self.radius) } } @@ -57,49 +57,52 @@ fn arc_bounding_points(arc: Arc2d, rotation: impl Into) -> SmallVec<[Vec2; } impl Bounded2d for Arc2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { // If our arc covers more than a circle, just return the bounding box of the circle. if self.half_angle >= PI { - return Circle::new(self.radius).aabb_2d(translation, rotation); + return Circle::new(self.radius).aabb_2d(isometry); } - Aabb2d::from_point_cloud(translation, 0.0, &arc_bounding_points(*self, rotation)) + Aabb2d::from_point_cloud( + Isometry2d::from_translation(isometry.translation), + &arc_bounding_points(*self, isometry.rotation), + ) } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { // There are two possibilities for the bounding circle. if self.is_major() { // If the arc is major, then the widest distance between two points is a diameter of the arc's circle; // therefore, that circle is the bounding radius. - BoundingCircle::new(translation, self.radius) + BoundingCircle::new(isometry.translation, self.radius) } else { // Otherwise, the widest distance between two points is the chord, // so a circle of that diameter around the midpoint will contain the entire arc. - let center = rotation.into() * self.chord_midpoint(); - BoundingCircle::new(center + translation, self.half_chord_length()) + let center = isometry.rotation * self.chord_midpoint(); + BoundingCircle::new(center + isometry.translation, self.half_chord_length()) } } } impl Bounded2d for CircularSector { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { // If our sector covers more than a circle, just return the bounding box of the circle. if self.half_angle() >= PI { - return Circle::new(self.radius()).aabb_2d(translation, rotation); + return Circle::new(self.radius()).aabb_2d(isometry); } // Otherwise, we use the same logic as for Arc2d, above, just with the circle's center as an additional possibility. - let mut bounds = arc_bounding_points(self.arc, rotation); + let mut bounds = arc_bounding_points(self.arc, isometry.rotation); bounds.push(Vec2::ZERO); - Aabb2d::from_point_cloud(translation, 0.0, &bounds) + Aabb2d::from_point_cloud(Isometry2d::from_translation(isometry.translation), &bounds) } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { if self.arc.is_major() { // If the arc is major, that is, greater than a semicircle, // then bounding circle is just the circle defining the sector. - BoundingCircle::new(translation, self.arc.radius) + BoundingCircle::new(isometry.translation, self.arc.radius) } else { // However, when the arc is minor, // we need our bounding circle to include both endpoints of the arc as well as the circle center. @@ -111,25 +114,23 @@ impl Bounded2d for CircularSector { self.arc.left_endpoint(), self.arc.right_endpoint(), ) - .bounding_circle(translation, rotation) + .bounding_circle(isometry) } } } impl Bounded2d for CircularSegment { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - self.arc.aabb_2d(translation, rotation) + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + self.arc.aabb_2d(isometry) } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { - self.arc.bounding_circle(translation, rotation) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + self.arc.bounding_circle(isometry) } } impl Bounded2d for Ellipse { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation: Rot2 = rotation.into(); - + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { // V = (hh * cos(beta), hh * sin(beta)) // #####*##### // ### | ### @@ -142,7 +143,7 @@ impl Bounded2d for Ellipse { let (hw, hh) = (self.half_size.x, self.half_size.y); // Sine and cosine of rotation angle alpha. - let (alpha_sin, alpha_cos) = rotation.sin_cos(); + let (alpha_sin, alpha_cos) = isometry.rotation.sin_cos(); // Sine and cosine of alpha + pi/2. We can avoid the trigonometric functions: // sin(beta) = sin(alpha + pi/2) = cos(alpha) @@ -155,51 +156,48 @@ impl Bounded2d for Ellipse { let half_size = Vec2::new(ux.hypot(vx), uy.hypot(vy)); - Aabb2d::new(translation, half_size) + Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, self.semi_major()) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, self.semi_major()) } } impl Bounded2d for Annulus { - fn aabb_2d(&self, translation: Vec2, _rotation: impl Into) -> Aabb2d { - Aabb2d::new(translation, Vec2::splat(self.outer_circle.radius)) + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + Aabb2d::new(isometry.translation, Vec2::splat(self.outer_circle.radius)) } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, self.outer_circle.radius) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, self.outer_circle.radius) } } impl Bounded2d for Rhombus { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation_mat = rotation.into(); - + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { let [rotated_x_half_diagonal, rotated_y_half_diagonal] = [ - rotation_mat * Vec2::new(self.half_diagonals.x, 0.0), - rotation_mat * Vec2::new(0.0, self.half_diagonals.y), + isometry.rotation * Vec2::new(self.half_diagonals.x, 0.0), + isometry.rotation * Vec2::new(0.0, self.half_diagonals.y), ]; let aabb_half_extent = rotated_x_half_diagonal .abs() .max(rotated_y_half_diagonal.abs()); Aabb2d { - min: -aabb_half_extent + translation, - max: aabb_half_extent + translation, + min: -aabb_half_extent + isometry.translation, + max: aabb_half_extent + isometry.translation, } } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, self.circumradius()) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, self.circumradius()) } } impl Bounded2d for Plane2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation: Rot2 = rotation.into(); - let normal = rotation * *self.normal; + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + let normal = isometry.rotation * *self.normal; let facing_x = normal == Vec2::X || normal == Vec2::NEG_X; let facing_y = normal == Vec2::Y || normal == Vec2::NEG_Y; @@ -209,18 +207,17 @@ impl Bounded2d for Plane2d { let half_height = if facing_y { 0.0 } else { f32::MAX / 2.0 }; let half_size = Vec2::new(half_width, half_height); - Aabb2d::new(translation, half_size) + Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, f32::MAX / 2.0) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded2d for Line2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation: Rot2 = rotation.into(); - let direction = rotation * *self.direction; + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + let direction = isometry.rotation * *self.direction; // Dividing `f32::MAX` by 2.0 is helpful so that we can do operations // like growing or shrinking the AABB without breaking things. @@ -229,65 +226,62 @@ impl Bounded2d for Line2d { let half_height = if direction.y == 0.0 { 0.0 } else { max }; let half_size = Vec2::new(half_width, half_height); - Aabb2d::new(translation, half_size) + Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, f32::MAX / 2.0) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded2d for Segment2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { // Rotate the segment by `rotation` - let rotation: Rot2 = rotation.into(); - let direction = rotation * *self.direction; + let direction = isometry.rotation * *self.direction; let half_size = (self.half_length * direction).abs(); - Aabb2d::new(translation, half_size) + Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, self.half_length) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, self.half_length) } } impl Bounded2d for Polyline2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - Aabb2d::from_point_cloud(translation, rotation, &self.vertices) + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { - BoundingCircle::from_point_cloud(translation, rotation, &self.vertices) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for BoxedPolyline2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - Aabb2d::from_point_cloud(translation, rotation, &self.vertices) + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { - BoundingCircle::from_point_cloud(translation, rotation, &self.vertices) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for Triangle2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation: Rot2 = rotation.into(); - let [a, b, c] = self.vertices.map(|vtx| rotation * vtx); + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + let [a, b, c] = self.vertices.map(|vtx| isometry.rotation * vtx); let min = Vec2::new(a.x.min(b.x).min(c.x), a.y.min(b.y).min(c.y)); let max = Vec2::new(a.x.max(b.x).max(c.x), a.y.max(b.y).max(c.y)); Aabb2d { - min: min + translation, - max: max + translation, + min: min + isometry.translation, + max: max + isometry.translation, } } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { - let rotation: Rot2 = rotation.into(); + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { let [a, b, c] = self.vertices; // The points of the segment opposite to the obtuse or right angle if one exists @@ -308,85 +302,79 @@ impl Bounded2d for Triangle2d { // The triangle is obtuse or right, so the minimum bounding circle's diameter is equal to the longest side. // We can compute the minimum bounding circle from the line segment of the longest side. let (segment, center) = Segment2d::from_points(point1, point2); - segment.bounding_circle(rotation * center + translation, rotation) + segment.bounding_circle(isometry * Isometry2d::from_translation(center)) } else { // The triangle is acute, so the smallest bounding circle is the circumcircle. let (Circle { radius }, circumcenter) = self.circumcircle(); - BoundingCircle::new(rotation * circumcenter + translation, radius) + BoundingCircle::new(isometry * circumcenter, radius) } } } impl Bounded2d for Rectangle { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation: Rot2 = rotation.into(); - + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { // Compute the AABB of the rotated rectangle by transforming the half-extents // by an absolute rotation matrix. - let (sin, cos) = rotation.sin_cos(); + let (sin, cos) = isometry.rotation.sin_cos(); let abs_rot_mat = Mat2::from_cols_array(&[cos.abs(), sin.abs(), sin.abs(), cos.abs()]); let half_size = abs_rot_mat * self.half_size; - Aabb2d::new(translation, half_size) + Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { let radius = self.half_size.length(); - BoundingCircle::new(translation, radius) + BoundingCircle::new(isometry.translation, radius) } } impl Bounded2d for Polygon { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - Aabb2d::from_point_cloud(translation, rotation, &self.vertices) + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { - BoundingCircle::from_point_cloud(translation, rotation, &self.vertices) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for BoxedPolygon { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - Aabb2d::from_point_cloud(translation, rotation, &self.vertices) + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { - BoundingCircle::from_point_cloud(translation, rotation, &self.vertices) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for RegularPolygon { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation: Rot2 = rotation.into(); - + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { let mut min = Vec2::ZERO; let mut max = Vec2::ZERO; - for vertex in self.vertices(rotation.as_radians()) { + for vertex in self.vertices(isometry.rotation.as_radians()) { min = min.min(vertex); max = max.max(vertex); } Aabb2d { - min: min + translation, - max: max + translation, + min: min + isometry.translation, + max: max + isometry.translation, } } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, self.circumcircle.radius) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, self.circumcircle.radius) } } impl Bounded2d for Capsule2d { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation: Rot2 = rotation.into(); - + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { // Get the line segment between the hemicircles of the rotated capsule let segment = Segment2d { // Multiplying a normalized vector (Vec2::Y) with a rotation returns a normalized vector. - direction: rotation * Dir2::Y, + direction: isometry.rotation * Dir2::Y, half_length: self.half_length, }; let (a, b) = (segment.point1(), segment.point2()); @@ -396,13 +384,13 @@ impl Bounded2d for Capsule2d { let max = a.max(b) + Vec2::splat(self.radius); Aabb2d { - min: min + translation, - max: max + translation, + min: min + isometry.translation, + max: max + isometry.translation, } } - fn bounding_circle(&self, translation: Vec2, _rotation: impl Into) -> BoundingCircle { - BoundingCircle::new(translation, self.radius + self.half_length) + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + BoundingCircle::new(isometry.translation, self.radius + self.half_length) } } @@ -420,19 +408,20 @@ mod tests { Plane2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d, }, - Dir2, + Dir2, Isometry2d, Rot2, }; #[test] fn circle() { let circle = Circle { radius: 1.0 }; let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = circle.aabb_2d(translation, 0.0); + let aabb = circle.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(1.0, 0.0)); assert_eq!(aabb.max, Vec2::new(3.0, 2.0)); - let bounding_circle = circle.bounding_circle(translation, 0.0); + let bounding_circle = circle.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 1.0); } @@ -451,6 +440,12 @@ mod tests { bounding_circle_radius: f32, } + impl TestCase { + fn isometry(&self) -> Isometry2d { + Isometry2d::new(self.translation, self.rotation.into()) + } + } + // The apothem of an arc covering 1/6th of a circle. let apothem = f32::sqrt(3.0) / 2.0; let tests = [ @@ -565,17 +560,17 @@ mod tests { println!("subtest case: {}", test.name); let segment: CircularSegment = test.arc.into(); - let arc_aabb = test.arc.aabb_2d(test.translation, test.rotation); + let arc_aabb = test.arc.aabb_2d(test.isometry()); assert_abs_diff_eq!(test.aabb_min, arc_aabb.min); assert_abs_diff_eq!(test.aabb_max, arc_aabb.max); - let segment_aabb = segment.aabb_2d(test.translation, test.rotation); + let segment_aabb = segment.aabb_2d(test.isometry()); assert_abs_diff_eq!(test.aabb_min, segment_aabb.min); assert_abs_diff_eq!(test.aabb_max, segment_aabb.max); - let arc_bounding_circle = test.arc.bounding_circle(test.translation, test.rotation); + let arc_bounding_circle = test.arc.bounding_circle(test.isometry()); assert_abs_diff_eq!(test.bounding_circle_center, arc_bounding_circle.center); assert_abs_diff_eq!(test.bounding_circle_radius, arc_bounding_circle.radius()); - let segment_bounding_circle = segment.bounding_circle(test.translation, test.rotation); + let segment_bounding_circle = segment.bounding_circle(test.isometry()); assert_abs_diff_eq!(test.bounding_circle_center, segment_bounding_circle.center); assert_abs_diff_eq!( test.bounding_circle_radius, @@ -597,6 +592,12 @@ mod tests { bounding_circle_radius: f32, } + impl TestCase { + fn isometry(&self) -> Isometry2d { + Isometry2d::new(self.translation, self.rotation.into()) + } + } + // The apothem of an arc covering 1/6th of a circle. let apothem = f32::sqrt(3.0) / 2.0; let inv_sqrt_3 = f32::sqrt(3.0).recip(); @@ -715,11 +716,11 @@ mod tests { println!("subtest case: {}", test.name); let sector: CircularSector = test.arc.into(); - let aabb = sector.aabb_2d(test.translation, test.rotation); + let aabb = sector.aabb_2d(test.isometry()); assert_abs_diff_eq!(test.aabb_min, aabb.min); assert_abs_diff_eq!(test.aabb_max, aabb.max); - let bounding_circle = sector.bounding_circle(test.translation, test.rotation); + let bounding_circle = sector.bounding_circle(test.isometry()); assert_abs_diff_eq!(test.bounding_circle_center, bounding_circle.center); assert_abs_diff_eq!(test.bounding_circle_radius, bounding_circle.radius()); } @@ -729,12 +730,13 @@ mod tests { fn ellipse() { let ellipse = Ellipse::new(1.0, 0.5); let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = ellipse.aabb_2d(translation, 0.0); + let aabb = ellipse.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(1.0, 0.5)); assert_eq!(aabb.max, Vec2::new(3.0, 1.5)); - let bounding_circle = ellipse.bounding_circle(translation, 0.0); + let bounding_circle = ellipse.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 1.0); } @@ -743,12 +745,14 @@ mod tests { fn annulus() { let annulus = Annulus::new(1.0, 2.0); let translation = Vec2::new(2.0, 1.0); + let rotation = Rot2::radians(1.0); + let isometry = Isometry2d::new(translation, rotation); - let aabb = annulus.aabb_2d(translation, 1.0); + let aabb = annulus.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(0.0, -1.0)); assert_eq!(aabb.max, Vec2::new(4.0, 3.0)); - let bounding_circle = annulus.bounding_circle(translation, 1.0); + let bounding_circle = annulus.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 2.0); } @@ -757,23 +761,26 @@ mod tests { fn rhombus() { let rhombus = Rhombus::new(2.0, 1.0); let translation = Vec2::new(2.0, 1.0); + let rotation = Rot2::radians(std::f32::consts::FRAC_PI_4); + let isometry = Isometry2d::new(translation, rotation); - let aabb = rhombus.aabb_2d(translation, std::f32::consts::FRAC_PI_4); + let aabb = rhombus.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(1.2928932, 0.29289323)); assert_eq!(aabb.max, Vec2::new(2.7071068, 1.7071068)); - let bounding_circle = rhombus.bounding_circle(translation, std::f32::consts::FRAC_PI_4); + let bounding_circle = rhombus.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 1.0); let rhombus = Rhombus::new(0.0, 0.0); let translation = Vec2::new(0.0, 0.0); + let isometry = Isometry2d::new(translation, rotation); - let aabb = rhombus.aabb_2d(translation, std::f32::consts::FRAC_PI_4); + let aabb = rhombus.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(0.0, 0.0)); assert_eq!(aabb.max, Vec2::new(0.0, 0.0)); - let bounding_circle = rhombus.bounding_circle(translation, std::f32::consts::FRAC_PI_4); + let bounding_circle = rhombus.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 0.0); } @@ -781,20 +788,21 @@ mod tests { #[test] fn plane() { let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb1 = Plane2d::new(Vec2::X).aabb_2d(translation, 0.0); + let aabb1 = Plane2d::new(Vec2::X).aabb_2d(isometry); assert_eq!(aabb1.min, Vec2::new(2.0, -f32::MAX / 2.0)); assert_eq!(aabb1.max, Vec2::new(2.0, f32::MAX / 2.0)); - let aabb2 = Plane2d::new(Vec2::Y).aabb_2d(translation, 0.0); + let aabb2 = Plane2d::new(Vec2::Y).aabb_2d(isometry); assert_eq!(aabb2.min, Vec2::new(-f32::MAX / 2.0, 1.0)); assert_eq!(aabb2.max, Vec2::new(f32::MAX / 2.0, 1.0)); - let aabb3 = Plane2d::new(Vec2::ONE).aabb_2d(translation, 0.0); + let aabb3 = Plane2d::new(Vec2::ONE).aabb_2d(isometry); assert_eq!(aabb3.min, Vec2::new(-f32::MAX / 2.0, -f32::MAX / 2.0)); assert_eq!(aabb3.max, Vec2::new(f32::MAX / 2.0, f32::MAX / 2.0)); - let bounding_circle = Plane2d::new(Vec2::Y).bounding_circle(translation, 0.0); + let bounding_circle = Plane2d::new(Vec2::Y).bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), f32::MAX / 2.0); } @@ -802,23 +810,24 @@ mod tests { #[test] fn line() { let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb1 = Line2d { direction: Dir2::Y }.aabb_2d(translation, 0.0); + let aabb1 = Line2d { direction: Dir2::Y }.aabb_2d(isometry); assert_eq!(aabb1.min, Vec2::new(2.0, -f32::MAX / 2.0)); assert_eq!(aabb1.max, Vec2::new(2.0, f32::MAX / 2.0)); - let aabb2 = Line2d { direction: Dir2::X }.aabb_2d(translation, 0.0); + let aabb2 = Line2d { direction: Dir2::X }.aabb_2d(isometry); assert_eq!(aabb2.min, Vec2::new(-f32::MAX / 2.0, 1.0)); assert_eq!(aabb2.max, Vec2::new(f32::MAX / 2.0, 1.0)); let aabb3 = Line2d { direction: Dir2::from_xy(1.0, 1.0).unwrap(), } - .aabb_2d(translation, 0.0); + .aabb_2d(isometry); assert_eq!(aabb3.min, Vec2::new(-f32::MAX / 2.0, -f32::MAX / 2.0)); assert_eq!(aabb3.max, Vec2::new(f32::MAX / 2.0, f32::MAX / 2.0)); - let bounding_circle = Line2d { direction: Dir2::Y }.bounding_circle(translation, 0.0); + let bounding_circle = Line2d { direction: Dir2::Y }.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), f32::MAX / 2.0); } @@ -826,13 +835,14 @@ mod tests { #[test] fn segment() { let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); let segment = Segment2d::from_points(Vec2::new(-1.0, -0.5), Vec2::new(1.0, 0.5)).0; - let aabb = segment.aabb_2d(translation, 0.0); + let aabb = segment.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(1.0, 0.5)); assert_eq!(aabb.max, Vec2::new(3.0, 1.5)); - let bounding_circle = segment.bounding_circle(translation, 0.0); + let bounding_circle = segment.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 1.0_f32.hypot(0.5)); } @@ -846,12 +856,13 @@ mod tests { Vec2::new(1.0, -1.0), ]); let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = polyline.aabb_2d(translation, 0.0); + let aabb = polyline.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(1.0, 0.0)); assert_eq!(aabb.max, Vec2::new(3.0, 2.0)); - let bounding_circle = polyline.bounding_circle(translation, 0.0); + let bounding_circle = polyline.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), std::f32::consts::SQRT_2); } @@ -861,14 +872,15 @@ mod tests { let acute_triangle = Triangle2d::new(Vec2::new(0.0, 1.0), Vec2::NEG_ONE, Vec2::new(1.0, -1.0)); let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = acute_triangle.aabb_2d(translation, 0.0); + let aabb = acute_triangle.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(1.0, 0.0)); assert_eq!(aabb.max, Vec2::new(3.0, 2.0)); // For acute triangles, the center is the circumcenter let (Circle { radius }, circumcenter) = acute_triangle.circumcircle(); - let bounding_circle = acute_triangle.bounding_circle(translation, 0.0); + let bounding_circle = acute_triangle.bounding_circle(isometry); assert_eq!(bounding_circle.center, circumcenter + translation); assert_eq!(bounding_circle.radius(), radius); } @@ -881,13 +893,14 @@ mod tests { Vec2::new(10.0, -1.0), ); let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = obtuse_triangle.aabb_2d(translation, 0.0); + let aabb = obtuse_triangle.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(-8.0, 0.0)); assert_eq!(aabb.max, Vec2::new(12.0, 2.0)); // For obtuse and right triangles, the center is the midpoint of the longest side (diameter of bounding circle) - let bounding_circle = obtuse_triangle.bounding_circle(translation, 0.0); + let bounding_circle = obtuse_triangle.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation - Vec2::Y); assert_eq!(bounding_circle.radius(), 10.0); } @@ -897,12 +910,15 @@ mod tests { let rectangle = Rectangle::new(2.0, 1.0); let translation = Vec2::new(2.0, 1.0); - let aabb = rectangle.aabb_2d(translation, std::f32::consts::FRAC_PI_4); + let aabb = rectangle.aabb_2d(Isometry2d::new( + translation, + Rot2::radians(std::f32::consts::FRAC_PI_4), + )); let expected_half_size = Vec2::splat(1.0606601); assert_eq!(aabb.min, translation - expected_half_size); assert_eq!(aabb.max, translation + expected_half_size); - let bounding_circle = rectangle.bounding_circle(translation, 0.0); + let bounding_circle = rectangle.bounding_circle(Isometry2d::from_translation(translation)); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 1.0_f32.hypot(0.5)); } @@ -916,12 +932,13 @@ mod tests { Vec2::new(1.0, -1.0), ]); let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = polygon.aabb_2d(translation, 0.0); + let aabb = polygon.aabb_2d(isometry); assert_eq!(aabb.min, Vec2::new(1.0, 0.0)); assert_eq!(aabb.max, Vec2::new(3.0, 2.0)); - let bounding_circle = polygon.bounding_circle(translation, 0.0); + let bounding_circle = polygon.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), std::f32::consts::SQRT_2); } @@ -930,12 +947,13 @@ mod tests { fn regular_polygon() { let regular_polygon = RegularPolygon::new(1.0, 5); let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = regular_polygon.aabb_2d(translation, 0.0); + let aabb = regular_polygon.aabb_2d(isometry); assert!((aabb.min - (translation - Vec2::new(0.9510565, 0.8090169))).length() < 1e-6); assert!((aabb.max - (translation + Vec2::new(0.9510565, 1.0))).length() < 1e-6); - let bounding_circle = regular_polygon.bounding_circle(translation, 0.0); + let bounding_circle = regular_polygon.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 1.0); } @@ -944,12 +962,13 @@ mod tests { fn capsule() { let capsule = Capsule2d::new(0.5, 2.0); let translation = Vec2::new(2.0, 1.0); + let isometry = Isometry2d::from_translation(translation); - let aabb = capsule.aabb_2d(translation, 0.0); + let aabb = capsule.aabb_2d(isometry); assert_eq!(aabb.min, translation - Vec2::new(0.5, 1.5)); assert_eq!(aabb.max, translation + Vec2::new(0.5, 1.5)); - let bounding_circle = capsule.bounding_circle(translation, 0.0); + let bounding_circle = capsule.bounding_circle(isometry); assert_eq!(bounding_circle.center, translation); assert_eq!(bounding_circle.radius(), 1.5); } diff --git a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs b/crates/bevy_math/src/bounding/bounded3d/extrusion.rs index f6aac82d97..b990b490f3 100644 --- a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs +++ b/crates/bevy_math/src/bounding/bounded3d/extrusion.rs @@ -7,194 +7,176 @@ use crate::primitives::{ BoxedPolygon, BoxedPolyline2d, Capsule2d, Cuboid, Cylinder, Ellipse, Extrusion, Line2d, Polygon, Polyline2d, Primitive2d, Rectangle, RegularPolygon, Segment2d, Triangle2d, }; -use crate::{Quat, Vec3}; +use crate::{Isometry2d, Isometry3d, Quat, Rot2}; use crate::{bounding::Bounded2d, primitives::Circle}; use super::{Aabb3d, Bounded3d, BoundingSphere}; impl BoundedExtrusion for Circle { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ - let segment_dir = rotation * Vec3::Z; + let segment_dir = isometry.rotation * Vec3A::Z; let top = (segment_dir * half_depth).abs(); - let e = (Vec3::ONE - segment_dir * segment_dir).max(Vec3::ZERO); - let half_size = self.radius * Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); + let e = (Vec3A::ONE - segment_dir * segment_dir).max(Vec3A::ZERO); + let half_size = self.radius * Vec3A::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); Aabb3d { - min: (translation - half_size - top).into(), - max: (translation + half_size + top).into(), + min: isometry.translation - half_size - top, + max: isometry.translation + half_size + top, } } } impl BoundedExtrusion for Ellipse { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { let Vec2 { x: a, y: b } = self.half_size; - let normal = rotation * Vec3::Z; - let conjugate_rot = rotation.conjugate(); + let normal = isometry.rotation * Vec3A::Z; + let conjugate_rot = isometry.rotation.conjugate(); - let [max_x, max_y, max_z] = Vec3::AXES.map(|axis: Vec3| { + let [max_x, max_y, max_z] = Vec3A::AXES.map(|axis| { let Some(axis) = (conjugate_rot * axis.reject_from(normal)) .xy() .try_normalize() else { - return Vec3::ZERO; + return Vec3A::ZERO; }; if axis.element_product() == 0. { - return rotation * Vec3::new(a * axis.y, b * axis.x, 0.); + return isometry.rotation * Vec3A::new(a * axis.y, b * axis.x, 0.); } let m = -axis.x / axis.y; let signum = axis.signum(); let y = signum.y * b * b / (b * b + m * m * a * a).sqrt(); let x = signum.x * a * (1. - y * y / b / b).sqrt(); - rotation * Vec3::new(x, y, 0.) + isometry.rotation * Vec3A::new(x, y, 0.) }); - let half_size = Vec3::new(max_x.x, max_y.y, max_z.z).abs() + (normal * half_depth).abs(); - Aabb3d::new(translation, half_size) + let half_size = Vec3A::new(max_x.x, max_y.y, max_z.z).abs() + (normal * half_depth).abs(); + Aabb3d::new(isometry.translation, half_size) } } impl BoundedExtrusion for Line2d { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let dir = rotation * self.direction.extend(0.); - let half_depth = (rotation * Vec3::new(0., 0., half_depth)).abs(); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let dir = isometry.rotation * Vec3A::from(self.direction.extend(0.)); + let half_depth = (isometry.rotation * Vec3A::new(0., 0., half_depth)).abs(); let max = f32::MAX / 2.; - let half_size = Vec3::new( + let half_size = Vec3A::new( if dir.x == 0. { half_depth.x } else { max }, if dir.y == 0. { half_depth.y } else { max }, if dir.z == 0. { half_depth.z } else { max }, ); - Aabb3d::new(translation, half_size) + Aabb3d::new(isometry.translation, half_size) } } impl BoundedExtrusion for Segment2d { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let half_size = rotation * self.point1().extend(0.); - let depth = rotation * Vec3::new(0., 0., half_depth); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let half_size = isometry.rotation * Vec3A::from(self.point1().extend(0.)); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); - Aabb3d::new(translation, half_size.abs() + depth.abs()) + Aabb3d::new(isometry.translation, half_size.abs() + depth.abs()) } } impl BoundedExtrusion for Polyline2d { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let aabb = Aabb3d::from_point_cloud( - translation, - rotation, - self.vertices.map(|v| v.extend(0.)).into_iter(), - ); - let depth = rotation * Vec3A::new(0., 0., half_depth); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let aabb = + Aabb3d::from_point_cloud(isometry, self.vertices.map(|v| v.extend(0.)).into_iter()); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); aabb.grow(depth.abs()) } } impl BoundedExtrusion for BoxedPolyline2d { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let aabb = Aabb3d::from_point_cloud( - translation, - rotation, - self.vertices.iter().map(|v| v.extend(0.)), - ); - let depth = rotation * Vec3A::new(0., 0., half_depth); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let aabb = Aabb3d::from_point_cloud(isometry, self.vertices.iter().map(|v| v.extend(0.))); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); aabb.grow(depth.abs()) } } impl BoundedExtrusion for Triangle2d { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let aabb = Aabb3d::from_point_cloud( - translation, - rotation, - self.vertices.iter().map(|v| v.extend(0.)), - ); - let depth = rotation * Vec3A::new(0., 0., half_depth); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let aabb = Aabb3d::from_point_cloud(isometry, self.vertices.iter().map(|v| v.extend(0.))); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); aabb.grow(depth.abs()) } } impl BoundedExtrusion for Rectangle { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { Cuboid { half_size: self.half_size.extend(half_depth), } - .aabb_3d(translation, rotation) + .aabb_3d(isometry) } } impl BoundedExtrusion for Polygon { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let aabb = Aabb3d::from_point_cloud( - translation, - rotation, - self.vertices.map(|v| v.extend(0.)).into_iter(), - ); - let depth = rotation * Vec3A::new(0., 0., half_depth); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let aabb = + Aabb3d::from_point_cloud(isometry, self.vertices.map(|v| v.extend(0.)).into_iter()); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); aabb.grow(depth.abs()) } } impl BoundedExtrusion for BoxedPolygon { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let aabb = Aabb3d::from_point_cloud( - translation, - rotation, - self.vertices.iter().map(|v| v.extend(0.)), - ); - let depth = rotation * Vec3A::new(0., 0., half_depth); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let aabb = Aabb3d::from_point_cloud(isometry, self.vertices.iter().map(|v| v.extend(0.))); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); aabb.grow(depth.abs()) } } impl BoundedExtrusion for RegularPolygon { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { let aabb = Aabb3d::from_point_cloud( - translation, - rotation, + isometry, self.vertices(0.).into_iter().map(|v| v.extend(0.)), ); - let depth = rotation * Vec3A::new(0., 0., half_depth); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); aabb.grow(depth.abs()) } } impl BoundedExtrusion for Capsule2d { - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { let aabb = Cylinder { half_height: half_depth, radius: self.radius, } - .aabb_3d(Vec3::ZERO, rotation * Quat::from_rotation_x(FRAC_PI_2)); + .aabb_3d(Isometry3d::from_rotation( + isometry.rotation * Quat::from_rotation_x(FRAC_PI_2), + )); - let up = rotation * Vec3::new(0., self.half_length, 0.); - let half_size = Into::::into(aabb.max) + up.abs(); - Aabb3d::new(translation, half_size) + let up = isometry.rotation * Vec3A::new(0., self.half_length, 0.); + let half_size = aabb.max + up.abs(); + Aabb3d::new(isometry.translation, half_size) } } impl Bounded3d for Extrusion { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { - self.base_shape - .extrusion_aabb_3d(self.half_depth, translation, rotation) + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + self.base_shape.extrusion_aabb_3d(self.half_depth, isometry) } - fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere { + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { self.base_shape - .extrusion_bounding_sphere(self.half_depth, translation, rotation) + .extrusion_bounding_sphere(self.half_depth, isometry) } } @@ -206,12 +188,12 @@ impl Bounded3d for Extrusion { /// `impl BoundedExtrusion for MyShape {}` pub trait BoundedExtrusion: Primitive2d + Bounded2d { /// Get an axis-aligned bounding box for an extrusion with this shape as a base and the given `half_depth`, transformed by the given `translation` and `rotation`. - fn extrusion_aabb_3d(&self, half_depth: f32, translation: Vec3, rotation: Quat) -> Aabb3d { - let cap_normal = rotation * Vec3::Z; - let conjugate_rot = rotation.conjugate(); + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + let cap_normal = isometry.rotation * Vec3A::Z; + let conjugate_rot = isometry.rotation.conjugate(); // The `(halfsize, offset)` for each axis - let axis_values = Vec3::AXES.map(|ax| { + let axis_values = Vec3A::AXES.map(|ax| { // This is the direction of the line of intersection of a plane with the `ax` normal and the plane containing the cap of the extrusion. let intersect_line = ax.cross(cap_normal); if intersect_line.length_squared() <= f32::EPSILON { @@ -228,24 +210,19 @@ pub trait BoundedExtrusion: Primitive2d + Bounded2d { // Calculate the `Aabb2d` of the base shape. The shape is rotated so that the line of intersection is parallel to the Y axis in the `Aabb2d` calculations. // This guarantees that the X value of the `Aabb2d` is closest to the `ax` plane - let aabb2d = self.aabb_2d(Vec2::ZERO, angle); + let aabb2d = self.aabb_2d(Isometry2d::from_rotation(Rot2::radians(angle))); (aabb2d.half_size().x * scale, aabb2d.center().x * scale) }); let offset = Vec3A::from_array(axis_values.map(|(_, offset)| offset)); let cap_size = Vec3A::from_array(axis_values.map(|(max_val, _)| max_val)).abs(); - let depth = rotation * Vec3A::new(0., 0., half_depth); + let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); - Aabb3d::new(Vec3A::from(translation) - offset, cap_size + depth.abs()) + Aabb3d::new(isometry.translation - offset, cap_size + depth.abs()) } /// Get a bounding sphere for an extrusion of the `base_shape` with the given `half_depth` with the given translation and rotation - fn extrusion_bounding_sphere( - &self, - half_depth: f32, - translation: Vec3, - rotation: Quat, - ) -> BoundingSphere { + fn extrusion_bounding_sphere(&self, half_depth: f32, isometry: Isometry3d) -> BoundingSphere { // We calculate the bounding circle of the base shape. // Since each of the extrusions bases will have the same distance from its center, // and they are just shifted along the Z-axis, the minimum bounding sphere will be the bounding sphere @@ -253,9 +230,9 @@ pub trait BoundedExtrusion: Primitive2d + Bounded2d { let BoundingCircle { center, circle: Circle { radius }, - } = self.bounding_circle(Vec2::ZERO, 0.); + } = self.bounding_circle(Isometry2d::IDENTITY); let radius = radius.hypot(half_depth); - let center = translation + rotation * center.extend(0.); + let center = isometry.translation + isometry.rotation * Vec3A::from(center.extend(0.)); BoundingSphere::new(center, radius) } @@ -273,19 +250,20 @@ mod tests { Capsule2d, Circle, Ellipse, Extrusion, Line2d, Polygon, Polyline2d, Rectangle, RegularPolygon, Segment2d, Triangle2d, }, - Dir2, + Dir2, Isometry3d, }; #[test] fn circle() { let cylinder = Extrusion::new(Circle::new(0.5), 2.0); let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = cylinder.aabb_3d(translation, Quat::IDENTITY); + let aabb = cylinder.aabb_3d(isometry); assert_eq!(aabb.center(), Vec3A::from(translation)); assert_eq!(aabb.half_size(), Vec3A::new(0.5, 0.5, 1.0)); - let bounding_sphere = cylinder.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = cylinder.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1f32.hypot(0.5)); } @@ -295,12 +273,13 @@ mod tests { let extrusion = Extrusion::new(Ellipse::new(2.0, 0.5), 4.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_euler(EulerRot::ZYX, FRAC_PI_4, FRAC_PI_4, FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.center(), Vec3A::from(translation)); assert_eq!(aabb.half_size(), Vec3A::new(2.709784, 1.3801551, 2.436141)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 8f32.sqrt()); } @@ -315,12 +294,13 @@ mod tests { ); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_y(FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(1.5857864, f32::MIN / 2., 3.5857865)); assert_eq!(aabb.max, Vec3A::new(4.4142136, f32::MAX / 2., 6.414213)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center(), translation.into()); assert_eq!(bounding_sphere.radius(), f32::MAX / 2.); } @@ -330,12 +310,13 @@ mod tests { let extrusion = Extrusion::new(Rectangle::new(2.0, 1.0), 4.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_z(std::f32::consts::FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.center(), translation.into()); assert_eq!(aabb.half_size(), Vec3A::new(1.0606602, 1.0606602, 2.)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 2.291288); } @@ -345,12 +326,13 @@ mod tests { let extrusion = Extrusion::new(Segment2d::new(Dir2::new_unchecked(Vec2::NEG_Y), 3.), 4.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_x(FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.center(), translation.into()); assert_eq!(aabb.half_size(), Vec3A::new(0., 2.4748735, 2.4748735)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 2.5); } @@ -366,12 +348,13 @@ mod tests { let extrusion = Extrusion::new(polyline, 3.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_x(FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.center(), translation.into()); assert_eq!(aabb.half_size(), Vec3A::new(1., 1.7677668, 1.7677668)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 2.0615528); } @@ -386,12 +369,13 @@ mod tests { let extrusion = Extrusion::new(triangle, 3.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_x(FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.center(), translation.into()); assert_eq!(aabb.half_size(), Vec3A::new(10., 1.7677668, 1.7677668)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!( bounding_sphere.center, Vec3A::new(3.0, 3.2928934, 4.2928934) @@ -410,12 +394,13 @@ mod tests { let extrusion = Extrusion::new(polygon, 3.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_x(FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.center(), translation.into()); assert_eq!(aabb.half_size(), Vec3A::new(1., 1.7677668, 1.7677668)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 2.0615528); } @@ -425,8 +410,9 @@ mod tests { let extrusion = Extrusion::new(RegularPolygon::new(2.0, 7), 4.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_x(FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!( aabb.center(), Vec3A::from(translation) + Vec3A::new(0., 0.0700254, 0.0700254) @@ -436,7 +422,7 @@ mod tests { Vec3A::new(1.9498558, 2.7584014, 2.7584019) ); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 8f32.sqrt()); } @@ -446,12 +432,13 @@ mod tests { let extrusion = Extrusion::new(Capsule2d::new(0.5, 2.0), 4.0); let translation = Vec3::new(3., 4., 5.); let rotation = Quat::from_rotation_x(FRAC_PI_4); + let isometry = Isometry3d::new(translation, rotation); - let aabb = extrusion.aabb_3d(translation, rotation); + let aabb = extrusion.aabb_3d(isometry); assert_eq!(aabb.center(), translation.into()); assert_eq!(aabb.half_size(), Vec3A::new(0.5, 2.4748735, 2.4748735)); - let bounding_sphere = extrusion.bounding_sphere(translation, rotation); + let bounding_sphere = extrusion.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 2.5); } diff --git a/crates/bevy_math/src/bounding/bounded3d/mod.rs b/crates/bevy_math/src/bounding/bounded3d/mod.rs index 4e67a4d408..fa92c3f256 100644 --- a/crates/bevy_math/src/bounding/bounded3d/mod.rs +++ b/crates/bevy_math/src/bounding/bounded3d/mod.rs @@ -4,7 +4,7 @@ mod primitive_impls; use glam::Mat3; use super::{BoundingVolume, IntersectsVolume}; -use crate::{Quat, Vec3, Vec3A}; +use crate::{Isometry3d, Quat, Vec3A}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; @@ -24,12 +24,12 @@ fn point_cloud_3d_center(points: impl Iterator>) -> Vec3 acc / len as f32 } -/// A trait with methods that return 3D bounded volumes for a shape +/// A trait with methods that return 3D bounding volumes for a shape. pub trait Bounded3d { - /// Get an axis-aligned bounding box for the shape with the given translation and rotation - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d; - /// Get a bounding sphere for the shape with the given translation and rotation - fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere; + /// Get an axis-aligned bounding box for the shape translated and rotated by the given isometry. + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d; + /// Get a bounding sphere for the shape translated and rotated by the given isometry. + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere; } /// A 3D axis-aligned bounding box @@ -55,19 +55,18 @@ impl Aabb3d { } /// Computes the smallest [`Aabb3d`] containing the given set of points, - /// transformed by `translation` and `rotation`. + /// transformed by the rotation and translation of the given isometry. /// /// # Panics /// /// Panics if the given set of points is empty. #[inline(always)] pub fn from_point_cloud( - translation: impl Into, - rotation: Quat, + isometry: Isometry3d, points: impl Iterator>, ) -> Aabb3d { // Transform all points by rotation - let mut iter = points.map(|point| rotation * point.into()); + let mut iter = points.map(|point| isometry.rotation * point.into()); let first = iter .next() @@ -77,10 +76,9 @@ impl Aabb3d { (point.min(prev_min), point.max(prev_max)) }); - let translation = translation.into(); Aabb3d { - min: min + translation, - max: max + translation, + min: min + isometry.translation, + max: max + isometry.translation, } } @@ -473,13 +471,12 @@ impl BoundingSphere { } /// Computes a [`BoundingSphere`] containing the given set of points, - /// transformed by `translation` and `rotation`. + /// transformed by the rotation and translation of the given isometry. /// /// The bounding sphere is not guaranteed to be the smallest possible. #[inline(always)] pub fn from_point_cloud( - translation: impl Into, - rotation: Quat, + isometry: Isometry3d, points: &[impl Copy + Into], ) -> BoundingSphere { let center = point_cloud_3d_center(points.iter().map(|v| Into::::into(*v))); @@ -493,10 +490,7 @@ impl BoundingSphere { } } - BoundingSphere::new( - rotation * center + translation.into(), - radius_squared.sqrt(), - ) + BoundingSphere::new(isometry * center, radius_squared.sqrt()) } /// Get the radius of the bounding sphere diff --git a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs index efd605a0d9..0d984657ea 100644 --- a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs @@ -1,29 +1,31 @@ //! Contains [`Bounded3d`] implementations for [geometric primitives](crate::primitives). +use glam::Vec3A; + use crate::{ bounding::{Bounded2d, BoundingCircle}, primitives::{ BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Polyline3d, Segment3d, Sphere, Torus, Triangle2d, Triangle3d, }, - Dir3, Mat3, Quat, Vec2, Vec3, + Isometry2d, Isometry3d, Mat3, Vec2, Vec3, }; use super::{Aabb3d, Bounded3d, BoundingSphere}; impl Bounded3d for Sphere { - fn aabb_3d(&self, translation: Vec3, _rotation: Quat) -> Aabb3d { - Aabb3d::new(translation, Vec3::splat(self.radius)) + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + Aabb3d::new(isometry.translation, Vec3::splat(self.radius)) } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { - BoundingSphere::new(translation, self.radius) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::new(isometry.translation, self.radius) } } impl Bounded3d for InfinitePlane3d { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { - let normal = rotation * *self.normal; + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + let normal = isometry.rotation * *self.normal; let facing_x = normal == Vec3::X || normal == Vec3::NEG_X; let facing_y = normal == Vec3::Y || normal == Vec3::NEG_Y; let facing_z = normal == Vec3::Z || normal == Vec3::NEG_Z; @@ -33,19 +35,19 @@ impl Bounded3d for InfinitePlane3d { let half_width = if facing_x { 0.0 } else { f32::MAX / 2.0 }; let half_height = if facing_y { 0.0 } else { f32::MAX / 2.0 }; let half_depth = if facing_z { 0.0 } else { f32::MAX / 2.0 }; - let half_size = Vec3::new(half_width, half_height, half_depth); + let half_size = Vec3A::new(half_width, half_height, half_depth); - Aabb3d::new(translation, half_size) + Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { - BoundingSphere::new(translation, f32::MAX / 2.0) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded3d for Line3d { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { - let direction = rotation * *self.direction; + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + let direction = isometry.rotation * *self.direction; // Dividing `f32::MAX` by 2.0 is helpful so that we can do operations // like growing or shrinking the AABB without breaking things. @@ -53,55 +55,55 @@ impl Bounded3d for Line3d { let half_width = if direction.x == 0.0 { 0.0 } else { max }; let half_height = if direction.y == 0.0 { 0.0 } else { max }; let half_depth = if direction.z == 0.0 { 0.0 } else { max }; - let half_size = Vec3::new(half_width, half_height, half_depth); + let half_size = Vec3A::new(half_width, half_height, half_depth); - Aabb3d::new(translation, half_size) + Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { - BoundingSphere::new(translation, f32::MAX / 2.0) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded3d for Segment3d { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { // Rotate the segment by `rotation` - let direction = rotation * *self.direction; + let direction = isometry.rotation * *self.direction; let half_size = (self.half_length * direction).abs(); - Aabb3d::new(translation, half_size) + Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { - BoundingSphere::new(translation, self.half_length) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::new(isometry.translation, self.half_length) } } impl Bounded3d for Polyline3d { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { - Aabb3d::from_point_cloud(translation, rotation, self.vertices.iter().copied()) + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + Aabb3d::from_point_cloud(isometry, self.vertices.iter().copied()) } - fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere { - BoundingSphere::from_point_cloud(translation, rotation, &self.vertices) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::from_point_cloud(isometry, &self.vertices) } } impl Bounded3d for BoxedPolyline3d { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { - Aabb3d::from_point_cloud(translation, rotation, self.vertices.iter().copied()) + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + Aabb3d::from_point_cloud(isometry, self.vertices.iter().copied()) } - fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere { - BoundingSphere::from_point_cloud(translation, rotation, &self.vertices) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::from_point_cloud(isometry, &self.vertices) } } impl Bounded3d for Cuboid { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { // Compute the AABB of the rotated cuboid by transforming the half-size // by an absolute rotation matrix. - let rot_mat = Mat3::from_quat(rotation); + let rot_mat = Mat3::from_quat(isometry.rotation); let abs_rot_mat = Mat3::from_cols( rot_mat.x_axis.abs(), rot_mat.y_axis.abs(), @@ -109,80 +111,77 @@ impl Bounded3d for Cuboid { ); let half_size = abs_rot_mat * self.half_size; - Aabb3d::new(translation, half_size) + Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { - BoundingSphere::new(translation, self.half_size.length()) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::new(isometry.translation, self.half_size.length()) } } impl Bounded3d for Cylinder { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ - let segment_dir = rotation * Vec3::Y; + let segment_dir = isometry.rotation * Vec3A::Y; let top = segment_dir * self.half_height; let bottom = -top; - let e = (Vec3::ONE - segment_dir * segment_dir).max(Vec3::ZERO); - let half_size = self.radius * Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); + let e = (Vec3A::ONE - segment_dir * segment_dir).max(Vec3A::ZERO); + let half_size = self.radius * Vec3A::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); Aabb3d { - min: (translation + (top - half_size).min(bottom - half_size)).into(), - max: (translation + (top + half_size).max(bottom + half_size)).into(), + min: isometry.translation + (top - half_size).min(bottom - half_size), + max: isometry.translation + (top + half_size).max(bottom + half_size), } } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { let radius = self.radius.hypot(self.half_height); - BoundingSphere::new(translation, radius) + BoundingSphere::new(isometry.translation, radius) } } impl Bounded3d for Capsule3d { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { // Get the line segment between the hemispheres of the rotated capsule - let segment = Segment3d { - // Multiplying a normalized vector (Vec3::Y) with a rotation returns a normalized vector. - direction: rotation * Dir3::Y, - half_length: self.half_length, - }; - let (a, b) = (segment.point1(), segment.point2()); + let segment_dir = isometry.rotation * Vec3A::Y; + let top = segment_dir * self.half_length; + let bottom = -top; // Expand the line segment by the capsule radius to get the capsule half-extents - let min = a.min(b) - Vec3::splat(self.radius); - let max = a.max(b) + Vec3::splat(self.radius); + let min = bottom.min(top) - Vec3A::splat(self.radius); + let max = bottom.max(top) + Vec3A::splat(self.radius); Aabb3d { - min: (min + translation).into(), - max: (max + translation).into(), + min: min + isometry.translation, + max: max + isometry.translation, } } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { - BoundingSphere::new(translation, self.radius + self.half_length) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::new(isometry.translation, self.radius + self.half_length) } } impl Bounded3d for Cone { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ - let segment_dir = rotation * Vec3::Y; + let segment_dir = isometry.rotation * Vec3A::Y; let top = segment_dir * 0.5 * self.height; let bottom = -top; - let e = (Vec3::ONE - segment_dir * segment_dir).max(Vec3::ZERO); - let half_extents = Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); + let e = (Vec3A::ONE - segment_dir * segment_dir).max(Vec3A::ZERO); + let half_extents = Vec3A::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); Aabb3d { - min: (translation + top.min(bottom - self.radius * half_extents)).into(), - max: (translation + top.max(bottom + self.radius * half_extents)).into(), + min: isometry.translation + top.min(bottom - self.radius * half_extents), + max: isometry.translation + top.max(bottom + self.radius * half_extents), } } - fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere { + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { // Get the triangular cross-section of the cone. let half_height = 0.5 * self.height; let triangle = Triangle2d::new( @@ -193,36 +192,37 @@ impl Bounded3d for Cone { // Because of circular symmetry, we can use the bounding circle of the triangle // for the bounding sphere of the cone. - let BoundingCircle { circle, center } = triangle.bounding_circle(Vec2::ZERO, 0.0); + let BoundingCircle { circle, center } = triangle.bounding_circle(Isometry2d::IDENTITY); - BoundingSphere::new(rotation * center.extend(0.0) + translation, circle.radius) + BoundingSphere::new( + isometry.rotation * Vec3A::from(center.extend(0.0)) + isometry.translation, + circle.radius, + ) } } impl Bounded3d for ConicalFrustum { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ - let segment_dir = rotation * Vec3::Y; + let segment_dir = isometry.rotation * Vec3A::Y; let top = segment_dir * 0.5 * self.height; let bottom = -top; - let e = (Vec3::ONE - segment_dir * segment_dir).max(Vec3::ZERO); - let half_extents = Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); + let e = (Vec3A::ONE - segment_dir * segment_dir).max(Vec3A::ZERO); + let half_extents = Vec3A::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); Aabb3d { - min: (translation + min: isometry.translation + (top - self.radius_top * half_extents) - .min(bottom - self.radius_bottom * half_extents)) - .into(), - max: (translation + .min(bottom - self.radius_bottom * half_extents), + max: isometry.translation + (top + self.radius_top * half_extents) - .max(bottom + self.radius_bottom * half_extents)) - .into(), + .max(bottom + self.radius_bottom * half_extents), } } - fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere { + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { let half_height = 0.5 * self.height; // To compute the bounding sphere, we'll get the center and radius of the circumcircle @@ -277,42 +277,45 @@ impl Bounded3d for ConicalFrustum { (circumcenter, a.distance(circumcenter)) }; - BoundingSphere::new(translation + rotation * center.extend(0.0), radius) + BoundingSphere::new( + isometry.translation + isometry.rotation * Vec3A::from(center.extend(0.0)), + radius, + ) } } impl Bounded3d for Torus { - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { // Compute the AABB of a flat disc with the major radius of the torus. // Reference: http://iquilezles.org/articles/diskbbox/ - let normal = rotation * Vec3::Y; - let e = (Vec3::ONE - normal * normal).max(Vec3::ZERO); - let disc_half_size = self.major_radius * Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); + let normal = isometry.rotation * Vec3A::Y; + let e = (Vec3A::ONE - normal * normal).max(Vec3A::ZERO); + let disc_half_size = self.major_radius * Vec3A::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt()); // Expand the disc by the minor radius to get the torus half-size - let half_size = disc_half_size + Vec3::splat(self.minor_radius); + let half_size = disc_half_size + Vec3A::splat(self.minor_radius); - Aabb3d::new(translation, half_size) + Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { - BoundingSphere::new(translation, self.outer_radius()) + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + BoundingSphere::new(isometry.translation, self.outer_radius()) } } impl Bounded3d for Triangle3d { /// Get the bounding box of the triangle. - fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { let [a, b, c] = self.vertices; - let a = rotation * a; - let b = rotation * b; - let c = rotation * c; + let a = isometry.rotation * a; + let b = isometry.rotation * b; + let c = isometry.rotation * c; - let min = a.min(b).min(c); - let max = a.max(b).max(c); + let min = Vec3A::from(a.min(b).min(c)); + let max = Vec3A::from(a.max(b).max(c)); - let bounding_center = (max + min) / 2.0 + translation; + let bounding_center = (max + min) / 2.0 + isometry.translation; let half_extents = (max - min) / 2.0; Aabb3d::new(bounding_center, half_extents) @@ -323,25 +326,26 @@ impl Bounded3d for Triangle3d { /// The [`Triangle3d`] implements the minimal bounding sphere calculation. For acute triangles, the circumcenter is used as /// the center of the sphere. For the others, the bounding sphere is the minimal sphere /// that contains the largest side of the triangle. - fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere { + fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { if self.is_degenerate() || self.is_obtuse() { let (p1, p2) = self.largest_side(); + let (p1, p2) = (Vec3A::from(p1), Vec3A::from(p2)); let mid_point = (p1 + p2) / 2.0; let radius = mid_point.distance(p1); - BoundingSphere::new(mid_point + translation, radius) + BoundingSphere::new(mid_point + isometry.translation, radius) } else { let [a, _, _] = self.vertices; let circumcenter = self.circumcenter(); let radius = circumcenter.distance(a); - BoundingSphere::new(circumcenter + translation, radius) + BoundingSphere::new(Vec3A::from(circumcenter) + isometry.translation, radius) } } } #[cfg(test)] mod tests { - use crate::bounding::BoundingVolume; + use crate::{bounding::BoundingVolume, Isometry3d}; use glam::{Quat, Vec3, Vec3A}; use crate::{ @@ -357,12 +361,13 @@ mod tests { fn sphere() { let sphere = Sphere { radius: 1.0 }; let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = sphere.aabb_3d(translation, Quat::IDENTITY); + let aabb = sphere.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0)); assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0)); - let bounding_sphere = sphere.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = sphere.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.0); } @@ -370,25 +375,25 @@ mod tests { #[test] fn plane() { let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb1 = InfinitePlane3d::new(Vec3::X).aabb_3d(translation, Quat::IDENTITY); + let aabb1 = InfinitePlane3d::new(Vec3::X).aabb_3d(isometry); assert_eq!(aabb1.min, Vec3A::new(2.0, -f32::MAX / 2.0, -f32::MAX / 2.0)); assert_eq!(aabb1.max, Vec3A::new(2.0, f32::MAX / 2.0, f32::MAX / 2.0)); - let aabb2 = InfinitePlane3d::new(Vec3::Y).aabb_3d(translation, Quat::IDENTITY); + let aabb2 = InfinitePlane3d::new(Vec3::Y).aabb_3d(isometry); assert_eq!(aabb2.min, Vec3A::new(-f32::MAX / 2.0, 1.0, -f32::MAX / 2.0)); assert_eq!(aabb2.max, Vec3A::new(f32::MAX / 2.0, 1.0, f32::MAX / 2.0)); - let aabb3 = InfinitePlane3d::new(Vec3::Z).aabb_3d(translation, Quat::IDENTITY); + let aabb3 = InfinitePlane3d::new(Vec3::Z).aabb_3d(isometry); assert_eq!(aabb3.min, Vec3A::new(-f32::MAX / 2.0, -f32::MAX / 2.0, 0.0)); assert_eq!(aabb3.max, Vec3A::new(f32::MAX / 2.0, f32::MAX / 2.0, 0.0)); - let aabb4 = InfinitePlane3d::new(Vec3::ONE).aabb_3d(translation, Quat::IDENTITY); + let aabb4 = InfinitePlane3d::new(Vec3::ONE).aabb_3d(isometry); assert_eq!(aabb4.min, Vec3A::splat(-f32::MAX / 2.0)); assert_eq!(aabb4.max, Vec3A::splat(f32::MAX / 2.0)); - let bounding_sphere = - InfinitePlane3d::new(Vec3::Y).bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = InfinitePlane3d::new(Vec3::Y).bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), f32::MAX / 2.0); } @@ -396,28 +401,28 @@ mod tests { #[test] fn line() { let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb1 = Line3d { direction: Dir3::Y }.aabb_3d(translation, Quat::IDENTITY); + let aabb1 = Line3d { direction: Dir3::Y }.aabb_3d(isometry); assert_eq!(aabb1.min, Vec3A::new(2.0, -f32::MAX / 2.0, 0.0)); assert_eq!(aabb1.max, Vec3A::new(2.0, f32::MAX / 2.0, 0.0)); - let aabb2 = Line3d { direction: Dir3::X }.aabb_3d(translation, Quat::IDENTITY); + let aabb2 = Line3d { direction: Dir3::X }.aabb_3d(isometry); assert_eq!(aabb2.min, Vec3A::new(-f32::MAX / 2.0, 1.0, 0.0)); assert_eq!(aabb2.max, Vec3A::new(f32::MAX / 2.0, 1.0, 0.0)); - let aabb3 = Line3d { direction: Dir3::Z }.aabb_3d(translation, Quat::IDENTITY); + let aabb3 = Line3d { direction: Dir3::Z }.aabb_3d(isometry); assert_eq!(aabb3.min, Vec3A::new(2.0, 1.0, -f32::MAX / 2.0)); assert_eq!(aabb3.max, Vec3A::new(2.0, 1.0, f32::MAX / 2.0)); let aabb4 = Line3d { direction: Dir3::from_xyz(1.0, 1.0, 1.0).unwrap(), } - .aabb_3d(translation, Quat::IDENTITY); + .aabb_3d(isometry); assert_eq!(aabb4.min, Vec3A::splat(-f32::MAX / 2.0)); assert_eq!(aabb4.max, Vec3A::splat(f32::MAX / 2.0)); - let bounding_sphere = - Line3d { direction: Dir3::Y }.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = Line3d { direction: Dir3::Y }.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), f32::MAX / 2.0); } @@ -425,14 +430,16 @@ mod tests { #[test] fn segment() { let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); + let segment = Segment3d::from_points(Vec3::new(-1.0, -0.5, 0.0), Vec3::new(1.0, 0.5, 0.0)).0; - let aabb = segment.aabb_3d(translation, Quat::IDENTITY); + let aabb = segment.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(1.0, 0.5, 0.0)); assert_eq!(aabb.max, Vec3A::new(3.0, 1.5, 0.0)); - let bounding_sphere = segment.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = segment.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(0.5)); } @@ -446,12 +453,13 @@ mod tests { Vec3::new(1.0, -1.0, -1.0), ]); let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = polyline.aabb_3d(translation, Quat::IDENTITY); + let aabb = polyline.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0)); assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0)); - let bounding_sphere = polyline.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = polyline.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(1.0).hypot(1.0)); } @@ -461,15 +469,15 @@ mod tests { let cuboid = Cuboid::new(2.0, 1.0, 1.0); let translation = Vec3::new(2.0, 1.0, 0.0); - let aabb = cuboid.aabb_3d( + let aabb = cuboid.aabb_3d(Isometry3d::new( translation, Quat::from_rotation_z(std::f32::consts::FRAC_PI_4), - ); + )); let expected_half_size = Vec3A::new(1.0606601, 1.0606601, 0.5); assert_eq!(aabb.min, Vec3A::from(translation) - expected_half_size); assert_eq!(aabb.max, Vec3A::from(translation) + expected_half_size); - let bounding_sphere = cuboid.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = cuboid.bounding_sphere(Isometry3d::from_translation(translation)); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(0.5).hypot(0.5)); } @@ -478,8 +486,9 @@ mod tests { fn cylinder() { let cylinder = Cylinder::new(0.5, 2.0); let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = cylinder.aabb_3d(translation, Quat::IDENTITY); + let aabb = cylinder.aabb_3d(isometry); assert_eq!( aabb.min, Vec3A::from(translation) - Vec3A::new(0.5, 1.0, 0.5) @@ -489,7 +498,7 @@ mod tests { Vec3A::from(translation) + Vec3A::new(0.5, 1.0, 0.5) ); - let bounding_sphere = cylinder.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = cylinder.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(0.5)); } @@ -498,8 +507,9 @@ mod tests { fn capsule() { let capsule = Capsule3d::new(0.5, 2.0); let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = capsule.aabb_3d(translation, Quat::IDENTITY); + let aabb = capsule.aabb_3d(isometry); assert_eq!( aabb.min, Vec3A::from(translation) - Vec3A::new(0.5, 1.5, 0.5) @@ -509,7 +519,7 @@ mod tests { Vec3A::from(translation) + Vec3A::new(0.5, 1.5, 0.5) ); - let bounding_sphere = capsule.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = capsule.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.5); } @@ -521,12 +531,13 @@ mod tests { height: 2.0, }; let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = cone.aabb_3d(translation, Quat::IDENTITY); + let aabb = cone.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0)); assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0)); - let bounding_sphere = cone.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = cone.bounding_sphere(isometry); assert_eq!( bounding_sphere.center, Vec3A::from(translation) + Vec3A::NEG_Y * 0.25 @@ -542,12 +553,13 @@ mod tests { height: 2.0, }; let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = conical_frustum.aabb_3d(translation, Quat::IDENTITY); + let aabb = conical_frustum.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0)); assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0)); - let bounding_sphere = conical_frustum.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = conical_frustum.bounding_sphere(isometry); assert_eq!( bounding_sphere.center, Vec3A::from(translation) + Vec3A::NEG_Y * 0.1875 @@ -563,14 +575,15 @@ mod tests { height: 1.0, }; let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = conical_frustum.aabb_3d(translation, Quat::IDENTITY); + let aabb = conical_frustum.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(-3.0, 0.5, -5.0)); assert_eq!(aabb.max, Vec3A::new(7.0, 1.5, 5.0)); // For wide conical frusta like this, the circumcenter can be outside the frustum, // so the center and radius should be clamped to the longest side. - let bounding_sphere = conical_frustum.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = conical_frustum.bounding_sphere(isometry); assert_eq!( bounding_sphere.center, Vec3A::from(translation) + Vec3A::NEG_Y * 0.5 @@ -585,12 +598,13 @@ mod tests { major_radius: 1.0, }; let translation = Vec3::new(2.0, 1.0, 0.0); + let isometry = Isometry3d::from_translation(translation); - let aabb = torus.aabb_3d(translation, Quat::IDENTITY); + let aabb = torus.aabb_3d(isometry); assert_eq!(aabb.min, Vec3A::new(0.5, 0.5, -1.5)); assert_eq!(aabb.max, Vec3A::new(3.5, 1.5, 1.5)); - let bounding_sphere = torus.bounding_sphere(translation, Quat::IDENTITY); + let bounding_sphere = torus.bounding_sphere(isometry); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.5); } @@ -599,7 +613,7 @@ mod tests { fn triangle3d() { let zero_degenerate_triangle = Triangle3d::new(Vec3::ZERO, Vec3::ZERO, Vec3::ZERO); - let br = zero_degenerate_triangle.aabb_3d(Vec3::ZERO, Quat::IDENTITY); + let br = zero_degenerate_triangle.aabb_3d(Isometry3d::IDENTITY); assert_eq!( br.center(), Vec3::ZERO.into(), @@ -611,7 +625,7 @@ mod tests { "incorrect bounding box half extents" ); - let bs = zero_degenerate_triangle.bounding_sphere(Vec3::ZERO, Quat::IDENTITY); + let bs = zero_degenerate_triangle.bounding_sphere(Isometry3d::IDENTITY); assert_eq!( bs.center, Vec3::ZERO.into(), @@ -620,14 +634,14 @@ mod tests { assert_eq!(bs.sphere.radius, 0.0, "incorrect bounding sphere radius"); let dup_degenerate_triangle = Triangle3d::new(Vec3::ZERO, Vec3::X, Vec3::X); - let bs = dup_degenerate_triangle.bounding_sphere(Vec3::ZERO, Quat::IDENTITY); + let bs = dup_degenerate_triangle.bounding_sphere(Isometry3d::IDENTITY); assert_eq!( bs.center, Vec3::new(0.5, 0.0, 0.0).into(), "incorrect bounding sphere center" ); assert_eq!(bs.sphere.radius, 0.5, "incorrect bounding sphere radius"); - let br = dup_degenerate_triangle.aabb_3d(Vec3::ZERO, Quat::IDENTITY); + let br = dup_degenerate_triangle.aabb_3d(Isometry3d::IDENTITY); assert_eq!( br.center(), Vec3::new(0.5, 0.0, 0.0).into(), @@ -640,14 +654,14 @@ mod tests { ); let collinear_degenerate_triangle = Triangle3d::new(Vec3::NEG_X, Vec3::ZERO, Vec3::X); - let bs = collinear_degenerate_triangle.bounding_sphere(Vec3::ZERO, Quat::IDENTITY); + let bs = collinear_degenerate_triangle.bounding_sphere(Isometry3d::IDENTITY); assert_eq!( bs.center, Vec3::ZERO.into(), "incorrect bounding sphere center" ); assert_eq!(bs.sphere.radius, 1.0, "incorrect bounding sphere radius"); - let br = collinear_degenerate_triangle.aabb_3d(Vec3::ZERO, Quat::IDENTITY); + let br = collinear_degenerate_triangle.aabb_3d(Isometry3d::IDENTITY); assert_eq!( br.center(), Vec3::ZERO.into(), diff --git a/examples/2d/bounding_2d.rs b/examples/2d/bounding_2d.rs index 0807a4890c..2025b00741 100644 --- a/examples/2d/bounding_2d.rs +++ b/examples/2d/bounding_2d.rs @@ -1,6 +1,10 @@ //! This example demonstrates bounding volume intersections. -use bevy::{color::palettes::css::*, math::bounding::*, prelude::*}; +use bevy::{ + color::palettes::css::*, + math::{bounding::*, Isometry2d}, + prelude::*, +}; fn main() { App::new() @@ -146,26 +150,27 @@ fn update_volumes( for (entity, desired_volume, shape, transform) in query.iter() { let translation = transform.translation.xy(); let rotation = transform.rotation.to_euler(EulerRot::YXZ).2; + let isometry = Isometry2d::new(translation, Rot2::radians(rotation)); match desired_volume { DesiredVolume::Aabb => { let aabb = match shape { - Shape::Rectangle(r) => r.aabb_2d(translation, rotation), - Shape::Circle(c) => c.aabb_2d(translation, rotation), - Shape::Triangle(t) => t.aabb_2d(translation, rotation), - Shape::Line(l) => l.aabb_2d(translation, rotation), - Shape::Capsule(c) => c.aabb_2d(translation, rotation), - Shape::Polygon(p) => p.aabb_2d(translation, rotation), + Shape::Rectangle(r) => r.aabb_2d(isometry), + Shape::Circle(c) => c.aabb_2d(isometry), + Shape::Triangle(t) => t.aabb_2d(isometry), + Shape::Line(l) => l.aabb_2d(isometry), + Shape::Capsule(c) => c.aabb_2d(isometry), + Shape::Polygon(p) => p.aabb_2d(isometry), }; commands.entity(entity).insert(CurrentVolume::Aabb(aabb)); } DesiredVolume::Circle => { let circle = match shape { - Shape::Rectangle(r) => r.bounding_circle(translation, rotation), - Shape::Circle(c) => c.bounding_circle(translation, rotation), - Shape::Triangle(t) => t.bounding_circle(translation, rotation), - Shape::Line(l) => l.bounding_circle(translation, rotation), - Shape::Capsule(c) => c.bounding_circle(translation, rotation), - Shape::Polygon(p) => p.bounding_circle(translation, rotation), + Shape::Rectangle(r) => r.bounding_circle(isometry), + Shape::Circle(c) => c.bounding_circle(isometry), + Shape::Triangle(t) => t.bounding_circle(isometry), + Shape::Line(l) => l.bounding_circle(isometry), + Shape::Capsule(c) => c.bounding_circle(isometry), + Shape::Polygon(p) => p.bounding_circle(isometry), }; commands .entity(entity) diff --git a/examples/2d/mesh2d_arcs.rs b/examples/2d/mesh2d_arcs.rs index f15421d449..4b22417deb 100644 --- a/examples/2d/mesh2d_arcs.rs +++ b/examples/2d/mesh2d_arcs.rs @@ -5,7 +5,10 @@ use std::f32::consts::FRAC_PI_2; use bevy::{ color::palettes::css::{BLUE, DARK_SLATE_GREY, RED}, - math::bounding::{Bounded2d, BoundingVolume}, + math::{ + bounding::{Bounded2d, BoundingVolume}, + Isometry2d, + }, prelude::*, render::mesh::{CircularMeshUvMode, CircularSectorMeshBuilder, CircularSegmentMeshBuilder}, sprite::MaterialMesh2dBundle, @@ -114,11 +117,12 @@ fn draw_bounds( let (_, rotation, translation) = transform.to_scale_rotation_translation(); let translation = translation.truncate(); let rotation = rotation.to_euler(EulerRot::XYZ).2; + let isometry = Isometry2d::new(translation, Rot2::radians(rotation)); - let aabb = shape.0.aabb_2d(translation, rotation); + let aabb = shape.0.aabb_2d(isometry); gizmos.rect_2d(aabb.center(), 0.0, aabb.half_size() * 2.0, RED); - let bounding_circle = shape.0.bounding_circle(translation, rotation); + let bounding_circle = shape.0.bounding_circle(isometry); gizmos.circle_2d(bounding_circle.center, bounding_circle.radius(), BLUE); } } diff --git a/examples/math/custom_primitives.rs b/examples/math/custom_primitives.rs index 9196714da9..6dcb896ff1 100644 --- a/examples/math/custom_primitives.rs +++ b/examples/math/custom_primitives.rs @@ -6,8 +6,11 @@ use std::f32::consts::{PI, SQRT_2}; use bevy::{ color::palettes::css::{RED, WHITE}, input::common_conditions::input_just_pressed, - math::bounding::{ - Aabb2d, Bounded2d, Bounded3d, BoundedExtrusion, BoundingCircle, BoundingVolume, + math::{ + bounding::{ + Aabb2d, Bounded2d, Bounded3d, BoundedExtrusion, BoundingCircle, BoundingVolume, + }, + Isometry2d, }, prelude::*, render::{ @@ -199,18 +202,20 @@ fn bounding_shapes_2d( for transform in shapes.iter() { // Get the rotation angle from the 3D rotation. let rotation = transform.rotation.to_scaled_axis().z; + let rotation = Rot2::radians(rotation); + let isometry = Isometry2d::new(transform.translation.xy(), rotation); match bounding_shape.get() { BoundingShape::None => (), BoundingShape::BoundingBox => { // Get the AABB of the primitive with the rotation and translation of the mesh. - let aabb = HEART.aabb_2d(transform.translation.xy(), rotation); + let aabb = HEART.aabb_2d(isometry); gizmos.rect_2d(aabb.center(), 0., aabb.half_size() * 2., WHITE); } BoundingShape::BoundingSphere => { // Get the bounding sphere of the primitive with the rotation and translation of the mesh. - let bounding_circle = HEART.bounding_circle(transform.translation.xy(), rotation); + let bounding_circle = HEART.bounding_circle(isometry); gizmos .circle_2d(bounding_circle.center(), bounding_circle.radius(), WHITE) @@ -240,7 +245,7 @@ fn bounding_shapes_3d( BoundingShape::None => (), BoundingShape::BoundingBox => { // Get the AABB of the extrusion with the rotation and translation of the mesh. - let aabb = EXTRUSION.aabb_3d(transform.translation, transform.rotation); + let aabb = EXTRUSION.aabb_3d(transform.to_isometry()); gizmos.primitive_3d( &Cuboid::from_size(Vec3::from(aabb.half_size()) * 2.), @@ -251,8 +256,7 @@ fn bounding_shapes_3d( } BoundingShape::BoundingSphere => { // Get the bounding sphere of the extrusion with the rotation and translation of the mesh. - let bounding_sphere = - EXTRUSION.bounding_sphere(transform.translation, transform.rotation); + let bounding_sphere = EXTRUSION.bounding_sphere(transform.to_isometry()); gizmos.sphere( bounding_sphere.center().into(), @@ -338,29 +342,28 @@ impl Measured2d for Heart { // The `Bounded2d` or `Bounded3d` traits are used to compute the Axis Aligned Bounding Boxes or bounding circles / spheres for primitives. impl Bounded2d for Heart { - fn aabb_2d(&self, translation: Vec2, rotation: impl Into) -> Aabb2d { - let rotation = rotation.into(); + fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { // The center of the circle at the center of the right wing of the heart - let circle_center = rotation * Vec2::new(self.radius, 0.0); + let circle_center = isometry.rotation * Vec2::new(self.radius, 0.0); // The maximum X and Y positions of the two circles of the wings of the heart. let max_circle = circle_center.abs() + Vec2::splat(self.radius); // Since the two circles of the heart are mirrored around the origin, the minimum position is the negative of the maximum. let min_circle = -max_circle; // The position of the tip at the bottom of the heart - let tip_position = rotation * Vec2::new(0.0, -self.radius * (1. + SQRT_2)); + let tip_position = isometry.rotation * Vec2::new(0.0, -self.radius * (1. + SQRT_2)); Aabb2d { - min: translation + min_circle.min(tip_position), - max: translation + max_circle.max(tip_position), + min: isometry.translation + min_circle.min(tip_position), + max: isometry.translation + max_circle.max(tip_position), } } - fn bounding_circle(&self, translation: Vec2, rotation: impl Into) -> BoundingCircle { + fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { // The bounding circle of the heart is not at its origin. This `offset` is the offset between the center of the bounding circle and its translation. let offset = self.radius / 2f32.powf(1.5); // The center of the bounding circle - let center = translation + rotation.into() * Vec2::new(0.0, -offset); + let center = isometry * Vec2::new(0.0, -offset); // The radius of the bounding circle let radius = self.radius * (1.0 + 2f32.sqrt()) - offset;