//! A module for the [`Gizmos`] [`SystemParam`].12use core::{3iter,4marker::PhantomData,5mem,6ops::{Deref, DerefMut},7};89use bevy_color::{Color, LinearRgba};10use bevy_ecs::{11component::Tick,12query::FilteredAccessSet,13resource::Resource,14system::{15Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam,16SystemParamValidationError,17},18world::{unsafe_world_cell::UnsafeWorldCell, World},19};20use bevy_math::{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}225226#[inline]227unsafe fn validate_param(228state: &mut Self::State,229system_meta: &SystemMeta,230world: UnsafeWorldCell,231) -> Result<(), SystemParamValidationError> {232// SAFETY: Delegated to existing `SystemParam` implementations.233unsafe {234GizmosState::<Config, Clear>::validate_param(&mut state.state, system_meta, world)235}236}237238#[inline]239unsafe fn get_param<'w, 's>(240state: &'s mut Self::State,241system_meta: &SystemMeta,242world: UnsafeWorldCell<'w>,243change_tick: Tick,244) -> Self::Item<'w, 's> {245// SAFETY: Delegated to existing `SystemParam` implementations.246let (mut f0, f1) = unsafe {247GizmosState::<Config, Clear>::get_param(248&mut state.state,249system_meta,250world,251change_tick,252)253};254255// Accessing the GizmoConfigStore in every API call reduces performance significantly.256// Implementing SystemParam manually allows us to cache whether the config is currently enabled.257// Having this available allows for cheap early returns when gizmos are disabled.258let (config, config_ext) = f1.into_inner().config::<Config>();259f0.enabled = config.enabled;260261Gizmos {262buffer: f0,263config,264config_ext,265}266}267}268269#[expect(270unsafe_code,271reason = "We cannot implement ReadOnlySystemParam without using unsafe code."272)]273// Safety: Each field is `ReadOnlySystemParam`, and Gizmos SystemParam does not mutate world274unsafe impl<'w, 's, Config, Clear> ReadOnlySystemParam for Gizmos<'w, 's, Config, Clear>275where276Config: GizmoConfigGroup,277Clear: 'static + Send + Sync,278Deferred<'s, GizmoBuffer<Config, Clear>>: ReadOnlySystemParam,279Res<'w, GizmoConfigStore>: ReadOnlySystemParam,280{281}282283/// Buffer for gizmo vertex data.284#[derive(Debug, Clone, Reflect)]285#[reflect(Default)]286pub struct GizmoBuffer<Config, Clear>287where288Config: GizmoConfigGroup,289Clear: 'static + Send + Sync,290{291pub(crate) enabled: bool,292pub(crate) list_positions: Vec<Vec3>,293pub(crate) list_colors: Vec<LinearRgba>,294pub(crate) strip_positions: Vec<Vec3>,295pub(crate) strip_colors: Vec<LinearRgba>,296#[reflect(ignore, clone)]297pub(crate) marker: PhantomData<(Config, Clear)>,298}299300impl<Config, Clear> Default for GizmoBuffer<Config, Clear>301where302Config: GizmoConfigGroup,303Clear: 'static + Send + Sync,304{305fn default() -> Self {306GizmoBuffer {307enabled: true,308list_positions: Vec::new(),309list_colors: Vec::new(),310strip_positions: Vec::new(),311strip_colors: Vec::new(),312marker: PhantomData,313}314}315}316317/// Read-only view into [`GizmoBuffer`] data.318pub struct GizmoBufferView<'a> {319/// Vertex positions for line-list topology.320pub list_positions: &'a Vec<Vec3>,321/// Vertex colors for line-list topology.322pub list_colors: &'a Vec<LinearRgba>,323/// Vertex positions for line-strip topology.324pub strip_positions: &'a Vec<Vec3>,325/// Vertex colors for line-strip topology.326pub strip_colors: &'a Vec<LinearRgba>,327}328329impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>330where331Config: GizmoConfigGroup,332Clear: 'static + Send + Sync,333{334fn apply(&mut self, _system_meta: &SystemMeta, world: &mut World) {335let mut storage = world.resource_mut::<GizmoStorage<Config, Clear>>();336storage.list_positions.append(&mut self.list_positions);337storage.list_colors.append(&mut self.list_colors);338storage.strip_positions.append(&mut self.strip_positions);339storage.strip_colors.append(&mut self.strip_colors);340}341}342343impl<Config, Clear> GizmoBuffer<Config, Clear>344where345Config: GizmoConfigGroup,346Clear: 'static + Send + Sync,347{348/// Clear all data.349pub fn clear(&mut self) {350self.list_positions.clear();351self.list_colors.clear();352self.strip_positions.clear();353self.strip_colors.clear();354}355356/// Read-only view into the buffers data.357pub fn buffer(&self) -> GizmoBufferView<'_> {358let GizmoBuffer {359list_positions,360list_colors,361strip_positions,362strip_colors,363..364} = self;365GizmoBufferView {366list_positions,367list_colors,368strip_positions,369strip_colors,370}371}372/// Draw a line in 3D from `start` to `end`.373///374/// This should be called for each frame the line needs to be rendered.375///376/// # Example377/// ```378/// # use bevy_gizmos::prelude::*;379/// # use bevy_math::prelude::*;380/// # use bevy_color::palettes::basic::GREEN;381/// fn system(mut gizmos: Gizmos) {382/// gizmos.line(Vec3::ZERO, Vec3::X, GREEN);383/// }384/// # bevy_ecs::system::assert_is_system(system);385/// ```386#[inline]387pub fn line(&mut self, start: Vec3, end: Vec3, color: impl Into<Color>) {388if !self.enabled {389return;390}391self.extend_list_positions([start, end]);392self.add_list_color(color, 2);393}394395/// Draw a line in 3D with a color gradient from `start` to `end`.396///397/// This should be called for each frame the line needs to be rendered.398///399/// # Example400/// ```401/// # use bevy_gizmos::prelude::*;402/// # use bevy_math::prelude::*;403/// # use bevy_color::palettes::basic::{RED, GREEN};404/// fn system(mut gizmos: Gizmos) {405/// gizmos.line_gradient(Vec3::ZERO, Vec3::X, GREEN, RED);406/// }407/// # bevy_ecs::system::assert_is_system(system);408/// ```409#[inline]410pub fn line_gradient<C: Into<Color>>(411&mut self,412start: Vec3,413end: Vec3,414start_color: C,415end_color: C,416) {417if !self.enabled {418return;419}420self.extend_list_positions([start, end]);421self.extend_list_colors([start_color, end_color]);422}423424/// Draw a line in 3D from `start` to `start + vector`.425///426/// This should be called for each frame the line needs to be rendered.427///428/// # Example429/// ```430/// # use bevy_gizmos::prelude::*;431/// # use bevy_math::prelude::*;432/// # use bevy_color::palettes::basic::GREEN;433/// fn system(mut gizmos: Gizmos) {434/// gizmos.ray(Vec3::Y, Vec3::X, GREEN);435/// }436/// # bevy_ecs::system::assert_is_system(system);437/// ```438#[inline]439pub fn ray(&mut self, start: Vec3, vector: Vec3, color: impl Into<Color>) {440if !self.enabled {441return;442}443self.line(start, start + vector, color);444}445446/// Draw a line in 3D with a color gradient from `start` to `start + vector`.447///448/// This should be called for each frame the line needs to be rendered.449///450/// # Example451/// ```452/// # use bevy_gizmos::prelude::*;453/// # use bevy_math::prelude::*;454/// # use bevy_color::palettes::basic::{RED, GREEN};455/// fn system(mut gizmos: Gizmos) {456/// gizmos.ray_gradient(Vec3::Y, Vec3::X, GREEN, RED);457/// }458/// # bevy_ecs::system::assert_is_system(system);459/// ```460#[inline]461pub fn ray_gradient<C: Into<Color>>(462&mut self,463start: Vec3,464vector: Vec3,465start_color: C,466end_color: C,467) {468if !self.enabled {469return;470}471self.line_gradient(start, start + vector, start_color, end_color);472}473474/// Draw a line in 3D made of straight segments between the points.475///476/// This should be called for each frame the line needs to be rendered.477///478/// # Example479/// ```480/// # use bevy_gizmos::prelude::*;481/// # use bevy_math::prelude::*;482/// # use bevy_color::palettes::basic::GREEN;483/// fn system(mut gizmos: Gizmos) {484/// gizmos.linestrip([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);485/// }486/// # bevy_ecs::system::assert_is_system(system);487/// ```488#[inline]489pub fn linestrip(490&mut self,491positions: impl IntoIterator<Item = Vec3>,492color: impl Into<Color>,493) {494if !self.enabled {495return;496}497self.extend_strip_positions(positions);498let len = self.strip_positions.len();499let linear_color = LinearRgba::from(color.into());500self.strip_colors.resize(len - 1, linear_color);501self.strip_colors.push(LinearRgba::NAN);502}503504/// Draw a line in 3D made of straight segments between the points, with a color gradient.505///506/// This should be called for each frame the lines need to be rendered.507///508/// # Example509/// ```510/// # use bevy_gizmos::prelude::*;511/// # use bevy_math::prelude::*;512/// # use bevy_color::palettes::basic::{BLUE, GREEN, RED};513/// fn system(mut gizmos: Gizmos) {514/// gizmos.linestrip_gradient([515/// (Vec3::ZERO, GREEN),516/// (Vec3::X, RED),517/// (Vec3::Y, BLUE)518/// ]);519/// }520/// # bevy_ecs::system::assert_is_system(system);521/// ```522#[inline]523pub fn linestrip_gradient<C: Into<Color>>(524&mut self,525points: impl IntoIterator<Item = (Vec3, C)>,526) {527if !self.enabled {528return;529}530let points = points.into_iter();531532let GizmoBuffer {533strip_positions,534strip_colors,535..536} = self;537538let (min, _) = points.size_hint();539strip_positions.reserve(min);540strip_colors.reserve(min);541542for (position, color) in points {543strip_positions.push(position);544strip_colors.push(LinearRgba::from(color.into()));545}546547strip_positions.push(Vec3::NAN);548strip_colors.push(LinearRgba::NAN);549}550551/// Draw a wireframe rectangle in 3D with the given `isometry` applied.552///553/// If `isometry == Isometry3d::IDENTITY` then554///555/// - the center is at `Vec3::ZERO`556/// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.557///558/// This should be called for each frame the rectangle needs to be rendered.559///560/// # Example561/// ```562/// # use bevy_gizmos::prelude::*;563/// # use bevy_math::prelude::*;564/// # use bevy_color::palettes::basic::GREEN;565/// fn system(mut gizmos: Gizmos) {566/// gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);567/// }568/// # bevy_ecs::system::assert_is_system(system);569/// ```570#[inline]571pub fn rect(&mut self, isometry: impl Into<Isometry3d>, size: Vec2, color: impl Into<Color>) {572if !self.enabled {573return;574}575let isometry = isometry.into();576let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));577self.linestrip([tl, tr, br, bl, tl], color);578}579580/// Draw a wireframe cube in 3D.581///582/// This should be called for each frame the cube needs to be rendered.583///584/// # Example585/// ```586/// # use bevy_gizmos::prelude::*;587/// # use bevy_transform::prelude::*;588/// # use bevy_color::palettes::basic::GREEN;589/// fn system(mut gizmos: Gizmos) {590/// gizmos.cuboid(Transform::IDENTITY, GREEN);591/// }592/// # bevy_ecs::system::assert_is_system(system);593/// ```594#[inline]595pub fn cuboid(&mut self, transform: impl TransformPoint, color: impl Into<Color>) {596let polymorphic_color: Color = color.into();597if !self.enabled {598return;599}600let rect = rect_inner(Vec2::ONE);601// Front602let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));603// Back604let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));605606let strip_positions = [607tlf, trf, brf, blf, tlf, // Front608tlb, trb, brb, blb, tlb, // Back609];610self.linestrip(strip_positions, polymorphic_color);611612let list_positions = [613trf, trb, brf, brb, blf, blb, // Front to back614];615self.extend_list_positions(list_positions);616617self.add_list_color(polymorphic_color, 6);618}619620/// Draw a line in 2D from `start` to `end`.621///622/// This should be called for each frame the line needs to be rendered.623///624/// # Example625/// ```626/// # use bevy_gizmos::prelude::*;627/// # use bevy_math::prelude::*;628/// # use bevy_color::palettes::basic::GREEN;629/// fn system(mut gizmos: Gizmos) {630/// gizmos.line_2d(Vec2::ZERO, Vec2::X, GREEN);631/// }632/// # bevy_ecs::system::assert_is_system(system);633/// ```634#[inline]635pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: impl Into<Color>) {636if !self.enabled {637return;638}639self.line(start.extend(0.), end.extend(0.), color);640}641642/// Draw a line in 2D with a color gradient from `start` to `end`.643///644/// This should be called for each frame the line needs to be rendered.645///646/// # Example647/// ```648/// # use bevy_gizmos::prelude::*;649/// # use bevy_math::prelude::*;650/// # use bevy_color::palettes::basic::{RED, GREEN};651/// fn system(mut gizmos: Gizmos) {652/// gizmos.line_gradient_2d(Vec2::ZERO, Vec2::X, GREEN, RED);653/// }654/// # bevy_ecs::system::assert_is_system(system);655/// ```656#[inline]657pub fn line_gradient_2d<C: Into<Color>>(658&mut self,659start: Vec2,660end: Vec2,661start_color: C,662end_color: C,663) {664if !self.enabled {665return;666}667self.line_gradient(start.extend(0.), end.extend(0.), start_color, end_color);668}669670/// Draw a line in 2D made of straight segments between the points.671///672/// This should be called for each frame the line needs to be rendered.673///674/// # Example675/// ```676/// # use bevy_gizmos::prelude::*;677/// # use bevy_math::prelude::*;678/// # use bevy_color::palettes::basic::GREEN;679/// fn system(mut gizmos: Gizmos) {680/// gizmos.linestrip_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);681/// }682/// # bevy_ecs::system::assert_is_system(system);683/// ```684#[inline]685pub fn linestrip_2d(686&mut self,687positions: impl IntoIterator<Item = Vec2>,688color: impl Into<Color>,689) {690if !self.enabled {691return;692}693self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);694}695696/// Draw a line in 2D made of straight segments between the points, with a color gradient.697///698/// This should be called for each frame the line needs to be rendered.699///700/// # Example701/// ```702/// # use bevy_gizmos::prelude::*;703/// # use bevy_math::prelude::*;704/// # use bevy_color::palettes::basic::{RED, GREEN, BLUE};705/// fn system(mut gizmos: Gizmos) {706/// gizmos.linestrip_gradient_2d([707/// (Vec2::ZERO, GREEN),708/// (Vec2::X, RED),709/// (Vec2::Y, BLUE)710/// ]);711/// }712/// # bevy_ecs::system::assert_is_system(system);713/// ```714#[inline]715pub fn linestrip_gradient_2d<C: Into<Color>>(716&mut self,717positions: impl IntoIterator<Item = (Vec2, C)>,718) {719if !self.enabled {720return;721}722self.linestrip_gradient(723positions724.into_iter()725.map(|(vec2, color)| (vec2.extend(0.), color)),726);727}728729/// Draw a line in 2D from `start` to `start + vector`.730///731/// This should be called for each frame the line needs to be rendered.732///733/// # Example734/// ```735/// # use bevy_gizmos::prelude::*;736/// # use bevy_math::prelude::*;737/// # use bevy_color::palettes::basic::GREEN;738/// fn system(mut gizmos: Gizmos) {739/// gizmos.ray_2d(Vec2::Y, Vec2::X, GREEN);740/// }741/// # bevy_ecs::system::assert_is_system(system);742/// ```743#[inline]744pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: impl Into<Color>) {745if !self.enabled {746return;747}748self.line_2d(start, start + vector, color);749}750751/// Draw a line in 2D with a color gradient from `start` to `start + vector`.752///753/// This should be called for each frame the line needs to be rendered.754///755/// # Example756/// ```757/// # use bevy_gizmos::prelude::*;758/// # use bevy_math::prelude::*;759/// # use bevy_color::palettes::basic::{RED, GREEN};760/// fn system(mut gizmos: Gizmos) {761/// gizmos.line_gradient(Vec3::Y, Vec3::X, GREEN, RED);762/// }763/// # bevy_ecs::system::assert_is_system(system);764/// ```765#[inline]766pub fn ray_gradient_2d<C: Into<Color>>(767&mut self,768start: Vec2,769vector: Vec2,770start_color: C,771end_color: C,772) {773if !self.enabled {774return;775}776self.line_gradient_2d(start, start + vector, start_color, end_color);777}778779/// Draw a wireframe rectangle in 2D with the given `isometry` applied.780///781/// If `isometry == Isometry2d::IDENTITY` then782///783/// - the center is at `Vec2::ZERO`784/// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.785///786/// This should be called for each frame the rectangle needs to be rendered.787///788/// # Example789/// ```790/// # use bevy_gizmos::prelude::*;791/// # use bevy_math::prelude::*;792/// # use bevy_color::palettes::basic::GREEN;793/// fn system(mut gizmos: Gizmos) {794/// gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);795/// }796/// # bevy_ecs::system::assert_is_system(system);797/// ```798#[inline]799pub fn rect_2d(800&mut self,801isometry: impl Into<Isometry2d>,802size: Vec2,803color: impl Into<Color>,804) {805if !self.enabled {806return;807}808let isometry = isometry.into();809let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);810self.linestrip_2d([tl, tr, br, bl, tl], color);811}812813#[inline]814fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {815self.list_positions.extend(positions);816}817818#[inline]819fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {820self.list_colors.extend(821colors822.into_iter()823.map(|color| LinearRgba::from(color.into())),824);825}826827#[inline]828fn add_list_color(&mut self, color: impl Into<Color>, count: usize) {829let polymorphic_color: Color = color.into();830let linear_color = LinearRgba::from(polymorphic_color);831832self.list_colors.extend(iter::repeat_n(linear_color, count));833}834835#[inline]836fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {837self.strip_positions.extend(positions);838self.strip_positions.push(Vec3::NAN);839}840}841842fn rect_inner(size: Vec2) -> [Vec2; 4] {843let half_size = size / 2.;844let tl = Vec2::new(-half_size.x, half_size.y);845let tr = Vec2::new(half_size.x, half_size.y);846let bl = Vec2::new(-half_size.x, -half_size.y);847let br = Vec2::new(half_size.x, -half_size.y);848[tl, tr, br, bl]849}850851852