//! 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/// # Example33/// ```34/// # use bevy_gizmos::prelude::*;35/// # use bevy_math::prelude::*;36/// # use bevy_color::palettes::basic::{RED, GREEN};37/// fn system(mut gizmos: Gizmos) {38/// gizmos.ellipse(Isometry3d::IDENTITY, Vec2::new(1., 2.), GREEN);39///40/// // Ellipses have 32 line-segments by default.41/// // You may want to increase this for larger ellipses.42/// gizmos43/// .ellipse(Isometry3d::IDENTITY, Vec2::new(5., 1.), RED)44/// .resolution(64);45/// }46/// # bevy_ecs::system::assert_is_system(system);47/// ```48#[inline]49pub fn ellipse(50&mut self,51isometry: impl Into<Isometry3d>,52half_size: Vec2,53color: impl Into<Color>,54) -> EllipseBuilder<'_, Config, Clear> {55EllipseBuilder {56gizmos: self,57isometry: isometry.into(),58half_size,59color: color.into(),60resolution: DEFAULT_CIRCLE_RESOLUTION,61}62}6364/// Draw an ellipse in 2D with the given `isometry` applied.65///66/// If `isometry == Isometry2d::IDENTITY` then67///68/// - the center is at `Vec2::ZERO`69/// - the `half_sizes` are aligned with the `Vec2::X` and `Vec2::Y` axes.70///71/// # Example72/// ```73/// # use bevy_gizmos::prelude::*;74/// # use bevy_math::prelude::*;75/// # use bevy_color::palettes::basic::{RED, GREEN};76/// fn system(mut gizmos: Gizmos) {77/// gizmos.ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(2., 1.), GREEN);78///79/// // Ellipses have 32 line-segments by default.80/// // You may want to increase this for larger ellipses.81/// gizmos82/// .ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(5., 1.), RED)83/// .resolution(64);84/// }85/// # bevy_ecs::system::assert_is_system(system);86/// ```87#[inline]88pub fn ellipse_2d(89&mut self,90isometry: impl Into<Isometry2d>,91half_size: Vec2,92color: impl Into<Color>,93) -> Ellipse2dBuilder<'_, Config, Clear> {94Ellipse2dBuilder {95gizmos: self,96isometry: isometry.into(),97half_size,98color: color.into(),99resolution: DEFAULT_CIRCLE_RESOLUTION,100}101}102103/// Draw a circle in 3D with the given `isometry` applied.104///105/// If `isometry == Isometry3d::IDENTITY` then106///107/// - the center is at `Vec3::ZERO`108/// - the radius is aligned with the `Vec3::X` and `Vec3::Y` axes.109///110/// # Example111/// ```112/// # use bevy_gizmos::prelude::*;113/// # use bevy_math::prelude::*;114/// # use bevy_color::palettes::basic::{RED, GREEN};115/// fn system(mut gizmos: Gizmos) {116/// gizmos.circle(Isometry3d::IDENTITY, 1., GREEN);117///118/// // Circles have 32 line-segments by default.119/// // You may want to increase this for larger circles.120/// gizmos121/// .circle(Isometry3d::IDENTITY, 5., RED)122/// .resolution(64);123/// }124/// # bevy_ecs::system::assert_is_system(system);125/// ```126#[inline]127pub fn circle(128&mut self,129isometry: impl Into<Isometry3d>,130radius: f32,131color: impl Into<Color>,132) -> EllipseBuilder<'_, Config, Clear> {133EllipseBuilder {134gizmos: self,135isometry: isometry.into(),136half_size: Vec2::splat(radius),137color: color.into(),138resolution: DEFAULT_CIRCLE_RESOLUTION,139}140}141142/// Draw a circle in 2D with the given `isometry` applied.143///144/// If `isometry == Isometry2d::IDENTITY` then145///146/// - the center is at `Vec2::ZERO`147/// - the radius is aligned with the `Vec2::X` and `Vec2::Y` axes.148///149/// # Example150/// ```151/// # use bevy_gizmos::prelude::*;152/// # use bevy_math::prelude::*;153/// # use bevy_color::palettes::basic::{RED, GREEN};154/// fn system(mut gizmos: Gizmos) {155/// gizmos.circle_2d(Isometry2d::IDENTITY, 1., GREEN);156///157/// // Circles have 32 line-segments by default.158/// // You may want to increase this for larger circles.159/// gizmos160/// .circle_2d(Isometry2d::IDENTITY, 5., RED)161/// .resolution(64);162/// }163/// # bevy_ecs::system::assert_is_system(system);164/// ```165#[inline]166pub fn circle_2d(167&mut self,168isometry: impl Into<Isometry2d>,169radius: f32,170color: impl Into<Color>,171) -> Ellipse2dBuilder<'_, Config, Clear> {172Ellipse2dBuilder {173gizmos: self,174isometry: isometry.into(),175half_size: Vec2::splat(radius),176color: color.into(),177resolution: DEFAULT_CIRCLE_RESOLUTION,178}179}180181/// Draw a wireframe sphere in 3D made out of 3 circles around the axes with the given182/// `isometry` applied.183///184/// If `isometry == Isometry3d::IDENTITY` then185///186/// - the center is at `Vec3::ZERO`187/// - the 3 circles are in the XY, YZ and XZ planes.188///189/// # Example190/// ```191/// # use bevy_gizmos::prelude::*;192/// # use bevy_math::prelude::*;193/// # use bevy_color::Color;194/// fn system(mut gizmos: Gizmos) {195/// gizmos.sphere(Isometry3d::IDENTITY, 1., Color::BLACK);196///197/// // Each circle has 32 line-segments by default.198/// // You may want to increase this for larger spheres.199/// gizmos200/// .sphere(Isometry3d::IDENTITY, 5., Color::BLACK)201/// .resolution(64);202/// }203/// # bevy_ecs::system::assert_is_system(system);204/// ```205#[inline]206pub fn sphere(207&mut self,208isometry: impl Into<Isometry3d>,209radius: f32,210color: impl Into<Color>,211) -> SphereBuilder<'_, Config, Clear> {212SphereBuilder {213gizmos: self,214radius,215isometry: isometry.into(),216color: color.into(),217resolution: DEFAULT_CIRCLE_RESOLUTION,218}219}220}221222/// A builder returned by [`GizmoBuffer::ellipse`].223pub struct EllipseBuilder<'a, Config, Clear>224where225Config: GizmoConfigGroup,226Clear: 'static + Send + Sync,227{228gizmos: &'a mut GizmoBuffer<Config, Clear>,229isometry: Isometry3d,230half_size: Vec2,231color: Color,232resolution: u32,233}234235impl<Config, Clear> EllipseBuilder<'_, Config, Clear>236where237Config: GizmoConfigGroup,238Clear: 'static + Send + Sync,239{240/// Set the number of lines used to approximate the geometry of this ellipse.241pub fn resolution(mut self, resolution: u32) -> Self {242self.resolution = resolution;243self244}245}246247impl<Config, Clear> Drop for EllipseBuilder<'_, Config, Clear>248where249Config: GizmoConfigGroup,250Clear: 'static + Send + Sync,251{252fn drop(&mut self) {253if !self.gizmos.enabled {254return;255}256257let positions = ellipse_inner(self.half_size, self.resolution)258.map(|vec2| self.isometry * vec2.extend(0.));259self.gizmos.linestrip(positions, self.color);260}261}262263/// A builder returned by [`GizmoBuffer::ellipse_2d`].264pub struct Ellipse2dBuilder<'a, Config, Clear>265where266Config: GizmoConfigGroup,267Clear: 'static + Send + Sync,268{269gizmos: &'a mut GizmoBuffer<Config, Clear>,270isometry: Isometry2d,271half_size: Vec2,272color: Color,273resolution: u32,274}275276impl<Config, Clear> Ellipse2dBuilder<'_, Config, Clear>277where278Config: GizmoConfigGroup,279Clear: 'static + Send + Sync,280{281/// Set the number of line-segments used to approximate the geometry of this ellipse.282pub fn resolution(mut self, resolution: u32) -> Self {283self.resolution = resolution;284self285}286}287288impl<Config, Clear> Drop for Ellipse2dBuilder<'_, Config, Clear>289where290Config: GizmoConfigGroup,291Clear: 'static + Send + Sync,292{293/// Set the number of line-segments for this ellipse.294fn drop(&mut self) {295if !self.gizmos.enabled {296return;297};298299let positions =300ellipse_inner(self.half_size, self.resolution).map(|vec2| self.isometry * vec2);301self.gizmos.linestrip_2d(positions, self.color);302}303}304305/// A builder returned by [`GizmoBuffer::sphere`].306pub struct SphereBuilder<'a, Config, Clear>307where308Config: GizmoConfigGroup,309Clear: 'static + Send + Sync,310{311gizmos: &'a mut GizmoBuffer<Config, Clear>,312313// Radius of the sphere314radius: f32,315316isometry: Isometry3d,317// Color of the sphere318color: Color,319320// Number of line-segments used to approximate the sphere geometry321resolution: u32,322}323324impl<Config, Clear> SphereBuilder<'_, Config, Clear>325where326Config: GizmoConfigGroup,327Clear: 'static + Send + Sync,328{329/// Set the number of line-segments used to approximate the sphere geometry.330pub fn resolution(mut self, resolution: u32) -> Self {331self.resolution = resolution;332self333}334}335336impl<Config, Clear> Drop for SphereBuilder<'_, Config, Clear>337where338Config: GizmoConfigGroup,339Clear: 'static + Send + Sync,340{341fn drop(&mut self) {342if !self.gizmos.enabled {343return;344}345346// draws one great circle around each of the local axes347Vec3::AXES.into_iter().for_each(|axis| {348let axis_rotation = Isometry3d::from_rotation(Quat::from_rotation_arc(Vec3::Z, axis));349self.gizmos350.circle(self.isometry * axis_rotation, self.radius, self.color)351.resolution(self.resolution);352});353}354}355356357