From 21b78b5990967f8b9888479269c8127557d8b48e Mon Sep 17 00:00:00 2001 From: Joona Aalto Date: Tue, 8 Oct 2024 19:09:28 +0300 Subject: [PATCH] Implement `From` translation and rotation for isometries (#15733) # Objective Several of our APIs (namely gizmos and bounding) use isometries on current Bevy main. This is nicer than separate properties in a lot of cases, but users have still expressed usability concerns. One problem is that in a lot of cases, you only care about e.g. translation, so you end up with this: ```rust gizmos.cross_2d( Isometry2d::from_translation(Vec2::new(-160.0, 120.0)), 12.0, FUCHSIA, ); ``` The isometry adds quite a lot of length and verbosity, and isn't really that relevant since only the translation is important here. It would be nice if you could use the translation directly, and only supply an isometry if both translation and rotation are needed. This would make the following possible: ```rust gizmos.cross_2d(Vec2::new(-160.0, 120.0), 12.0, FUCHSIA); ``` removing a lot of verbosity. ## Solution Implement `From` and `From` for `Isometry2d`, and `From`, `From`, and `From` for `Isometry3d`. These are lossless conversions that fit the semantics of `From`. This makes the proposed API possible! The methods must now simply take an `impl Into`, and this works: ```rust gizmos.cross_2d(Vec2::new(-160.0, 120.0), 12.0, FUCHSIA); ``` --- crates/bevy_gizmos/src/arcs.rs | 8 +- crates/bevy_gizmos/src/circles.rs | 20 +-- crates/bevy_gizmos/src/cross.rs | 16 +- crates/bevy_gizmos/src/gizmos.rs | 11 +- crates/bevy_gizmos/src/grid.rs | 11 +- crates/bevy_gizmos/src/light.rs | 22 +-- crates/bevy_gizmos/src/primitives/dim2.rs | 68 +++++--- crates/bevy_gizmos/src/primitives/dim3.rs | 54 ++++--- crates/bevy_gizmos/src/rounded_box.rs | 11 +- .../bevy_math/src/bounding/bounded2d/mod.rs | 12 +- .../src/bounding/bounded2d/primitive_impls.rs | 110 ++++++++----- .../src/bounding/bounded3d/extrusion.rs | 62 ++++--- .../bevy_math/src/bounding/bounded3d/mod.rs | 12 +- .../src/bounding/bounded3d/primitive_impls.rs | 153 ++++++++++-------- crates/bevy_math/src/isometry.rs | 35 ++++ crates/bevy_math/src/primitives/dim3.rs | 17 +- examples/2d/2d_viewport_to_world.rs | 4 +- examples/2d/bounding_2d.rs | 30 +--- examples/2d/mesh2d_arcs.rs | 12 +- examples/ecs/observers.rs | 3 +- examples/gizmos/2d_gizmos.rs | 12 +- examples/gizmos/3d_gizmos.rs | 41 ++--- examples/math/cubic_splines.rs | 20 +-- examples/math/custom_primitives.rs | 30 ++-- 24 files changed, 430 insertions(+), 344 deletions(-) diff --git a/crates/bevy_gizmos/src/arcs.rs b/crates/bevy_gizmos/src/arcs.rs index cc4934398db06..ce57450f2d5d6 100644 --- a/crates/bevy_gizmos/src/arcs.rs +++ b/crates/bevy_gizmos/src/arcs.rs @@ -50,14 +50,14 @@ where #[inline] pub fn arc_2d( &mut self, - isometry: Isometry2d, + isometry: impl Into, arc_angle: f32, radius: f32, color: impl Into, ) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> { Arc2dBuilder { gizmos: self, - isometry, + isometry: isometry.into(), arc_angle, radius, color: color.into(), @@ -176,13 +176,13 @@ where &mut self, angle: f32, radius: f32, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> { Arc3dBuilder { gizmos: self, start_vertex: Vec3::X, - isometry, + isometry: isometry.into(), angle, radius, color: color.into(), diff --git a/crates/bevy_gizmos/src/circles.rs b/crates/bevy_gizmos/src/circles.rs index 7b0a5e82890a8..6cd11e5381822 100644 --- a/crates/bevy_gizmos/src/circles.rs +++ b/crates/bevy_gizmos/src/circles.rs @@ -51,13 +51,13 @@ where #[inline] pub fn ellipse( &mut self, - isometry: Isometry3d, + isometry: impl Into, half_size: Vec2, color: impl Into, ) -> EllipseBuilder<'_, 'w, 's, Config, Clear> { EllipseBuilder { gizmos: self, - isometry, + isometry: isometry.into(), half_size, color: color.into(), resolution: DEFAULT_CIRCLE_RESOLUTION, @@ -92,13 +92,13 @@ where #[inline] pub fn ellipse_2d( &mut self, - isometry: Isometry2d, + isometry: impl Into, half_size: Vec2, color: impl Into, ) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> { Ellipse2dBuilder { gizmos: self, - isometry, + isometry: isometry.into(), half_size, color: color.into(), resolution: DEFAULT_CIRCLE_RESOLUTION, @@ -131,13 +131,13 @@ where #[inline] pub fn circle( &mut self, - isometry: Isometry3d, + isometry: impl Into, radius: f32, color: impl Into, ) -> EllipseBuilder<'_, 'w, 's, Config, Clear> { EllipseBuilder { gizmos: self, - isometry, + isometry: isometry.into(), half_size: Vec2::splat(radius), color: color.into(), resolution: DEFAULT_CIRCLE_RESOLUTION, @@ -172,13 +172,13 @@ where #[inline] pub fn circle_2d( &mut self, - isometry: Isometry2d, + isometry: impl Into, radius: f32, color: impl Into, ) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> { Ellipse2dBuilder { gizmos: self, - isometry, + isometry: isometry.into(), half_size: Vec2::splat(radius), color: color.into(), resolution: DEFAULT_CIRCLE_RESOLUTION, @@ -214,14 +214,14 @@ where #[inline] pub fn sphere( &mut self, - isometry: Isometry3d, + isometry: impl Into, radius: f32, color: impl Into, ) -> SphereBuilder<'_, 'w, 's, Config, Clear> { SphereBuilder { gizmos: self, radius, - isometry, + isometry: isometry.into(), color: color.into(), resolution: DEFAULT_CIRCLE_RESOLUTION, } diff --git a/crates/bevy_gizmos/src/cross.rs b/crates/bevy_gizmos/src/cross.rs index 710aa71e9a982..d08e2ddb8dcff 100644 --- a/crates/bevy_gizmos/src/cross.rs +++ b/crates/bevy_gizmos/src/cross.rs @@ -30,7 +30,13 @@ where /// } /// # bevy_ecs::system::assert_is_system(system); /// ``` - pub fn cross(&mut self, isometry: Isometry3d, half_size: f32, color: impl Into) { + pub fn cross( + &mut self, + isometry: impl Into, + half_size: f32, + color: impl Into, + ) { + let isometry = isometry.into(); let color: Color = color.into(); [Vec3::X, Vec3::Y, Vec3::Z] .map(|axis| axis * half_size) @@ -59,7 +65,13 @@ where /// } /// # bevy_ecs::system::assert_is_system(system); /// ``` - pub fn cross_2d(&mut self, isometry: Isometry2d, half_size: f32, color: impl Into) { + pub fn cross_2d( + &mut self, + isometry: impl Into, + half_size: f32, + color: impl Into, + ) { + let isometry = isometry.into(); let color: Color = color.into(); [Vec2::X, Vec2::Y] .map(|axis| axis * half_size) diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 86df15333949f..508cd5519c2bd 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -482,10 +482,11 @@ where /// # bevy_ecs::system::assert_is_system(system); /// ``` #[inline] - pub fn rect(&mut self, isometry: Isometry3d, size: Vec2, color: impl Into) { + pub fn rect(&mut self, isometry: impl Into, size: Vec2, color: impl Into) { if !self.enabled { return; } + let isometry = isometry.into(); let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.)); self.linestrip([tl, tr, br, bl, tl], color); } @@ -709,10 +710,16 @@ where /// # bevy_ecs::system::assert_is_system(system); /// ``` #[inline] - pub fn rect_2d(&mut self, isometry: Isometry2d, size: Vec2, color: impl Into) { + pub fn rect_2d( + &mut self, + isometry: impl Into, + size: Vec2, + color: impl Into, + ) { if !self.enabled { return; } + let isometry = isometry.into(); let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2); self.linestrip_2d([tl, tr, br, bl, tl], color); } diff --git a/crates/bevy_gizmos/src/grid.rs b/crates/bevy_gizmos/src/grid.rs index 55e611d767874..20cb4e115b023 100644 --- a/crates/bevy_gizmos/src/grid.rs +++ b/crates/bevy_gizmos/src/grid.rs @@ -218,14 +218,14 @@ where /// ``` pub fn grid( &mut self, - isometry: Isometry3d, + isometry: impl Into, cell_count: UVec2, spacing: Vec2, color: impl Into, ) -> GridBuilder2d<'_, 'w, 's, Config, Clear> { GridBuilder2d { gizmos: self, - isometry, + isometry: isometry.into(), spacing, cell_count, skew: Vec2::ZERO, @@ -272,14 +272,14 @@ where /// ``` pub fn grid_3d( &mut self, - isometry: Isometry3d, + isometry: impl Into, cell_count: UVec3, spacing: Vec3, color: impl Into, ) -> GridBuilder3d<'_, 'w, 's, Config, Clear> { GridBuilder3d { gizmos: self, - isometry, + isometry: isometry.into(), spacing, cell_count, skew: Vec3::ZERO, @@ -326,11 +326,12 @@ where /// ``` pub fn grid_2d( &mut self, - isometry: Isometry2d, + isometry: impl Into, cell_count: UVec2, spacing: Vec2, color: impl Into, ) -> GridBuilder2d<'_, 'w, 's, Config, Clear> { + let isometry = isometry.into(); GridBuilder2d { gizmos: self, isometry: Isometry3d::new( diff --git a/crates/bevy_gizmos/src/light.rs b/crates/bevy_gizmos/src/light.rs index 615f1e8753cbf..9a618ac85acc7 100644 --- a/crates/bevy_gizmos/src/light.rs +++ b/crates/bevy_gizmos/src/light.rs @@ -41,20 +41,10 @@ fn point_light_gizmo( ) { let position = transform.translation(); gizmos - .primitive_3d( - &Sphere { - radius: point_light.radius, - }, - Isometry3d::from_translation(position), - color, - ) + .primitive_3d(&Sphere::new(point_light.radius), position, color) .resolution(16); gizmos - .sphere( - Isometry3d::from_translation(position), - point_light.range, - color, - ) + .sphere(position, point_light.range, color) .resolution(32); } @@ -68,13 +58,7 @@ fn spot_light_gizmo( ) { let (_, rotation, translation) = transform.to_scale_rotation_translation(); gizmos - .primitive_3d( - &Sphere { - radius: spot_light.radius, - }, - Isometry3d::from_translation(translation), - color, - ) + .primitive_3d(&Sphere::new(spot_light.radius), translation, color) .resolution(16); // Offset the tip of the cone to the light position. diff --git a/crates/bevy_gizmos/src/primitives/dim2.rs b/crates/bevy_gizmos/src/primitives/dim2.rs index 732eba0c019d8..0c3b44cd67dee 100644 --- a/crates/bevy_gizmos/src/primitives/dim2.rs +++ b/crates/bevy_gizmos/src/primitives/dim2.rs @@ -33,7 +33,7 @@ pub trait GizmoPrimitive2d { fn primitive_2d( &mut self, primitive: &P, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_>; } @@ -53,12 +53,13 @@ where fn primitive_2d( &mut self, primitive: &Dir2, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); let start = Vec2::ZERO; let end = *primitive * MIN_LINE_LEN; self.arrow_2d(isometry * start, isometry * end, color); @@ -80,13 +81,14 @@ where fn primitive_2d( &mut self, primitive: &Arc2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); let start_iso = isometry * Isometry2d::from_rotation(Rot2::radians(-primitive.half_angle)); self.arc_2d( @@ -113,7 +115,7 @@ where fn primitive_2d( &mut self, primitive: &Circle, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { self.circle_2d(isometry, primitive.radius, color) @@ -135,13 +137,14 @@ where fn primitive_2d( &mut self, primitive: &CircularSector, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); let color = color.into(); let start_iso = @@ -177,13 +180,14 @@ where fn primitive_2d( &mut self, primitive: &CircularSegment, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); let color = color.into(); let start_iso = @@ -218,7 +222,7 @@ where fn primitive_2d<'a>( &mut self, primitive: &Ellipse, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { self.ellipse_2d(isometry, primitive.half_size, color) @@ -280,12 +284,12 @@ where fn primitive_2d( &mut self, primitive: &Annulus, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Annulus2dBuilder { gizmos: self, - isometry, + isometry: isometry.into(), inner_radius: primitive.inner_circle.radius, outer_radius: primitive.outer_circle.radius, color: color.into(), @@ -340,12 +344,13 @@ where fn primitive_2d( &mut self, primitive: &Rhombus, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; }; + let isometry = isometry.into(); let [a, b, c, d] = [(1.0, 0.0), (0.0, 1.0), (-1.0, 0.0), (0.0, -1.0)].map(|(sign_x, sign_y)| { Vec2::new( @@ -373,9 +378,10 @@ where fn primitive_2d( &mut self, primitive: &Capsule2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { + let isometry = isometry.into(); let polymorphic_color: Color = color.into(); if !self.enabled { @@ -465,13 +471,13 @@ where fn primitive_2d( &mut self, primitive: &Line2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Line2dBuilder { gizmos: self, direction: primitive.direction, - isometry, + isometry: isometry.into(), color: color.into(), draw_arrow: false, } @@ -523,9 +529,10 @@ where fn primitive_2d( &mut self, primitive: &Plane2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { + let isometry = isometry.into(); let polymorphic_color: Color = color.into(); if !self.enabled { @@ -604,7 +611,7 @@ where fn primitive_2d( &mut self, primitive: &Segment2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Segment2dBuilder { @@ -612,7 +619,7 @@ where direction: primitive.direction, half_length: primitive.half_length, - isometry, + isometry: isometry.into(), color: color.into(), draw_arrow: Default::default(), @@ -658,13 +665,15 @@ where fn primitive_2d( &mut self, primitive: &Polyline2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + self.linestrip_2d( primitive .vertices @@ -691,13 +700,15 @@ where fn primitive_2d( &mut self, primitive: &BoxedPolyline2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + self.linestrip_2d( primitive .vertices @@ -724,12 +735,15 @@ where fn primitive_2d( &mut self, primitive: &Triangle2d, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + + let isometry = isometry.into(); + let [a, b, c] = primitive.vertices; let positions = [a, b, c, a].map(|vec2| isometry * vec2); self.linestrip_2d(positions, color); @@ -751,13 +765,15 @@ where fn primitive_2d( &mut self, primitive: &Rectangle, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + let [a, b, c, d] = [(1.0, 1.0), (1.0, -1.0), (-1.0, -1.0), (-1.0, 1.0)].map(|(sign_x, sign_y)| { Vec2::new( @@ -786,13 +802,15 @@ where fn primitive_2d( &mut self, primitive: &Polygon, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + // Check if the polygon needs a closing point let closing_point = { let first = primitive.vertices.first(); @@ -829,13 +847,15 @@ where fn primitive_2d( &mut self, primitive: &BoxedPolygon, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + let closing_point = { let first = primitive.vertices.first(); (primitive.vertices.last() != first) @@ -870,13 +890,15 @@ where fn primitive_2d( &mut self, primitive: &RegularPolygon, - isometry: Isometry2d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + let points = (0..=primitive.sides) .map(|n| single_circle_coordinate(primitive.circumcircle.radius, primitive.sides, n)) .map(|vec2| isometry * vec2); diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_gizmos/src/primitives/dim3.rs index 5ed99314f7633..4c11628246878 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_gizmos/src/primitives/dim3.rs @@ -31,7 +31,7 @@ pub trait GizmoPrimitive3d { fn primitive_3d( &mut self, primitive: &P, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_>; } @@ -51,9 +51,10 @@ where fn primitive_3d( &mut self, primitive: &Dir3, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { + let isometry = isometry.into(); let start = Vec3::ZERO; let end = primitive.as_vec3(); self.arrow(isometry * start, isometry * end, color); @@ -75,7 +76,7 @@ where fn primitive_3d( &mut self, primitive: &Sphere, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { self.sphere(isometry, primitive.radius, color) @@ -136,13 +137,13 @@ where fn primitive_3d( &mut self, primitive: &Plane3d, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Plane3dBuilder { gizmos: self, normal: primitive.normal, - isometry, + isometry: isometry.into(), color: color.into(), cell_count: UVec2::splat(3), spacing: Vec2::splat(1.0), @@ -188,13 +189,14 @@ where fn primitive_3d( &mut self, primitive: &Line3d, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); let color = color.into(); let direction = primitive.direction.as_vec3(); self.arrow(isometry * Vec3::ZERO, isometry * direction, color); @@ -222,13 +224,14 @@ where fn primitive_3d( &mut self, primitive: &Segment3d, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); let direction = primitive.direction.as_vec3(); self.line(isometry * direction, isometry * (-direction), color); } @@ -250,13 +253,14 @@ where fn primitive_3d( &mut self, primitive: &Polyline3d, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); self.linestrip(primitive.vertices.map(|vec3| isometry * vec3), color); } } @@ -276,13 +280,14 @@ where fn primitive_3d( &mut self, primitive: &BoxedPolyline3d, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); self.linestrip( primitive .vertices @@ -309,13 +314,14 @@ where fn primitive_3d( &mut self, primitive: &Triangle3d, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); let [a, b, c] = primitive.vertices; self.linestrip([a, b, c, a].map(|vec3| isometry * vec3), color); } @@ -336,13 +342,15 @@ where fn primitive_3d( &mut self, primitive: &Cuboid, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + // transform the points from the reference unit cube to the cuboid coords let vertices @ [a, b, c, d, e, f, g, h] = [ [1.0, 1.0, 1.0], @@ -429,14 +437,14 @@ where fn primitive_3d( &mut self, primitive: &Cylinder, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Cylinder3dBuilder { gizmos: self, radius: primitive.radius, half_height: primitive.half_height, - isometry, + isometry: isometry.into(), color: color.into(), resolution: DEFAULT_RESOLUTION, } @@ -515,14 +523,14 @@ where fn primitive_3d( &mut self, primitive: &Capsule3d, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Capsule3dBuilder { gizmos: self, radius: primitive.radius, half_length: primitive.half_length, - isometry, + isometry: isometry.into(), color: color.into(), resolution: DEFAULT_RESOLUTION, } @@ -655,14 +663,14 @@ where fn primitive_3d( &mut self, primitive: &Cone, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Cone3dBuilder { gizmos: self, radius: primitive.radius, height: primitive.height, - isometry, + isometry: isometry.into(), color: color.into(), base_resolution: DEFAULT_RESOLUTION, height_resolution: DEFAULT_RESOLUTION, @@ -757,7 +765,7 @@ where fn primitive_3d( &mut self, primitive: &ConicalFrustum, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { ConicalFrustum3dBuilder { @@ -765,7 +773,7 @@ where radius_top: primitive.radius_top, radius_bottom: primitive.radius_bottom, height: primitive.height, - isometry, + isometry: isometry.into(), color: color.into(), resolution: DEFAULT_RESOLUTION, } @@ -861,14 +869,14 @@ where fn primitive_3d( &mut self, primitive: &Torus, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { Torus3dBuilder { gizmos: self, minor_radius: primitive.minor_radius, major_radius: primitive.major_radius, - isometry, + isometry: isometry.into(), color: color.into(), minor_resolution: DEFAULT_RESOLUTION, major_resolution: DEFAULT_RESOLUTION, @@ -937,13 +945,15 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d for Gizmos<'w, ' fn primitive_3d( &mut self, primitive: &Tetrahedron, - isometry: Isometry3d, + isometry: impl Into, color: impl Into, ) -> Self::Output<'_> { if !self.enabled { return; } + let isometry = isometry.into(); + let [a, b, c, d] = primitive.vertices.map(|vec3| isometry * vec3); let lines = [(a, b), (a, c), (a, d), (b, c), (b, d), (c, d)]; diff --git a/crates/bevy_gizmos/src/rounded_box.rs b/crates/bevy_gizmos/src/rounded_box.rs index 70ee09b64d01d..2b107dbcf52c8 100644 --- a/crates/bevy_gizmos/src/rounded_box.rs +++ b/crates/bevy_gizmos/src/rounded_box.rs @@ -240,7 +240,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { /// ``` pub fn rounded_rect( &mut self, - isometry: Isometry3d, + isometry: impl Into, size: Vec2, color: impl Into, ) -> RoundedRectBuilder<'_, 'w, 's, T> { @@ -248,7 +248,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { RoundedRectBuilder { gizmos: self, config: RoundedBoxConfig { - isometry, + isometry: isometry.into(), color: color.into(), corner_radius, arc_resolution: DEFAULT_ARC_RESOLUTION, @@ -294,10 +294,11 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { /// ``` pub fn rounded_rect_2d( &mut self, - isometry: Isometry2d, + isometry: impl Into, size: Vec2, color: impl Into, ) -> RoundedRectBuilder<'_, 'w, 's, T> { + let isometry = isometry.into(); let corner_radius = size.min_element() * DEFAULT_CORNER_RADIUS; RoundedRectBuilder { gizmos: self, @@ -351,7 +352,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { /// ``` pub fn rounded_cuboid( &mut self, - isometry: Isometry3d, + isometry: impl Into, size: Vec3, color: impl Into, ) -> RoundedCuboidBuilder<'_, 'w, 's, T> { @@ -359,7 +360,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { RoundedCuboidBuilder { gizmos: self, config: RoundedBoxConfig { - isometry, + isometry: isometry.into(), color: color.into(), corner_radius, arc_resolution: DEFAULT_ARC_RESOLUTION, diff --git a/crates/bevy_math/src/bounding/bounded2d/mod.rs b/crates/bevy_math/src/bounding/bounded2d/mod.rs index 8f6812bc27247..7e824101bc790 100644 --- a/crates/bevy_math/src/bounding/bounded2d/mod.rs +++ b/crates/bevy_math/src/bounding/bounded2d/mod.rs @@ -24,9 +24,9 @@ fn point_cloud_2d_center(points: &[Vec2]) -> Vec2 { /// A trait with methods that return 2D bounding volumes for a shape. pub trait Bounded2d { /// Get an axis-aligned bounding box for the shape translated and rotated by the given isometry. - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d; + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d; /// Get a bounding circle for the shape translated and rotated by the given isometry. - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle; + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle; } /// A 2D axis-aligned bounding box, or bounding rectangle @@ -58,7 +58,9 @@ impl Aabb2d { /// /// Panics if the given set of points is empty. #[inline(always)] - pub fn from_point_cloud(isometry: Isometry2d, points: &[Vec2]) -> Aabb2d { + pub fn from_point_cloud(isometry: impl Into, points: &[Vec2]) -> Aabb2d { + let isometry = isometry.into(); + // Transform all points by rotation let mut iter = points.iter().map(|point| isometry.rotation * *point); @@ -472,7 +474,9 @@ impl BoundingCircle { /// /// The bounding circle is not guaranteed to be the smallest possible. #[inline(always)] - pub fn from_point_cloud(isometry: Isometry2d, points: &[Vec2]) -> BoundingCircle { + pub fn from_point_cloud(isometry: impl Into, points: &[Vec2]) -> BoundingCircle { + let isometry = isometry.into(); + let center = point_cloud_2d_center(points); let mut radius_squared = 0.0; diff --git a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs index 1d6b67b0d6aa7..d4d211cd127a5 100644 --- a/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded2d/primitive_impls.rs @@ -16,11 +16,13 @@ use smallvec::SmallVec; use super::{Aabb2d, Bounded2d, BoundingCircle}; impl Bounded2d for Circle { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); Aabb2d::new(isometry.translation, Vec2::splat(self.radius)) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, self.radius) } } @@ -58,19 +60,23 @@ fn arc_bounding_points(arc: Arc2d, rotation: impl Into) -> SmallVec<[Vec2; } impl Bounded2d for Arc2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> 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(isometry); } + let isometry = isometry.into(); + Aabb2d::from_point_cloud( Isometry2d::from_translation(isometry.translation), &arc_bounding_points(*self, isometry.rotation), ) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); + // 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; @@ -86,7 +92,9 @@ impl Bounded2d for Arc2d { } impl Bounded2d for CircularSector { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + // 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(isometry); @@ -99,8 +107,10 @@ impl Bounded2d for CircularSector { Aabb2d::from_point_cloud(Isometry2d::from_translation(isometry.translation), &bounds) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { if self.arc.is_major() { + let isometry = isometry.into(); + // If the arc is major, that is, greater than a semicircle, // then bounding circle is just the circle defining the sector. BoundingCircle::new(isometry.translation, self.arc.radius) @@ -121,17 +131,19 @@ impl Bounded2d for CircularSector { } impl Bounded2d for CircularSegment { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { self.arc.aabb_2d(isometry) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { self.arc.bounding_circle(isometry) } } impl Bounded2d for Ellipse { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + // V = (hh * cos(beta), hh * sin(beta)) // #####*##### // ### | ### @@ -160,23 +172,28 @@ impl Bounded2d for Ellipse { Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, self.semi_major()) } } impl Bounded2d for Annulus { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); Aabb2d::new(isometry.translation, Vec2::splat(self.outer_circle.radius)) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, self.outer_circle.radius) } } impl Bounded2d for Rhombus { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + let [rotated_x_half_diagonal, rotated_y_half_diagonal] = [ isometry.rotation * Vec2::new(self.half_diagonals.x, 0.0), isometry.rotation * Vec2::new(0.0, self.half_diagonals.y), @@ -191,13 +208,16 @@ impl Bounded2d for Rhombus { } } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, self.circumradius()) } } impl Bounded2d for Plane2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + 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; @@ -211,13 +231,16 @@ impl Bounded2d for Plane2d { Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded2d for Line2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + let direction = isometry.rotation * *self.direction; // Dividing `f32::MAX` by 2.0 is helpful so that we can do operations @@ -230,13 +253,16 @@ impl Bounded2d for Line2d { Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded2d for Segment2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + // Rotate the segment by `rotation` let direction = isometry.rotation * *self.direction; let half_size = (self.half_length * direction).abs(); @@ -244,33 +270,35 @@ impl Bounded2d for Segment2d { Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, self.half_length) } } impl Bounded2d for Polyline2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for BoxedPolyline2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for Triangle2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); 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)); @@ -282,7 +310,8 @@ impl Bounded2d for Triangle2d { } } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); let [a, b, c] = self.vertices; // The points of the segment opposite to the obtuse or right angle if one exists @@ -313,7 +342,9 @@ impl Bounded2d for Triangle2d { } impl Bounded2d for Rectangle { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + // Compute the AABB of the rotated rectangle by transforming the half-extents // by an absolute rotation matrix. let (sin, cos) = isometry.rotation.sin_cos(); @@ -323,34 +354,37 @@ impl Bounded2d for Rectangle { Aabb2d::new(isometry.translation, half_size) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); let radius = self.half_size.length(); BoundingCircle::new(isometry.translation, radius) } } impl Bounded2d for Polygon { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for BoxedPolygon { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { Aabb2d::from_point_cloud(isometry, &self.vertices) } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { BoundingCircle::from_point_cloud(isometry, &self.vertices) } } impl Bounded2d for RegularPolygon { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + let mut min = Vec2::ZERO; let mut max = Vec2::ZERO; @@ -365,13 +399,16 @@ impl Bounded2d for RegularPolygon { } } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, self.circumcircle.radius) } } impl Bounded2d for Capsule2d { - fn aabb_2d(&self, isometry: Isometry2d) -> Aabb2d { + fn aabb_2d(&self, isometry: impl Into) -> Aabb2d { + let isometry = isometry.into(); + // 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. @@ -390,7 +427,8 @@ impl Bounded2d for Capsule2d { } } - fn bounding_circle(&self, isometry: Isometry2d) -> BoundingCircle { + fn bounding_circle(&self, isometry: impl Into) -> BoundingCircle { + let isometry = isometry.into(); BoundingCircle::new(isometry.translation, self.radius + self.half_length) } } diff --git a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs b/crates/bevy_math/src/bounding/bounded3d/extrusion.rs index 8784a3286ecfd..8eb3eb9791795 100644 --- a/crates/bevy_math/src/bounding/bounded3d/extrusion.rs +++ b/crates/bevy_math/src/bounding/bounded3d/extrusion.rs @@ -17,9 +17,11 @@ use crate::{bounding::Bounded2d, primitives::Circle}; use super::{Aabb3d, Bounded3d, BoundingSphere}; impl BoundedExtrusion for Circle { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ + let isometry = isometry.into(); + let segment_dir = isometry.rotation * Vec3A::Z; let top = (segment_dir * half_depth).abs(); @@ -34,7 +36,8 @@ impl BoundedExtrusion for Circle { } impl BoundedExtrusion for Ellipse { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let Vec2 { x: a, y: b } = self.half_size; let normal = isometry.rotation * Vec3A::Z; let conjugate_rot = isometry.rotation.conjugate(); @@ -64,7 +67,8 @@ impl BoundedExtrusion for Ellipse { } impl BoundedExtrusion for Line2d { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let dir = isometry.rotation * Vec3A::from(self.direction.extend(0.)); let half_depth = (isometry.rotation * Vec3A::new(0., 0., half_depth)).abs(); @@ -80,7 +84,8 @@ impl BoundedExtrusion for Line2d { } impl BoundedExtrusion for Segment2d { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let half_size = isometry.rotation * Vec3A::from(self.point1().extend(0.)); let depth = isometry.rotation * Vec3A::new(0., 0., half_depth); @@ -89,7 +94,8 @@ impl BoundedExtrusion for Segment2d { } impl BoundedExtrusion for Polyline2d { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); 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); @@ -99,7 +105,8 @@ impl BoundedExtrusion for Polyline2d { } impl BoundedExtrusion for BoxedPolyline2d { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); 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); @@ -108,7 +115,8 @@ impl BoundedExtrusion for BoxedPolyline2d { } impl BoundedExtrusion for Triangle2d { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); 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); @@ -117,7 +125,7 @@ impl BoundedExtrusion for Triangle2d { } impl BoundedExtrusion for Rectangle { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { Cuboid { half_size: self.half_size.extend(half_depth), } @@ -126,7 +134,8 @@ impl BoundedExtrusion for Rectangle { } impl BoundedExtrusion for Polygon { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); 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); @@ -136,7 +145,8 @@ impl BoundedExtrusion for Polygon { } impl BoundedExtrusion for BoxedPolygon { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); 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); @@ -145,7 +155,8 @@ impl BoundedExtrusion for BoxedPolygon { } impl BoundedExtrusion for RegularPolygon { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let aabb = Aabb3d::from_point_cloud( isometry, self.vertices(0.).into_iter().map(|v| v.extend(0.)), @@ -157,14 +168,13 @@ impl BoundedExtrusion for RegularPolygon { } impl BoundedExtrusion for Capsule2d { - fn extrusion_aabb_3d(&self, half_depth: f32, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let aabb = Cylinder { half_height: half_depth, radius: self.radius, } - .aabb_3d(Isometry3d::from_rotation( - isometry.rotation * Quat::from_rotation_x(FRAC_PI_2), - )); + .aabb_3d(isometry.rotation * Quat::from_rotation_x(FRAC_PI_2)); let up = isometry.rotation * Vec3A::new(0., self.half_length, 0.); let half_size = aabb.max + up.abs(); @@ -173,11 +183,11 @@ impl BoundedExtrusion for Capsule2d { } impl Bounded3d for Extrusion { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { self.base_shape.extrusion_aabb_3d(self.half_depth, isometry) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { self.base_shape .extrusion_bounding_sphere(self.half_depth, isometry) } @@ -191,7 +201,8 @@ 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, isometry: Isometry3d) -> Aabb3d { + fn extrusion_aabb_3d(&self, half_depth: f32, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let cap_normal = isometry.rotation * Vec3A::Z; let conjugate_rot = isometry.rotation.conjugate(); @@ -213,7 +224,7 @@ 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(Isometry2d::from_rotation(Rot2::radians(angle))); + let aabb2d = self.aabb_2d(Rot2::radians(angle)); (aabb2d.half_size().x * scale, aabb2d.center().x * scale) }); @@ -225,7 +236,13 @@ pub trait BoundedExtrusion: Primitive2d + Bounded2d { } /// 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, isometry: Isometry3d) -> BoundingSphere { + fn extrusion_bounding_sphere( + &self, + half_depth: f32, + isometry: impl Into, + ) -> BoundingSphere { + let isometry = isometry.into(); + // 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 @@ -261,13 +278,12 @@ mod tests { 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(isometry); + let aabb = cylinder.aabb_3d(translation); 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(isometry); + let bounding_sphere = cylinder.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), ops::hypot(1.0, 0.5)); } diff --git a/crates/bevy_math/src/bounding/bounded3d/mod.rs b/crates/bevy_math/src/bounding/bounded3d/mod.rs index 81edbe8a57d4f..38460fc08e09f 100644 --- a/crates/bevy_math/src/bounding/bounded3d/mod.rs +++ b/crates/bevy_math/src/bounding/bounded3d/mod.rs @@ -27,9 +27,9 @@ fn point_cloud_3d_center(points: impl Iterator>) -> Vec3 /// A trait with methods that return 3D bounding volumes for a shape. pub trait Bounded3d { /// Get an axis-aligned bounding box for the shape translated and rotated by the given isometry. - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d; + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d; /// Get a bounding sphere for the shape translated and rotated by the given isometry. - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere; + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere; } /// A 3D axis-aligned bounding box @@ -62,9 +62,11 @@ impl Aabb3d { /// Panics if the given set of points is empty. #[inline(always)] pub fn from_point_cloud( - isometry: Isometry3d, + isometry: impl Into, points: impl Iterator>, ) -> Aabb3d { + let isometry = isometry.into(); + // Transform all points by rotation let mut iter = points.map(|point| isometry.rotation * point.into()); @@ -476,9 +478,11 @@ impl BoundingSphere { /// The bounding sphere is not guaranteed to be the smallest possible. #[inline(always)] pub fn from_point_cloud( - isometry: Isometry3d, + isometry: impl Into, points: &[impl Copy + Into], ) -> BoundingSphere { + let isometry = isometry.into(); + let center = point_cloud_3d_center(points.iter().map(|v| Into::::into(*v))); let mut radius_squared: f32 = 0.0; diff --git a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs index bb6a6a5a12c37..3871ac07490ae 100644 --- a/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs +++ b/crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs @@ -15,17 +15,21 @@ use crate::{ use super::{Aabb3d, Bounded3d, BoundingSphere}; impl Bounded3d for Sphere { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); Aabb3d::new(isometry.translation, Vec3::splat(self.radius)) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); BoundingSphere::new(isometry.translation, self.radius) } } impl Bounded3d for InfinitePlane3d { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); + 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; @@ -41,13 +45,15 @@ impl Bounded3d for InfinitePlane3d { Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); BoundingSphere::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded3d for Line3d { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let direction = isometry.rotation * *self.direction; // Dividing `f32::MAX` by 2.0 is helpful so that we can do operations @@ -61,13 +67,16 @@ impl Bounded3d for Line3d { Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); BoundingSphere::new(isometry.translation, f32::MAX / 2.0) } } impl Bounded3d for Segment3d { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); + // Rotate the segment by `rotation` let direction = isometry.rotation * *self.direction; let half_size = (self.half_length * direction).abs(); @@ -75,33 +84,36 @@ impl Bounded3d for Segment3d { Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); BoundingSphere::new(isometry.translation, self.half_length) } } impl Bounded3d for Polyline3d { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { Aabb3d::from_point_cloud(isometry, self.vertices.iter().copied()) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { BoundingSphere::from_point_cloud(isometry, &self.vertices) } } impl Bounded3d for BoxedPolyline3d { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { Aabb3d::from_point_cloud(isometry, self.vertices.iter().copied()) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { BoundingSphere::from_point_cloud(isometry, &self.vertices) } } impl Bounded3d for Cuboid { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); + // Compute the AABB of the rotated cuboid by transforming the half-size // by an absolute rotation matrix. let rot_mat = Mat3::from_quat(isometry.rotation); @@ -115,15 +127,18 @@ impl Bounded3d for Cuboid { Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); BoundingSphere::new(isometry.translation, self.half_size.length()) } } impl Bounded3d for Cylinder { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ + let isometry = isometry.into(); + let segment_dir = isometry.rotation * Vec3A::Y; let top = segment_dir * self.half_height; let bottom = -top; @@ -137,14 +152,17 @@ impl Bounded3d for Cylinder { } } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); let radius = ops::hypot(self.radius, self.half_height); BoundingSphere::new(isometry.translation, radius) } } impl Bounded3d for Capsule3d { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); + // Get the line segment between the hemispheres of the rotated capsule let segment_dir = isometry.rotation * Vec3A::Y; let top = segment_dir * self.half_length; @@ -160,15 +178,18 @@ impl Bounded3d for Capsule3d { } } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); BoundingSphere::new(isometry.translation, self.radius + self.half_length) } } impl Bounded3d for Cone { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ + let isometry = isometry.into(); + let segment_dir = isometry.rotation * Vec3A::Y; let top = segment_dir * 0.5 * self.height; let bottom = -top; @@ -182,7 +203,9 @@ impl Bounded3d for Cone { } } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); + // Get the triangular cross-section of the cone. let half_height = 0.5 * self.height; let triangle = Triangle2d::new( @@ -203,9 +226,11 @@ impl Bounded3d for Cone { } impl Bounded3d for ConicalFrustum { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { // Reference: http://iquilezles.org/articles/diskbbox/ + let isometry = isometry.into(); + let segment_dir = isometry.rotation * Vec3A::Y; let top = segment_dir * 0.5 * self.height; let bottom = -top; @@ -223,7 +248,8 @@ impl Bounded3d for ConicalFrustum { } } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); let half_height = 0.5 * self.height; // To compute the bounding sphere, we'll get the center and radius of the circumcircle @@ -286,7 +312,9 @@ impl Bounded3d for ConicalFrustum { } impl Bounded3d for Torus { - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); + // Compute the AABB of a flat disc with the major radius of the torus. // Reference: http://iquilezles.org/articles/diskbbox/ let normal = isometry.rotation * Vec3A::Y; @@ -299,14 +327,16 @@ impl Bounded3d for Torus { Aabb3d::new(isometry.translation, half_size) } - fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); BoundingSphere::new(isometry.translation, self.outer_radius()) } } impl Bounded3d for Triangle3d { /// Get the bounding box of the triangle. - fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d { + fn aabb_3d(&self, isometry: impl Into) -> Aabb3d { + let isometry = isometry.into(); let [a, b, c] = self.vertices; let a = isometry.rotation * a; @@ -327,7 +357,9 @@ 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, isometry: Isometry3d) -> BoundingSphere { + fn bounding_sphere(&self, isometry: impl Into) -> BoundingSphere { + let isometry = isometry.into(); + if self.is_degenerate() || self.is_obtuse() { let (p1, p2) = self.largest_side(); let (p1, p2) = (Vec3A::from(p1), Vec3A::from(p2)); @@ -362,13 +394,12 @@ 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(isometry); + let aabb = sphere.aabb_3d(translation); 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(isometry); + let bounding_sphere = sphere.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.0); } @@ -376,25 +407,24 @@ 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(isometry); + let aabb1 = InfinitePlane3d::new(Vec3::X).aabb_3d(translation); 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(isometry); + let aabb2 = InfinitePlane3d::new(Vec3::Y).aabb_3d(translation); 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(isometry); + let aabb3 = InfinitePlane3d::new(Vec3::Z).aabb_3d(translation); 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(isometry); + let aabb4 = InfinitePlane3d::new(Vec3::ONE).aabb_3d(translation); 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(isometry); + let bounding_sphere = InfinitePlane3d::new(Vec3::Y).bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), f32::MAX / 2.0); } @@ -402,28 +432,27 @@ 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(isometry); + let aabb1 = Line3d { direction: Dir3::Y }.aabb_3d(translation); 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(isometry); + let aabb2 = Line3d { direction: Dir3::X }.aabb_3d(translation); 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(isometry); + let aabb3 = Line3d { direction: Dir3::Z }.aabb_3d(translation); 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(isometry); + .aabb_3d(translation); 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(isometry); + let bounding_sphere = Line3d { direction: Dir3::Y }.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), f32::MAX / 2.0); } @@ -431,16 +460,15 @@ 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(isometry); + let aabb = segment.aabb_3d(translation); 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(isometry); + let bounding_sphere = segment.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), ops::hypot(1.0, 0.5)); } @@ -454,13 +482,12 @@ 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(isometry); + let aabb = polyline.aabb_3d(translation); 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(isometry); + let bounding_sphere = polyline.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!( bounding_sphere.radius(), @@ -481,7 +508,7 @@ mod tests { 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(Isometry3d::from_translation(translation)); + let bounding_sphere = cuboid.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!( bounding_sphere.radius(), @@ -493,9 +520,8 @@ 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(isometry); + let aabb = cylinder.aabb_3d(translation); assert_eq!( aabb.min, Vec3A::from(translation) - Vec3A::new(0.5, 1.0, 0.5) @@ -505,7 +531,7 @@ mod tests { Vec3A::from(translation) + Vec3A::new(0.5, 1.0, 0.5) ); - let bounding_sphere = cylinder.bounding_sphere(isometry); + let bounding_sphere = cylinder.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), ops::hypot(1.0, 0.5)); } @@ -514,9 +540,8 @@ 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(isometry); + let aabb = capsule.aabb_3d(translation); assert_eq!( aabb.min, Vec3A::from(translation) - Vec3A::new(0.5, 1.5, 0.5) @@ -526,7 +551,7 @@ mod tests { Vec3A::from(translation) + Vec3A::new(0.5, 1.5, 0.5) ); - let bounding_sphere = capsule.bounding_sphere(isometry); + let bounding_sphere = capsule.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.5); } @@ -538,13 +563,12 @@ 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(isometry); + let aabb = cone.aabb_3d(translation); 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(isometry); + let bounding_sphere = cone.bounding_sphere(translation); assert_eq!( bounding_sphere.center, Vec3A::from(translation) + Vec3A::NEG_Y * 0.25 @@ -560,13 +584,12 @@ 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(isometry); + let aabb = conical_frustum.aabb_3d(translation); 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(isometry); + let bounding_sphere = conical_frustum.bounding_sphere(translation); assert_eq!( bounding_sphere.center, Vec3A::from(translation) + Vec3A::NEG_Y * 0.1875 @@ -582,15 +605,14 @@ 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(isometry); + let aabb = conical_frustum.aabb_3d(translation); 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(isometry); + let bounding_sphere = conical_frustum.bounding_sphere(translation); assert_eq!( bounding_sphere.center, Vec3A::from(translation) + Vec3A::NEG_Y * 0.5 @@ -605,13 +627,12 @@ 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(isometry); + let aabb = torus.aabb_3d(translation); 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(isometry); + let bounding_sphere = torus.bounding_sphere(translation); assert_eq!(bounding_sphere.center, translation.into()); assert_eq!(bounding_sphere.radius(), 1.5); } diff --git a/crates/bevy_math/src/isometry.rs b/crates/bevy_math/src/isometry.rs index 356b78738db05..e01a8cd713661 100644 --- a/crates/bevy_math/src/isometry.rs +++ b/crates/bevy_math/src/isometry.rs @@ -192,6 +192,20 @@ impl From for Affine2 { } } +impl From for Isometry2d { + #[inline] + fn from(translation: Vec2) -> Self { + Isometry2d::from_translation(translation) + } +} + +impl From for Isometry2d { + #[inline] + fn from(rotation: Rot2) -> Self { + Isometry2d::from_rotation(rotation) + } +} + impl Mul for Isometry2d { type Output = Self; @@ -466,6 +480,27 @@ impl From for Affine3A { } } +impl From for Isometry3d { + #[inline] + fn from(translation: Vec3) -> Self { + Isometry3d::from_translation(translation) + } +} + +impl From for Isometry3d { + #[inline] + fn from(translation: Vec3A) -> Self { + Isometry3d::from_translation(translation) + } +} + +impl From for Isometry3d { + #[inline] + fn from(rotation: Quat) -> Self { + Isometry3d::from_rotation(rotation) + } +} + impl Mul for Isometry3d { type Output = Self; diff --git a/crates/bevy_math/src/primitives/dim3.rs b/crates/bevy_math/src/primitives/dim3.rs index d3761455d8cf2..cd1b5013e6b72 100644 --- a/crates/bevy_math/src/primitives/dim3.rs +++ b/crates/bevy_math/src/primitives/dim3.rs @@ -220,7 +220,8 @@ impl InfinitePlane3d { /// `point`. The result is a signed value; it's positive if the point lies in the half-space /// that the plane's normal vector points towards. #[inline] - pub fn signed_distance(&self, isometry: Isometry3d, point: Vec3) -> f32 { + pub fn signed_distance(&self, isometry: impl Into, point: Vec3) -> f32 { + let isometry = isometry.into(); self.normal.dot(isometry.inverse() * point) } @@ -228,7 +229,7 @@ impl InfinitePlane3d { /// /// This projects the point orthogonally along the shortest path onto the plane. #[inline] - pub fn project_point(&self, isometry: Isometry3d, point: Vec3) -> Vec3 { + pub fn project_point(&self, isometry: impl Into, point: Vec3) -> Vec3 { point - self.normal * self.signed_distance(isometry, point) } @@ -1374,36 +1375,36 @@ mod tests { let point_in_plane = Vec3::X + Vec3::Z; assert_eq!( - plane.signed_distance(Isometry3d::from_translation(origin), point_in_plane), + plane.signed_distance(origin, point_in_plane), 0.0, "incorrect distance" ); assert_eq!( - plane.project_point(Isometry3d::from_translation(origin), point_in_plane), + plane.project_point(origin, point_in_plane), point_in_plane, "incorrect point" ); let point_outside = Vec3::Y; assert_eq!( - plane.signed_distance(Isometry3d::from_translation(origin), point_outside), + plane.signed_distance(origin, point_outside), -1.0, "incorrect distance" ); assert_eq!( - plane.project_point(Isometry3d::from_translation(origin), point_outside), + plane.project_point(origin, point_outside), Vec3::ZERO, "incorrect point" ); let point_outside = Vec3::NEG_Y; assert_eq!( - plane.signed_distance(Isometry3d::from_translation(origin), point_outside), + plane.signed_distance(origin, point_outside), 1.0, "incorrect distance" ); assert_eq!( - plane.project_point(Isometry3d::from_translation(origin), point_outside), + plane.project_point(origin, point_outside), Vec3::ZERO, "incorrect point" ); diff --git a/examples/2d/2d_viewport_to_world.rs b/examples/2d/2d_viewport_to_world.rs index 193fc281290a1..5648f30e87211 100644 --- a/examples/2d/2d_viewport_to_world.rs +++ b/examples/2d/2d_viewport_to_world.rs @@ -1,6 +1,6 @@ //! This example demonstrates how to use the `Camera::viewport_to_world_2d` method. -use bevy::{color::palettes::basic::WHITE, math::Isometry2d, prelude::*}; +use bevy::{color::palettes::basic::WHITE, prelude::*}; fn main() { App::new() @@ -30,7 +30,7 @@ fn draw_cursor( return; }; - gizmos.circle_2d(Isometry2d::from_translation(point), 10., WHITE); + gizmos.circle_2d(point, 10., WHITE); } fn setup(mut commands: Commands) { diff --git a/examples/2d/bounding_2d.rs b/examples/2d/bounding_2d.rs index 0a44c6ae686fb..ac71c4b8b9112 100644 --- a/examples/2d/bounding_2d.rs +++ b/examples/2d/bounding_2d.rs @@ -186,14 +186,10 @@ fn render_volumes(mut gizmos: Gizmos, query: Query<(&CurrentVolume, &Intersects) let color = if **intersects { AQUA } else { ORANGE_RED }; match volume { CurrentVolume::Aabb(a) => { - gizmos.rect_2d( - Isometry2d::from_translation(a.center()), - a.half_size() * 2., - color, - ); + gizmos.rect_2d(a.center(), a.half_size() * 2., color); } CurrentVolume::Circle(c) => { - gizmos.circle_2d(Isometry2d::from_translation(c.center()), c.radius(), color); + gizmos.circle_2d(c.center(), c.radius(), color); } } } @@ -288,7 +284,7 @@ fn setup(mut commands: Commands) { fn draw_filled_circle(gizmos: &mut Gizmos, position: Vec2, color: Srgba) { for r in [1., 2., 3.] { - gizmos.circle_2d(Isometry2d::from_translation(position), r, color); + gizmos.circle_2d(position, r, color); } } @@ -361,9 +357,7 @@ fn aabb_cast_system( **intersects = toi.is_some(); if let Some(toi) = toi { gizmos.rect_2d( - Isometry2d::from_translation( - aabb_cast.ray.ray.origin + *aabb_cast.ray.ray.direction * toi, - ), + aabb_cast.ray.ray.origin + *aabb_cast.ray.ray.direction * toi, aabb_cast.aabb.half_size() * 2., LIME, ); @@ -391,9 +385,7 @@ fn bounding_circle_cast_system( **intersects = toi.is_some(); if let Some(toi) = toi { gizmos.circle_2d( - Isometry2d::from_translation( - circle_cast.ray.ray.origin + *circle_cast.ray.ray.direction * toi, - ), + circle_cast.ray.ray.origin + *circle_cast.ray.ray.direction * toi, circle_cast.circle.radius(), LIME, ); @@ -414,11 +406,7 @@ fn aabb_intersection_system( ) { let center = get_intersection_position(&time); let aabb = Aabb2d::new(center, Vec2::splat(50.)); - gizmos.rect_2d( - Isometry2d::from_translation(center), - aabb.half_size() * 2., - YELLOW, - ); + gizmos.rect_2d(center, aabb.half_size() * 2., YELLOW); for (volume, mut intersects) in volumes.iter_mut() { let hit = match volume { @@ -437,11 +425,7 @@ fn circle_intersection_system( ) { let center = get_intersection_position(&time); let circle = BoundingCircle::new(center, 50.); - gizmos.circle_2d( - Isometry2d::from_translation(center), - circle.radius(), - YELLOW, - ); + gizmos.circle_2d(center, circle.radius(), YELLOW); for (volume, mut intersects) in volumes.iter_mut() { let hit = match volume { diff --git a/examples/2d/mesh2d_arcs.rs b/examples/2d/mesh2d_arcs.rs index 975cc2cf8bbf0..e1b2f604359e5 100644 --- a/examples/2d/mesh2d_arcs.rs +++ b/examples/2d/mesh2d_arcs.rs @@ -114,17 +114,9 @@ fn draw_bounds( let isometry = Isometry2d::new(translation, Rot2::radians(rotation)); let aabb = shape.0.aabb_2d(isometry); - gizmos.rect_2d( - Isometry2d::from_translation(aabb.center()), - aabb.half_size() * 2.0, - RED, - ); + gizmos.rect_2d(aabb.center(), aabb.half_size() * 2.0, RED); let bounding_circle = shape.0.bounding_circle(isometry); - gizmos.circle_2d( - Isometry2d::from_translation(bounding_circle.center), - bounding_circle.radius(), - BLUE, - ); + gizmos.circle_2d(bounding_circle.center, bounding_circle.radius(), BLUE); } } diff --git a/examples/ecs/observers.rs b/examples/ecs/observers.rs index 5b4d8b0b81129..58b5e4e13959d 100644 --- a/examples/ecs/observers.rs +++ b/examples/ecs/observers.rs @@ -1,7 +1,6 @@ //! Demonstrates how to observe life-cycle triggers as well as define custom ones. use bevy::{ - math::Isometry2d, prelude::*, utils::{HashMap, HashSet}, }; @@ -166,7 +165,7 @@ fn explode_mine(trigger: Trigger, query: Query<&Mine>, mut commands: Co fn draw_shapes(mut gizmos: Gizmos, mines: Query<&Mine>) { for mine in &mines { gizmos.circle_2d( - Isometry2d::from_translation(mine.pos), + mine.pos, mine.size, Color::hsl((mine.size - 4.0) / 16.0 * 360.0, 1.0, 0.8), ); diff --git a/examples/gizmos/2d_gizmos.rs b/examples/gizmos/2d_gizmos.rs index 929ec47e3788e..45f31ceb3c4e4 100644 --- a/examples/gizmos/2d_gizmos.rs +++ b/examples/gizmos/2d_gizmos.rs @@ -67,11 +67,7 @@ fn draw_example_collection( gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::splat(650.), BLACK); - gizmos.cross_2d( - Isometry2d::from_translation(Vec2::new(-160., 120.)), - 12., - FUCHSIA, - ); + gizmos.cross_2d(Vec2::new(-160., 120.), 12., FUCHSIA); let domain = Interval::EVERYWHERE; let curve = function_curve(domain, |t| Vec2::new(t, ops::sin(t / 25.0) * 100.0)); @@ -89,11 +85,11 @@ fn draw_example_collection( // Circles have 32 line-segments by default. // You may want to increase this for larger circles. my_gizmos - .circle_2d(Isometry2d::from_translation(Vec2::ZERO), 300., NAVY) + .circle_2d(Isometry2d::IDENTITY, 300., NAVY) .resolution(64); my_gizmos.ellipse_2d( - Isometry2d::new(Vec2::ZERO, Rot2::radians(time.elapsed_seconds() % TAU)), + Rot2::radians(time.elapsed_seconds() % TAU), Vec2::new(100., 200.), YELLOW_GREEN, ); @@ -101,7 +97,7 @@ fn draw_example_collection( // Arcs default resolution is linearly interpolated between // 1 and 32, using the arc length as scalar. my_gizmos.arc_2d( - Isometry2d::from_rotation(Rot2::radians(sin_t_scaled / 10.)), + Rot2::radians(sin_t_scaled / 10.), FRAC_PI_2, 310., ORANGE_RED, diff --git a/examples/gizmos/3d_gizmos.rs b/examples/gizmos/3d_gizmos.rs index 6da3db4110e2f..7a95d6e9a50c3 100644 --- a/examples/gizmos/3d_gizmos.rs +++ b/examples/gizmos/3d_gizmos.rs @@ -78,19 +78,19 @@ fn draw_example_collection( time: Res