diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 10e398d095..9d734dc96b 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -459,6 +459,51 @@ impl<'s> Gizmos<'s> { } } + /// Draw an arc, which is a part of the circumference of a circle. + /// + /// # Arguments + /// - `position` sets the center of this circle. + /// - `radius` controls the distance from `position` to this arc, and thus its curvature. + /// - `direction_angle` sets the angle in radians between `position` and the midpoint of the arc. + /// -`arc_angle` sets the length of this arc, in radians. + /// + /// # Example + /// ``` + /// # use bevy_gizmos::prelude::*; + /// # use bevy_render::prelude::*; + /// # use bevy_math::prelude::*; + /// # use std::f32::consts::PI; + /// fn system(mut gizmos: Gizmos) { + /// gizmos.arc_2d(Vec2::ZERO, 0., PI / 4., 1., Color::GREEN); + /// + /// // Arcs have 32 line-segments by default. + /// // You may want to increase this for larger arcs. + /// gizmos + /// .arc_2d(Vec2::ZERO, 0., PI / 4., 5., Color::RED) + /// .segments(64); + /// } + /// # bevy_ecs::system::assert_is_system(system); + /// ``` + #[inline] + pub fn arc_2d( + &mut self, + position: Vec2, + direction_angle: f32, + arc_angle: f32, + radius: f32, + color: Color, + ) -> Arc2dBuilder<'_, 's> { + Arc2dBuilder { + gizmos: self, + position, + direction_angle, + arc_angle, + radius, + color, + segments: None, + } + } + /// Draw a wireframe rectangle. /// /// # Example @@ -589,6 +634,54 @@ impl Drop for Circle2dBuilder<'_, '_> { } } +/// A builder returned by [`Gizmos::arc_2d`]. +pub struct Arc2dBuilder<'a, 's> { + gizmos: &'a mut Gizmos<'s>, + position: Vec2, + direction_angle: f32, + arc_angle: f32, + radius: f32, + color: Color, + segments: Option, +} + +impl Arc2dBuilder<'_, '_> { + /// Set the number of line-segements for this arc. + pub fn segments(mut self, segments: usize) -> Self { + self.segments = Some(segments); + self + } +} + +impl Drop for Arc2dBuilder<'_, '_> { + fn drop(&mut self) { + let segments = match self.segments { + Some(segments) => segments, + // Do a linear interpolation between 1 and `DEFAULT_CIRCLE_SEGMENTS` + // using the arc angle as scalar. + None => ((self.arc_angle.abs() / TAU) * DEFAULT_CIRCLE_SEGMENTS as f32).ceil() as usize, + }; + + let positions = arc_inner(self.direction_angle, self.arc_angle, self.radius, segments) + .map(|vec2| (vec2 + self.position)); + self.gizmos.linestrip_2d(positions, self.color); + } +} + +fn arc_inner( + direction_angle: f32, + arc_angle: f32, + radius: f32, + segments: usize, +) -> impl Iterator { + (0..segments + 1).map(move |i| { + let start = direction_angle - arc_angle / 2.; + + let angle = start + (i as f32 * (arc_angle / segments as f32)); + Vec2::from(angle.sin_cos()) * radius + }) +} + fn circle_inner(radius: f32, segments: usize) -> impl Iterator { (0..segments + 1).map(move |i| { let angle = i as f32 * TAU / segments as f32; diff --git a/examples/2d/2d_gizmos.rs b/examples/2d/2d_gizmos.rs index 00d2aaae6f..5cd7014967 100644 --- a/examples/2d/2d_gizmos.rs +++ b/examples/2d/2d_gizmos.rs @@ -1,5 +1,7 @@ //! This example demonstrates Bevy's immediate mode drawing API intended for visual debugging. +use std::f32::consts::PI; + use bevy::prelude::*; fn main() { @@ -38,4 +40,8 @@ fn system(mut gizmos: Gizmos, time: Res