From 4b996c75aba325e004d18903b83ab6767688cea5 Mon Sep 17 00:00:00 2001 From: Olle Lukowski <63189113+Olle-Lukowski@users.noreply.github.com> Date: Sat, 1 Jun 2024 14:30:34 +0200 Subject: [PATCH] Implemented GizmoPrimitive2d for `Arc2d`, `CircularSegment`, `CircularSector`, and make arc_2d use counter-clockwise angle. (#13610) # Objective Fixes #13606. Also Fixes #13614. ## Solution Added the missing trait impls, and made `gizmos.arc_2d()` work with a counter-clockwise angle. ## Testing - Updated the render_primitives example, and it works. --- crates/bevy_gizmos/src/arcs.rs | 8 +- crates/bevy_gizmos/src/primitives/dim2.rs | 123 ++++++++++++++++++++-- examples/math/render_primitives.rs | 38 ++++++- 3 files changed, 156 insertions(+), 13 deletions(-) diff --git a/crates/bevy_gizmos/src/arcs.rs b/crates/bevy_gizmos/src/arcs.rs index 427d96a4e5..5505f667e5 100644 --- a/crates/bevy_gizmos/src/arcs.rs +++ b/crates/bevy_gizmos/src/arcs.rs @@ -22,7 +22,7 @@ where /// /// # Arguments /// - `position` sets the center of this circle. - /// - `direction_angle` sets the clockwise angle in radians between `Vec2::Y` and + /// - `direction_angle` sets the counter-clockwise angle in radians between `Vec2::Y` and /// the vector from `position` to the midpoint of the arc. /// - `arc_angle` sets the length of this arc, in radians. /// - `radius` controls the distance from `position` to this arc, and thus its curvature. @@ -128,8 +128,10 @@ fn arc_2d_inner( (0..resolution + 1).map(move |i| { let start = direction_angle - arc_angle / 2.; - let angle = start + (i as f32 * (arc_angle / resolution as f32)); - Vec2::from(angle.sin_cos()) * radius + let angle = + start + (i as f32 * (arc_angle / resolution as f32)) + std::f32::consts::FRAC_PI_2; + + Vec2::new(angle.cos(), angle.sin()) * radius }) } diff --git a/crates/bevy_gizmos/src/primitives/dim2.rs b/crates/bevy_gizmos/src/primitives/dim2.rs index 651fd6cef0..1c0b699e60 100644 --- a/crates/bevy_gizmos/src/primitives/dim2.rs +++ b/crates/bevy_gizmos/src/primitives/dim2.rs @@ -6,8 +6,9 @@ use super::helpers::*; use bevy_color::Color; use bevy_math::primitives::{ - Annulus, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Ellipse, Line2d, Plane2d, Polygon, - Polyline2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d, + Annulus, Arc2d, BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, CircularSector, + CircularSegment, Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Primitive2d, Rectangle, + RegularPolygon, Rhombus, Segment2d, Triangle2d, }; use bevy_math::{Dir2, Mat2, Vec2}; @@ -64,6 +65,36 @@ where } } +// arc 2d + +impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ + type Output<'a> = () where Self: 'a; + + fn primitive_2d( + &mut self, + primitive: &Arc2d, + position: Vec2, + angle: f32, + color: impl Into, + ) -> Self::Output<'_> { + if !self.enabled { + return; + } + + self.arc_2d( + position, + angle, + primitive.half_angle * 2.0, + primitive.radius, + color, + ); + } +} + // circle 2d impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> @@ -88,6 +119,85 @@ where } } +// circular sector 2d + +impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ + type Output<'a> = () where Self: 'a; + + fn primitive_2d( + &mut self, + primitive: &CircularSector, + position: Vec2, + angle: f32, + color: impl Into, + ) -> Self::Output<'_> { + if !self.enabled { + return; + } + + let color = color.into(); + + // we need to draw the arc part of the sector, and the two lines connecting the arc and the center + self.arc_2d( + position, + angle, + primitive.arc.half_angle * 2.0, + primitive.arc.radius, + color, + ); + + let start = position + + primitive.arc.radius * Mat2::from_angle(angle - primitive.arc.half_angle) * Vec2::Y; + let end = position + + primitive.arc.radius * Mat2::from_angle(angle + primitive.arc.half_angle) * Vec2::Y; + self.line_2d(position, start, color); + self.line_2d(position, end, color); + } +} + +// circular segment 2d + +impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ + type Output<'a> = () where Self: 'a; + + fn primitive_2d( + &mut self, + primitive: &CircularSegment, + position: Vec2, + angle: f32, + color: impl Into, + ) -> Self::Output<'_> { + if !self.enabled { + return; + } + + let color = color.into(); + + // we need to draw the arc part of the segment, and the line connecting the two ends + self.arc_2d( + position, + angle, + primitive.arc.half_angle * 2.0, + primitive.arc.radius, + color, + ); + + let start = position + + primitive.arc.radius * Mat2::from_angle(angle - primitive.arc.half_angle) * Vec2::Y; + let end = position + + primitive.arc.radius * Mat2::from_angle(angle + primitive.arc.half_angle) * Vec2::Y; + self.line_2d(end, start, color); + } +} + // ellipse 2d impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> @@ -192,8 +302,6 @@ where return; } - let rotation = Mat2::from_angle(angle); - // transform points from the reference unit square to capsule "rectangle" let [top_left, top_right, bottom_left, bottom_right, top_center, bottom_center] = [ [-1.0, 1.0], @@ -215,11 +323,8 @@ where self.line_2d(bottom_left, top_left, polymorphic_color); self.line_2d(bottom_right, top_right, polymorphic_color); - // if the capsule is rotated we have to start the arc at a different offset angle, - // calculate that here - let angle_offset = (rotation * Vec2::Y).angle_between(Vec2::Y); - let start_angle_top = angle_offset; - let start_angle_bottom = PI + angle_offset; + let start_angle_top = angle; + let start_angle_bottom = PI + angle; // draw arcs self.arc_2d( diff --git a/examples/math/render_primitives.rs b/examples/math/render_primitives.rs index ae789fff08..30ba73380e 100644 --- a/examples/math/render_primitives.rs +++ b/examples/math/render_primitives.rs @@ -85,6 +85,9 @@ enum PrimitiveSelected { ConicalFrustum, Torus, Tetrahedron, + Arc, + CircularSector, + CircularSegment, } impl std::fmt::Display for PrimitiveSelected { @@ -99,7 +102,7 @@ impl std::fmt::Display for PrimitiveSelected { } impl PrimitiveSelected { - const ALL: [Self; 16] = [ + const ALL: [Self; 19] = [ Self::RectangleAndCuboid, Self::CircleAndSphere, Self::Ellipse, @@ -116,6 +119,9 @@ impl PrimitiveSelected { Self::ConicalFrustum, Self::Torus, Self::Tetrahedron, + Self::Arc, + Self::CircularSector, + Self::CircularSegment, ]; fn next(self) -> Self { @@ -269,6 +275,25 @@ const TETRAHEDRON: Tetrahedron = Tetrahedron { ], }; +const ARC: Arc2d = Arc2d { + radius: BIG_2D, + half_angle: std::f32::consts::FRAC_PI_4, +}; + +const CIRCULAR_SECTOR: CircularSector = CircularSector { + arc: Arc2d { + radius: BIG_2D, + half_angle: std::f32::consts::FRAC_PI_4, + }, +}; + +const CIRCULAR_SEGMENT: CircularSegment = CircularSegment { + arc: Arc2d { + radius: BIG_2D, + half_angle: std::f32::consts::FRAC_PI_4, + }, +}; + fn setup_cameras(mut commands: Commands) { let start_in_2d = true; let make_camera = |is_active| Camera { @@ -446,6 +471,13 @@ fn draw_gizmos_2d(mut gizmos: Gizmos, state: Res>, time PrimitiveSelected::ConicalFrustum => {} PrimitiveSelected::Torus => gizmos.primitive_2d(&ANNULUS, POSITION, angle, color), PrimitiveSelected::Tetrahedron => {} + PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, POSITION, angle, color), + PrimitiveSelected::CircularSector => { + gizmos.primitive_2d(&CIRCULAR_SECTOR, POSITION, angle, color); + } + PrimitiveSelected::CircularSegment => { + gizmos.primitive_2d(&CIRCULAR_SEGMENT, POSITION, angle, color); + } } } @@ -675,5 +707,9 @@ fn draw_gizmos_3d(mut gizmos: Gizmos, state: Res>, time PrimitiveSelected::Tetrahedron => { gizmos.primitive_3d(&TETRAHEDRON, POSITION, rotation, color); } + + PrimitiveSelected::Arc => {} + PrimitiveSelected::CircularSector => {} + PrimitiveSelected::CircularSegment => {} } }