//! A module for the [`Gizmos`] [`SystemParam`].12use core::{3iter,4marker::PhantomData,5mem,6ops::{Deref, DerefMut},7};89use bevy_color::{Color, LinearRgba};10use bevy_ecs::{11change_detection::Tick,12query::FilteredAccessSet,13resource::Resource,14system::{15Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam,16SystemParamValidationError,17},18world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},19};20use bevy_math::{bounding::Aabb3d, Isometry2d, Isometry3d, Vec2, Vec3};21use bevy_reflect::{std_traits::ReflectDefault, Reflect};22use bevy_transform::TransformPoint;23use bevy_utils::default;2425use crate::{26config::{DefaultGizmoConfigGroup, GizmoConfigGroup, GizmoConfigStore},27prelude::GizmoConfig,28};2930/// Storage of gizmo primitives.31#[derive(Resource)]32pub struct GizmoStorage<Config, Clear> {33pub(crate) list_positions: Vec<Vec3>,34pub(crate) list_colors: Vec<LinearRgba>,35pub(crate) strip_positions: Vec<Vec3>,36pub(crate) strip_colors: Vec<LinearRgba>,37marker: PhantomData<(Config, Clear)>,38}3940impl<Config, Clear> Default for GizmoStorage<Config, Clear> {41fn default() -> Self {42Self {43list_positions: default(),44list_colors: default(),45strip_positions: default(),46strip_colors: default(),47marker: PhantomData,48}49}50}5152impl<Config, Clear> GizmoStorage<Config, Clear>53where54Config: GizmoConfigGroup,55Clear: 'static + Send + Sync,56{57/// Combine the other gizmo storage with this one.58pub fn append_storage<OtherConfig, OtherClear>(59&mut self,60other: &GizmoStorage<OtherConfig, OtherClear>,61) {62self.list_positions.extend(other.list_positions.iter());63self.list_colors.extend(other.list_colors.iter());64self.strip_positions.extend(other.strip_positions.iter());65self.strip_colors.extend(other.strip_colors.iter());66}6768pub(crate) fn swap<OtherConfig, OtherClear>(69&mut self,70other: &mut GizmoStorage<OtherConfig, OtherClear>,71) {72mem::swap(&mut self.list_positions, &mut other.list_positions);73mem::swap(&mut self.list_colors, &mut other.list_colors);74mem::swap(&mut self.strip_positions, &mut other.strip_positions);75mem::swap(&mut self.strip_colors, &mut other.strip_colors);76}7778/// Clear this gizmo storage of any requested gizmos.79pub fn clear(&mut self) {80self.list_positions.clear();81self.list_colors.clear();82self.strip_positions.clear();83self.strip_colors.clear();84}85}8687/// Swap buffer for a specific clearing context.88///89/// This is to stash/store the default/requested gizmos so another context can90/// be substituted for that duration.91pub struct Swap<Clear>(PhantomData<Clear>);9293/// A [`SystemParam`] for drawing gizmos.94///95/// They are drawn in immediate mode, which means they will be rendered only for96/// the frames, or ticks when in [`FixedMain`](bevy_app::FixedMain), in which97/// they are spawned.98///99/// A system in [`Main`](bevy_app::Main) will be cleared each rendering100/// frame, while a system in [`FixedMain`](bevy_app::FixedMain) will be101/// cleared each time the [`RunFixedMainLoop`](bevy_app::RunFixedMainLoop)102/// schedule is run.103///104/// Gizmos should be spawned before the [`Last`](bevy_app::Last) schedule105/// to ensure they are drawn.106///107/// To set up your own clearing context (useful for custom scheduling similar108/// to [`FixedMain`](bevy_app::FixedMain)):109///110/// ```111/// use bevy_gizmos::{prelude::*, *, gizmos::GizmoStorage};112/// # use bevy_app::prelude::*;113/// # use bevy_ecs::{schedule::ScheduleLabel, prelude::*};114/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]115/// # struct StartOfMyContext;116/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]117/// # struct EndOfMyContext;118/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]119/// # struct StartOfRun;120/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]121/// # struct EndOfRun;122/// # struct MyContext;123/// struct ClearContextSetup;124/// impl Plugin for ClearContextSetup {125/// fn build(&self, app: &mut App) {126/// app.init_resource::<GizmoStorage<DefaultGizmoConfigGroup, MyContext>>()127/// // Make sure this context starts/ends cleanly if inside another context. E.g. it128/// // should start after the parent context starts and end after the parent context ends.129/// .add_systems(StartOfMyContext, start_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)130/// // If not running multiple times, put this with [`start_gizmo_context`].131/// .add_systems(StartOfRun, clear_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)132/// // If not running multiple times, put this with [`end_gizmo_context`].133/// .add_systems(EndOfRun, collect_requested_gizmos::<DefaultGizmoConfigGroup, MyContext>)134/// .add_systems(EndOfMyContext, end_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)135/// .add_systems(136/// Last,137/// propagate_gizmos::<DefaultGizmoConfigGroup, MyContext>.before(GizmoMeshSystems),138/// );139/// }140/// }141/// ```142pub struct Gizmos<'w, 's, Config = DefaultGizmoConfigGroup, Clear = ()>143where144Config: GizmoConfigGroup,145Clear: 'static + Send + Sync,146{147buffer: Deferred<'s, GizmoBuffer<Config, Clear>>,148/// The currently used [`GizmoConfig`]149pub config: &'w GizmoConfig,150/// The currently used [`GizmoConfigGroup`]151pub config_ext: &'w Config,152}153154impl<'w, 's, Config, Clear> Deref for Gizmos<'w, 's, Config, Clear>155where156Config: GizmoConfigGroup,157Clear: 'static + Send + Sync,158{159type Target = GizmoBuffer<Config, Clear>;160161fn deref(&self) -> &Self::Target {162&self.buffer163}164}165166impl<'w, 's, Config, Clear> DerefMut for Gizmos<'w, 's, Config, Clear>167where168Config: GizmoConfigGroup,169Clear: 'static + Send + Sync,170{171fn deref_mut(&mut self) -> &mut Self::Target {172&mut self.buffer173}174}175176type GizmosState<Config, Clear> = (177Deferred<'static, GizmoBuffer<Config, Clear>>,178Res<'static, GizmoConfigStore>,179);180#[doc(hidden)]181pub struct GizmosFetchState<Config, Clear>182where183Config: GizmoConfigGroup,184Clear: 'static + Send + Sync,185{186state: <GizmosState<Config, Clear> as SystemParam>::State,187}188189#[expect(190unsafe_code,191reason = "We cannot implement SystemParam without using unsafe code."192)]193// SAFETY: All methods are delegated to existing `SystemParam` implementations194unsafe impl<Config, Clear> SystemParam for Gizmos<'_, '_, Config, Clear>195where196Config: GizmoConfigGroup,197Clear: 'static + Send + Sync,198{199type State = GizmosFetchState<Config, Clear>;200type Item<'w, 's> = Gizmos<'w, 's, Config, Clear>;201202fn init_state(world: &mut World) -> Self::State {203GizmosFetchState {204state: GizmosState::<Config, Clear>::init_state(world),205}206}207208fn init_access(209state: &Self::State,210system_meta: &mut SystemMeta,211component_access_set: &mut FilteredAccessSet,212world: &mut World,213) {214GizmosState::<Config, Clear>::init_access(215&state.state,216system_meta,217component_access_set,218world,219);220}221222fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {223GizmosState::<Config, Clear>::apply(&mut state.state, system_meta, world);224}225226fn queue(state: &mut Self::State, system_meta: &SystemMeta, world: DeferredWorld) {227GizmosState::<Config, Clear>::queue(&mut state.state, system_meta, world);228}229230#[inline]231unsafe fn validate_param(232state: &mut Self::State,233system_meta: &SystemMeta,234world: UnsafeWorldCell,235) -> Result<(), SystemParamValidationError> {236// SAFETY: Delegated to existing `SystemParam` implementation.237unsafe {238GizmosState::<Config, Clear>::validate_param(&mut state.state, system_meta, world)?;239}240241// SAFETY: Delegated to existing `SystemParam` implementation.242let (_, f1) = unsafe {243GizmosState::<Config, Clear>::get_param(244&mut state.state,245system_meta,246world,247world.change_tick(),248)249};250// This if-block is to accommodate an Option<Gizmos> SystemParam.251// The user may decide not to initialize a gizmo group, so its config will not exist.252if f1.get_config::<Config>().is_none() {253Err(SystemParamValidationError::invalid::<Self>(254format!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?",255Config::type_path())))256} else {257Ok(())258}259}260261#[inline]262unsafe fn get_param<'w, 's>(263state: &'s mut Self::State,264system_meta: &SystemMeta,265world: UnsafeWorldCell<'w>,266change_tick: Tick,267) -> Self::Item<'w, 's> {268// SAFETY: Delegated to existing `SystemParam` implementations.269let (mut f0, f1) = unsafe {270GizmosState::<Config, Clear>::get_param(271&mut state.state,272system_meta,273world,274change_tick,275)276};277278// Accessing the GizmoConfigStore in every API call reduces performance significantly.279// Implementing SystemParam manually allows us to cache whether the config is currently enabled.280// Having this available allows for cheap early returns when gizmos are disabled.281let (config, config_ext) = f1.into_inner().config::<Config>();282f0.enabled = config.enabled;283284Gizmos {285buffer: f0,286config,287config_ext,288}289}290}291292#[expect(293unsafe_code,294reason = "We cannot implement ReadOnlySystemParam without using unsafe code."295)]296// Safety: Each field is `ReadOnlySystemParam`, and Gizmos SystemParam does not mutate world297unsafe impl<'w, 's, Config, Clear> ReadOnlySystemParam for Gizmos<'w, 's, Config, Clear>298where299Config: GizmoConfigGroup,300Clear: 'static + Send + Sync,301Deferred<'s, GizmoBuffer<Config, Clear>>: ReadOnlySystemParam,302Res<'w, GizmoConfigStore>: ReadOnlySystemParam,303{304}305306/// Buffer for gizmo vertex data.307#[derive(Debug, Clone, Reflect)]308#[reflect(Default)]309pub struct GizmoBuffer<Config, Clear>310where311Config: GizmoConfigGroup,312Clear: 'static + Send + Sync,313{314pub(crate) enabled: bool,315/// The positions of line segment endpoints.316pub list_positions: Vec<Vec3>,317/// The colors of line segment endpoints.318pub list_colors: Vec<LinearRgba>,319/// The positions of line strip vertices.320pub strip_positions: Vec<Vec3>,321/// The colors of line strip vertices.322pub strip_colors: Vec<LinearRgba>,323#[reflect(ignore, clone)]324pub(crate) marker: PhantomData<(Config, Clear)>,325}326327impl<Config, Clear> Default for GizmoBuffer<Config, Clear>328where329Config: GizmoConfigGroup,330Clear: 'static + Send + Sync,331{332fn default() -> Self {333GizmoBuffer::new()334}335}336337impl<Config, Clear> GizmoBuffer<Config, Clear>338where339Config: GizmoConfigGroup,340Clear: 'static + Send + Sync,341{342/// Constructs an empty `GizmoBuffer`.343pub const fn new() -> Self {344GizmoBuffer {345enabled: true,346list_positions: Vec::new(),347list_colors: Vec::new(),348strip_positions: Vec::new(),349strip_colors: Vec::new(),350marker: PhantomData,351}352}353}354355/// Read-only view into [`GizmoBuffer`] data.356pub struct GizmoBufferView<'a> {357/// Vertex positions for line-list topology.358pub list_positions: &'a Vec<Vec3>,359/// Vertex colors for line-list topology.360pub list_colors: &'a Vec<LinearRgba>,361/// Vertex positions for line-strip topology.362pub strip_positions: &'a Vec<Vec3>,363/// Vertex colors for line-strip topology.364pub strip_colors: &'a Vec<LinearRgba>,365}366367impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>368where369Config: GizmoConfigGroup,370Clear: 'static + Send + Sync,371{372fn queue(&mut self, _system_meta: &SystemMeta, mut world: DeferredWorld) {373if let Some(mut storage) = world.get_resource_mut::<GizmoStorage<Config, Clear>>() {374storage.list_positions.append(&mut self.list_positions);375storage.list_colors.append(&mut self.list_colors);376storage.strip_positions.append(&mut self.strip_positions);377storage.strip_colors.append(&mut self.strip_colors);378} else {379// Prevent the buffer from growing indefinitely if GizmoStorage380// for the config group has not been initialized381self.list_positions.clear();382self.list_colors.clear();383self.strip_positions.clear();384self.strip_colors.clear();385}386}387}388389impl<Config, Clear> GizmoBuffer<Config, Clear>390where391Config: GizmoConfigGroup,392Clear: 'static + Send + Sync,393{394/// Clear all data.395pub fn clear(&mut self) {396self.list_positions.clear();397self.list_colors.clear();398self.strip_positions.clear();399self.strip_colors.clear();400}401402/// Read-only view into the buffers data.403pub fn buffer(&self) -> GizmoBufferView<'_> {404let GizmoBuffer {405list_positions,406list_colors,407strip_positions,408strip_colors,409..410} = self;411GizmoBufferView {412list_positions,413list_colors,414strip_positions,415strip_colors,416}417}418/// Draw a line in 3D from `start` to `end`.419///420/// # Example421/// ```422/// # use bevy_gizmos::prelude::*;423/// # use bevy_math::prelude::*;424/// # use bevy_color::palettes::basic::GREEN;425/// fn system(mut gizmos: Gizmos) {426/// gizmos.line(Vec3::ZERO, Vec3::X, GREEN);427/// }428/// # bevy_ecs::system::assert_is_system(system);429/// ```430#[inline]431pub fn line(&mut self, start: Vec3, end: Vec3, color: impl Into<Color>) {432if !self.enabled {433return;434}435self.extend_list_positions([start, end]);436self.add_list_color(color, 2);437}438439/// Draw a line in 3D with a color gradient from `start` to `end`.440///441/// # Example442/// ```443/// # use bevy_gizmos::prelude::*;444/// # use bevy_math::prelude::*;445/// # use bevy_color::palettes::basic::{RED, GREEN};446/// fn system(mut gizmos: Gizmos) {447/// gizmos.line_gradient(Vec3::ZERO, Vec3::X, GREEN, RED);448/// }449/// # bevy_ecs::system::assert_is_system(system);450/// ```451#[inline]452pub fn line_gradient<C: Into<Color>>(453&mut self,454start: Vec3,455end: Vec3,456start_color: C,457end_color: C,458) {459if !self.enabled {460return;461}462self.extend_list_positions([start, end]);463self.extend_list_colors([start_color, end_color]);464}465466/// Draw a line in 3D from `start` to `start + vector`.467///468/// # Example469/// ```470/// # use bevy_gizmos::prelude::*;471/// # use bevy_math::prelude::*;472/// # use bevy_color::palettes::basic::GREEN;473/// fn system(mut gizmos: Gizmos) {474/// gizmos.ray(Vec3::Y, Vec3::X, GREEN);475/// }476/// # bevy_ecs::system::assert_is_system(system);477/// ```478#[inline]479pub fn ray(&mut self, start: Vec3, vector: Vec3, color: impl Into<Color>) {480if !self.enabled {481return;482}483self.line(start, start + vector, color);484}485486/// Draw a line in 3D with a color gradient from `start` to `start + vector`.487///488/// # Example489/// ```490/// # use bevy_gizmos::prelude::*;491/// # use bevy_math::prelude::*;492/// # use bevy_color::palettes::basic::{RED, GREEN};493/// fn system(mut gizmos: Gizmos) {494/// gizmos.ray_gradient(Vec3::Y, Vec3::X, GREEN, RED);495/// }496/// # bevy_ecs::system::assert_is_system(system);497/// ```498#[inline]499pub fn ray_gradient<C: Into<Color>>(500&mut self,501start: Vec3,502vector: Vec3,503start_color: C,504end_color: C,505) {506if !self.enabled {507return;508}509self.line_gradient(start, start + vector, start_color, end_color);510}511512/// Draw a line in 3D made of straight segments between the points.513///514/// # Example515/// ```516/// # use bevy_gizmos::prelude::*;517/// # use bevy_math::prelude::*;518/// # use bevy_color::palettes::basic::GREEN;519/// fn system(mut gizmos: Gizmos) {520/// gizmos.linestrip([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);521/// }522/// # bevy_ecs::system::assert_is_system(system);523/// ```524#[inline]525pub fn linestrip(526&mut self,527positions: impl IntoIterator<Item = Vec3>,528color: impl Into<Color>,529) {530if !self.enabled {531return;532}533self.extend_strip_positions(positions);534let len = self.strip_positions.len();535let linear_color = LinearRgba::from(color.into());536self.strip_colors.resize(len - 1, linear_color);537self.strip_colors.push(LinearRgba::NAN);538}539540/// Draw a line in 3D made of straight segments between the points, with the first and last connected.541///542/// # Example543/// ```544/// # use bevy_gizmos::prelude::*;545/// # use bevy_math::prelude::*;546/// # use bevy_color::palettes::basic::GREEN;547/// fn system(mut gizmos: Gizmos) {548/// gizmos.lineloop([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);549/// }550/// # bevy_ecs::system::assert_is_system(system);551/// ```552#[inline]553pub fn lineloop(&mut self, positions: impl IntoIterator<Item = Vec3>, color: impl Into<Color>) {554if !self.enabled {555return;556}557558// Loop back to the start; second is needed to ensure that559// the joint on the first corner is drawn.560let mut positions = positions.into_iter();561let first = positions.next();562let second = positions.next();563564self.linestrip(565first566.into_iter()567.chain(second)568.chain(positions)569.chain(first)570.chain(second),571color,572);573}574575/// Draw a line in 3D made of straight segments between the points, with a color gradient.576///577/// # Example578/// ```579/// # use bevy_gizmos::prelude::*;580/// # use bevy_math::prelude::*;581/// # use bevy_color::palettes::basic::{BLUE, GREEN, RED};582/// fn system(mut gizmos: Gizmos) {583/// gizmos.linestrip_gradient([584/// (Vec3::ZERO, GREEN),585/// (Vec3::X, RED),586/// (Vec3::Y, BLUE)587/// ]);588/// }589/// # bevy_ecs::system::assert_is_system(system);590/// ```591#[inline]592pub fn linestrip_gradient<C: Into<Color>>(593&mut self,594points: impl IntoIterator<Item = (Vec3, C)>,595) {596if !self.enabled {597return;598}599let points = points.into_iter();600601let GizmoBuffer {602strip_positions,603strip_colors,604..605} = self;606607let (min, _) = points.size_hint();608strip_positions.reserve(min);609strip_colors.reserve(min);610611for (position, color) in points {612strip_positions.push(position);613strip_colors.push(LinearRgba::from(color.into()));614}615616strip_positions.push(Vec3::NAN);617strip_colors.push(LinearRgba::NAN);618}619620/// Draw a wireframe rectangle in 3D with the given `isometry` applied.621///622/// If `isometry == Isometry3d::IDENTITY` then623///624/// - the center is at `Vec3::ZERO`625/// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.626///627/// # Example628/// ```629/// # use bevy_gizmos::prelude::*;630/// # use bevy_math::prelude::*;631/// # use bevy_color::palettes::basic::GREEN;632/// fn system(mut gizmos: Gizmos) {633/// gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);634/// }635/// # bevy_ecs::system::assert_is_system(system);636/// ```637#[inline]638pub fn rect(&mut self, isometry: impl Into<Isometry3d>, size: Vec2, color: impl Into<Color>) {639if !self.enabled {640return;641}642let isometry = isometry.into();643let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));644self.lineloop([tl, tr, br, bl], color);645}646647/// Draw a wireframe cube in 3D.648///649/// # Example650/// ```651/// # use bevy_gizmos::prelude::*;652/// # use bevy_transform::prelude::*;653/// # use bevy_color::palettes::basic::GREEN;654/// fn system(mut gizmos: Gizmos) {655/// gizmos.cube(Transform::IDENTITY, GREEN);656/// }657/// # bevy_ecs::system::assert_is_system(system);658/// ```659#[inline]660pub fn cube(&mut self, transform: impl TransformPoint, color: impl Into<Color>) {661let polymorphic_color: Color = color.into();662if !self.enabled {663return;664}665let rect = rect_inner(Vec2::ONE);666// Front667let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));668// Back669let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));670671let strip_positions = [672tlf, trf, brf, blf, tlf, // Front673tlb, trb, brb, blb, tlb, // Back674];675self.linestrip(strip_positions, polymorphic_color);676677let list_positions = [678trf, trb, brf, brb, blf, blb, // Front to back679];680self.extend_list_positions(list_positions);681682self.add_list_color(polymorphic_color, 6);683}684685/// Draw a wireframe aabb in 3D.686///687/// # Example688/// ```689/// # use bevy_gizmos::prelude::*;690/// # use bevy_transform::prelude::*;691/// # use bevy_math::{bounding::Aabb3d, Vec3};692/// # use bevy_color::palettes::basic::GREEN;693/// fn system(mut gizmos: Gizmos) {694/// gizmos.aabb_3d(Aabb3d::new(Vec3::ZERO, Vec3::ONE), Transform::IDENTITY, GREEN);695/// }696/// # bevy_ecs::system::assert_is_system(system);697/// ```698#[inline]699pub fn aabb_3d(700&mut self,701aabb: impl Into<Aabb3d>,702transform: impl TransformPoint,703color: impl Into<Color>,704) {705let polymorphic_color: Color = color.into();706if !self.enabled {707return;708}709let aabb = aabb.into();710let [tlf, trf, brf, blf, tlb, trb, brb, blb] = [711Vec3::new(aabb.min.x, aabb.max.y, aabb.max.z),712Vec3::new(aabb.max.x, aabb.max.y, aabb.max.z),713Vec3::new(aabb.max.x, aabb.min.y, aabb.max.z),714Vec3::new(aabb.min.x, aabb.min.y, aabb.max.z),715Vec3::new(aabb.min.x, aabb.max.y, aabb.min.z),716Vec3::new(aabb.max.x, aabb.max.y, aabb.min.z),717Vec3::new(aabb.max.x, aabb.min.y, aabb.min.z),718Vec3::new(aabb.min.x, aabb.min.y, aabb.min.z),719]720.map(|v| transform.transform_point(v));721722let strip_positions = [723tlf, trf, brf, blf, tlf, // Front724tlb, trb, brb, blb, tlb, // Back725];726self.linestrip(strip_positions, polymorphic_color);727728let list_positions = [729trf, trb, brf, brb, blf, blb, // Front to back730];731self.extend_list_positions(list_positions);732733self.add_list_color(polymorphic_color, 6);734}735736/// Draw a line in 2D from `start` to `end`.737///738/// # Example739/// ```740/// # use bevy_gizmos::prelude::*;741/// # use bevy_math::prelude::*;742/// # use bevy_color::palettes::basic::GREEN;743/// fn system(mut gizmos: Gizmos) {744/// gizmos.line_2d(Vec2::ZERO, Vec2::X, GREEN);745/// }746/// # bevy_ecs::system::assert_is_system(system);747/// ```748#[inline]749pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: impl Into<Color>) {750if !self.enabled {751return;752}753self.line(start.extend(0.), end.extend(0.), color);754}755756/// Draw a line in 2D with a color gradient from `start` to `end`.757///758/// # Example759/// ```760/// # use bevy_gizmos::prelude::*;761/// # use bevy_math::prelude::*;762/// # use bevy_color::palettes::basic::{RED, GREEN};763/// fn system(mut gizmos: Gizmos) {764/// gizmos.line_gradient_2d(Vec2::ZERO, Vec2::X, GREEN, RED);765/// }766/// # bevy_ecs::system::assert_is_system(system);767/// ```768#[inline]769pub fn line_gradient_2d<C: Into<Color>>(770&mut self,771start: Vec2,772end: Vec2,773start_color: C,774end_color: C,775) {776if !self.enabled {777return;778}779self.line_gradient(start.extend(0.), end.extend(0.), start_color, end_color);780}781782/// Draw a line in 2D made of straight segments between the points.783///784/// # Example785/// ```786/// # use bevy_gizmos::prelude::*;787/// # use bevy_math::prelude::*;788/// # use bevy_color::palettes::basic::GREEN;789/// fn system(mut gizmos: Gizmos) {790/// gizmos.linestrip_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);791/// }792/// # bevy_ecs::system::assert_is_system(system);793/// ```794#[inline]795pub fn linestrip_2d(796&mut self,797positions: impl IntoIterator<Item = Vec2>,798color: impl Into<Color>,799) {800if !self.enabled {801return;802}803self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);804}805806/// Draw a line in 2D made of straight segments between the points, with the first and last connected.807///808/// # Example809/// ```810/// # use bevy_gizmos::prelude::*;811/// # use bevy_math::prelude::*;812/// # use bevy_color::palettes::basic::GREEN;813/// fn system(mut gizmos: Gizmos) {814/// gizmos.lineloop_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);815/// }816/// # bevy_ecs::system::assert_is_system(system);817/// ```818#[inline]819pub fn lineloop_2d(820&mut self,821positions: impl IntoIterator<Item = Vec2>,822color: impl Into<Color>,823) {824if !self.enabled {825return;826}827self.lineloop(positions.into_iter().map(|vec2| vec2.extend(0.)), color);828}829830/// Draw a line in 2D made of straight segments between the points, with a color gradient.831///832/// # Example833/// ```834/// # use bevy_gizmos::prelude::*;835/// # use bevy_math::prelude::*;836/// # use bevy_color::palettes::basic::{RED, GREEN, BLUE};837/// fn system(mut gizmos: Gizmos) {838/// gizmos.linestrip_gradient_2d([839/// (Vec2::ZERO, GREEN),840/// (Vec2::X, RED),841/// (Vec2::Y, BLUE)842/// ]);843/// }844/// # bevy_ecs::system::assert_is_system(system);845/// ```846#[inline]847pub fn linestrip_gradient_2d<C: Into<Color>>(848&mut self,849positions: impl IntoIterator<Item = (Vec2, C)>,850) {851if !self.enabled {852return;853}854self.linestrip_gradient(855positions856.into_iter()857.map(|(vec2, color)| (vec2.extend(0.), color)),858);859}860861/// Draw a line in 2D from `start` to `start + vector`.862///863/// # Example864/// ```865/// # use bevy_gizmos::prelude::*;866/// # use bevy_math::prelude::*;867/// # use bevy_color::palettes::basic::GREEN;868/// fn system(mut gizmos: Gizmos) {869/// gizmos.ray_2d(Vec2::Y, Vec2::X, GREEN);870/// }871/// # bevy_ecs::system::assert_is_system(system);872/// ```873#[inline]874pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: impl Into<Color>) {875if !self.enabled {876return;877}878self.line_2d(start, start + vector, color);879}880881/// Draw a line in 2D with a color gradient from `start` to `start + vector`.882///883/// # Example884/// ```885/// # use bevy_gizmos::prelude::*;886/// # use bevy_math::prelude::*;887/// # use bevy_color::palettes::basic::{RED, GREEN};888/// fn system(mut gizmos: Gizmos) {889/// gizmos.line_gradient(Vec3::Y, Vec3::X, GREEN, RED);890/// }891/// # bevy_ecs::system::assert_is_system(system);892/// ```893#[inline]894pub fn ray_gradient_2d<C: Into<Color>>(895&mut self,896start: Vec2,897vector: Vec2,898start_color: C,899end_color: C,900) {901if !self.enabled {902return;903}904self.line_gradient_2d(start, start + vector, start_color, end_color);905}906907/// Draw a wireframe rectangle in 2D with the given `isometry` applied.908///909/// If `isometry == Isometry2d::IDENTITY` then910///911/// - the center is at `Vec2::ZERO`912/// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.913///914/// # Example915/// ```916/// # use bevy_gizmos::prelude::*;917/// # use bevy_math::prelude::*;918/// # use bevy_color::palettes::basic::GREEN;919/// fn system(mut gizmos: Gizmos) {920/// gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);921/// }922/// # bevy_ecs::system::assert_is_system(system);923/// ```924#[inline]925pub fn rect_2d(926&mut self,927isometry: impl Into<Isometry2d>,928size: Vec2,929color: impl Into<Color>,930) {931if !self.enabled {932return;933}934let isometry = isometry.into();935let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);936self.lineloop_2d([tl, tr, br, bl], color);937}938939#[inline]940fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {941self.list_positions.extend(positions);942}943944#[inline]945fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {946self.list_colors.extend(947colors948.into_iter()949.map(|color| LinearRgba::from(color.into())),950);951}952953#[inline]954fn add_list_color(&mut self, color: impl Into<Color>, count: usize) {955let polymorphic_color: Color = color.into();956let linear_color = LinearRgba::from(polymorphic_color);957958self.list_colors.extend(iter::repeat_n(linear_color, count));959}960961#[inline]962fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {963self.strip_positions.extend(positions);964self.strip_positions.push(Vec3::NAN);965}966}967968fn rect_inner(size: Vec2) -> [Vec2; 4] {969let half_size = size / 2.;970let tl = Vec2::new(-half_size.x, half_size.y);971let tr = Vec2::new(half_size.x, half_size.y);972let bl = Vec2::new(-half_size.x, -half_size.y);973let br = Vec2::new(half_size.x, -half_size.y);974[tl, tr, br, bl]975}976977978