//! Additional [`GizmoBuffer`] Functions -- Circles1//!2//! Includes the implementation of [`GizmoBuffer::circle`] and [`GizmoBuffer::circle_2d`],3//! and assorted support items.45use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup};6use bevy_color::Color;7use bevy_math::{ops, Isometry2d, Isometry3d, Quat, Vec2, Vec3};8use core::f32::consts::TAU;910pub(crate) const DEFAULT_CIRCLE_RESOLUTION: u32 = 32;1112fn ellipse_inner(half_size: Vec2, resolution: u32) -> impl Iterator<Item = Vec2> {13(0..resolution + 1).map(move |i| {14let angle = i as f32 * TAU / resolution as f32;15let (x, y) = ops::sin_cos(angle);16Vec2::new(x, y) * half_size17})18}1920impl<Config, Clear> GizmoBuffer<Config, Clear>21where22Config: GizmoConfigGroup,23Clear: 'static + Send + Sync,24{25/// Draw an ellipse in 3D with the given `isometry` applied.26///27/// If `isometry == Isometry3d::IDENTITY` then28///29/// - the center is at `Vec3::ZERO`30/// - the `half_sizes` are aligned with the `Vec3::X` and `Vec3::Y` axes.31///32/// This should be called for each frame the ellipse needs to be rendered.33///34/// # Example35/// ```36/// # use bevy_gizmos::prelude::*;37/// # use bevy_math::prelude::*;38/// # use bevy_color::palettes::basic::{RED, GREEN};39/// fn system(mut gizmos: Gizmos) {40/// gizmos.ellipse(Isometry3d::IDENTITY, Vec2::new(1., 2.), GREEN);41///42/// // Ellipses have 32 line-segments by default.43/// // You may want to increase this for larger ellipses.44/// gizmos45/// .ellipse(Isometry3d::IDENTITY, Vec2::new(5., 1.), RED)46/// .resolution(64);47/// }48/// # bevy_ecs::system::assert_is_system(system);49/// ```50#[inline]51pub fn ellipse(52&mut self,53isometry: impl Into<Isometry3d>,54half_size: Vec2,55color: impl Into<Color>,56) -> EllipseBuilder<'_, Config, Clear> {57EllipseBuilder {58gizmos: self,59isometry: isometry.into(),60half_size,61color: color.into(),62resolution: DEFAULT_CIRCLE_RESOLUTION,63}64}6566/// Draw an ellipse in 2D with the given `isometry` applied.67///68/// If `isometry == Isometry2d::IDENTITY` then69///70/// - the center is at `Vec2::ZERO`71/// - the `half_sizes` are aligned with the `Vec2::X` and `Vec2::Y` axes.72///73/// This should be called for each frame the ellipse needs to be rendered.74///75/// # Example76/// ```77/// # use bevy_gizmos::prelude::*;78/// # use bevy_math::prelude::*;79/// # use bevy_color::palettes::basic::{RED, GREEN};80/// fn system(mut gizmos: Gizmos) {81/// gizmos.ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(2., 1.), GREEN);82///83/// // Ellipses have 32 line-segments by default.84/// // You may want to increase this for larger ellipses.85/// gizmos86/// .ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(5., 1.), RED)87/// .resolution(64);88/// }89/// # bevy_ecs::system::assert_is_system(system);90/// ```91#[inline]92pub fn ellipse_2d(93&mut self,94isometry: impl Into<Isometry2d>,95half_size: Vec2,96color: impl Into<Color>,97) -> Ellipse2dBuilder<'_, Config, Clear> {98Ellipse2dBuilder {99gizmos: self,100isometry: isometry.into(),101half_size,102color: color.into(),103resolution: DEFAULT_CIRCLE_RESOLUTION,104}105}106107/// Draw a circle in 3D with the given `isometry` applied.108///109/// If `isometry == Isometry3d::IDENTITY` then110///111/// - the center is at `Vec3::ZERO`112/// - the radius is aligned with the `Vec3::X` and `Vec3::Y` axes.113///114/// # Example115/// ```116/// # use bevy_gizmos::prelude::*;117/// # use bevy_math::prelude::*;118/// # use bevy_color::palettes::basic::{RED, GREEN};119/// fn system(mut gizmos: Gizmos) {120/// gizmos.circle(Isometry3d::IDENTITY, 1., GREEN);121///122/// // Circles have 32 line-segments by default.123/// // You may want to increase this for larger circles.124/// gizmos125/// .circle(Isometry3d::IDENTITY, 5., RED)126/// .resolution(64);127/// }128/// # bevy_ecs::system::assert_is_system(system);129/// ```130#[inline]131pub fn circle(132&mut self,133isometry: impl Into<Isometry3d>,134radius: f32,135color: impl Into<Color>,136) -> EllipseBuilder<'_, Config, Clear> {137EllipseBuilder {138gizmos: self,139isometry: isometry.into(),140half_size: Vec2::splat(radius),141color: color.into(),142resolution: DEFAULT_CIRCLE_RESOLUTION,143}144}145146/// Draw a circle in 2D with the given `isometry` applied.147///148/// If `isometry == Isometry2d::IDENTITY` then149///150/// - the center is at `Vec2::ZERO`151/// - the radius is aligned with the `Vec2::X` and `Vec2::Y` axes.152///153/// This should be called for each frame the circle needs to be rendered.154///155/// # Example156/// ```157/// # use bevy_gizmos::prelude::*;158/// # use bevy_math::prelude::*;159/// # use bevy_color::palettes::basic::{RED, GREEN};160/// fn system(mut gizmos: Gizmos) {161/// gizmos.circle_2d(Isometry2d::IDENTITY, 1., GREEN);162///163/// // Circles have 32 line-segments by default.164/// // You may want to increase this for larger circles.165/// gizmos166/// .circle_2d(Isometry2d::IDENTITY, 5., RED)167/// .resolution(64);168/// }169/// # bevy_ecs::system::assert_is_system(system);170/// ```171#[inline]172pub fn circle_2d(173&mut self,174isometry: impl Into<Isometry2d>,175radius: f32,176color: impl Into<Color>,177) -> Ellipse2dBuilder<'_, Config, Clear> {178Ellipse2dBuilder {179gizmos: self,180isometry: isometry.into(),181half_size: Vec2::splat(radius),182color: color.into(),183resolution: DEFAULT_CIRCLE_RESOLUTION,184}185}186187/// Draw a wireframe sphere in 3D made out of 3 circles around the axes with the given188/// `isometry` applied.189///190/// If `isometry == Isometry3d::IDENTITY` then191///192/// - the center is at `Vec3::ZERO`193/// - the 3 circles are in the XY, YZ and XZ planes.194///195/// This should be called for each frame the sphere needs to be rendered.196///197/// # Example198/// ```199/// # use bevy_gizmos::prelude::*;200/// # use bevy_math::prelude::*;201/// # use bevy_color::Color;202/// fn system(mut gizmos: Gizmos) {203/// gizmos.sphere(Isometry3d::IDENTITY, 1., Color::BLACK);204///205/// // Each circle has 32 line-segments by default.206/// // You may want to increase this for larger spheres.207/// gizmos208/// .sphere(Isometry3d::IDENTITY, 5., Color::BLACK)209/// .resolution(64);210/// }211/// # bevy_ecs::system::assert_is_system(system);212/// ```213#[inline]214pub fn sphere(215&mut self,216isometry: impl Into<Isometry3d>,217radius: f32,218color: impl Into<Color>,219) -> SphereBuilder<'_, Config, Clear> {220SphereBuilder {221gizmos: self,222radius,223isometry: isometry.into(),224color: color.into(),225resolution: DEFAULT_CIRCLE_RESOLUTION,226}227}228}229230/// A builder returned by [`GizmoBuffer::ellipse`].231pub struct EllipseBuilder<'a, Config, Clear>232where233Config: GizmoConfigGroup,234Clear: 'static + Send + Sync,235{236gizmos: &'a mut GizmoBuffer<Config, Clear>,237isometry: Isometry3d,238half_size: Vec2,239color: Color,240resolution: u32,241}242243impl<Config, Clear> EllipseBuilder<'_, Config, Clear>244where245Config: GizmoConfigGroup,246Clear: 'static + Send + Sync,247{248/// Set the number of lines used to approximate the geometry of this ellipse.249pub fn resolution(mut self, resolution: u32) -> Self {250self.resolution = resolution;251self252}253}254255impl<Config, Clear> Drop for EllipseBuilder<'_, Config, Clear>256where257Config: GizmoConfigGroup,258Clear: 'static + Send + Sync,259{260fn drop(&mut self) {261if !self.gizmos.enabled {262return;263}264265let positions = ellipse_inner(self.half_size, self.resolution)266.map(|vec2| self.isometry * vec2.extend(0.));267self.gizmos.linestrip(positions, self.color);268}269}270271/// A builder returned by [`GizmoBuffer::ellipse_2d`].272pub struct Ellipse2dBuilder<'a, Config, Clear>273where274Config: GizmoConfigGroup,275Clear: 'static + Send + Sync,276{277gizmos: &'a mut GizmoBuffer<Config, Clear>,278isometry: Isometry2d,279half_size: Vec2,280color: Color,281resolution: u32,282}283284impl<Config, Clear> Ellipse2dBuilder<'_, Config, Clear>285where286Config: GizmoConfigGroup,287Clear: 'static + Send + Sync,288{289/// Set the number of line-segments used to approximate the geometry of this ellipse.290pub fn resolution(mut self, resolution: u32) -> Self {291self.resolution = resolution;292self293}294}295296impl<Config, Clear> Drop for Ellipse2dBuilder<'_, Config, Clear>297where298Config: GizmoConfigGroup,299Clear: 'static + Send + Sync,300{301/// Set the number of line-segments for this ellipse.302fn drop(&mut self) {303if !self.gizmos.enabled {304return;305};306307let positions =308ellipse_inner(self.half_size, self.resolution).map(|vec2| self.isometry * vec2);309self.gizmos.linestrip_2d(positions, self.color);310}311}312313/// A builder returned by [`GizmoBuffer::sphere`].314pub struct SphereBuilder<'a, Config, Clear>315where316Config: GizmoConfigGroup,317Clear: 'static + Send + Sync,318{319gizmos: &'a mut GizmoBuffer<Config, Clear>,320321// Radius of the sphere322radius: f32,323324isometry: Isometry3d,325// Color of the sphere326color: Color,327328// Number of line-segments used to approximate the sphere geometry329resolution: u32,330}331332impl<Config, Clear> SphereBuilder<'_, Config, Clear>333where334Config: GizmoConfigGroup,335Clear: 'static + Send + Sync,336{337/// Set the number of line-segments used to approximate the sphere geometry.338pub fn resolution(mut self, resolution: u32) -> Self {339self.resolution = resolution;340self341}342}343344impl<Config, Clear> Drop for SphereBuilder<'_, Config, Clear>345where346Config: GizmoConfigGroup,347Clear: 'static + Send + Sync,348{349fn drop(&mut self) {350if !self.gizmos.enabled {351return;352}353354// draws one great circle around each of the local axes355Vec3::AXES.into_iter().for_each(|axis| {356let axis_rotation = Isometry3d::from_rotation(Quat::from_rotation_arc(Vec3::Z, axis));357self.gizmos358.circle(self.isometry * axis_rotation, self.radius, self.color)359.resolution(self.resolution);360});361}362}363364365