//! Additional [`GizmoBuffer`] Functions -- Arrows1//!2//! Includes the implementation of [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`],3//! and assorted support items.45use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup};6use bevy_color::{7palettes::basic::{BLUE, GREEN, RED},8Color,9};10use bevy_math::{Quat, Vec2, Vec3, Vec3Swizzles};11use bevy_transform::TransformPoint;1213/// A builder returned by [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`]14pub struct ArrowBuilder<'a, Config, Clear>15where16Config: GizmoConfigGroup,17Clear: 'static + Send + Sync,18{19gizmos: &'a mut GizmoBuffer<Config, Clear>,20start: Vec3,21end: Vec3,22color: Color,23double_ended: bool,24tip_length: f32,25}2627impl<Config, Clear> ArrowBuilder<'_, Config, Clear>28where29Config: GizmoConfigGroup,30Clear: 'static + Send + Sync,31{32/// Change the length of the tips to be `length`.33/// The default tip length is [length of the arrow]/10.34///35/// # Example36/// ```37/// # use bevy_gizmos::prelude::*;38/// # use bevy_math::prelude::*;39/// # use bevy_color::palettes::basic::GREEN;40/// fn system(mut gizmos: Gizmos) {41/// gizmos.arrow(Vec3::ZERO, Vec3::ONE, GREEN)42/// .with_tip_length(3.);43/// }44/// # bevy_ecs::system::assert_is_system(system);45/// ```46#[doc(alias = "arrow_head_length")]47pub fn with_tip_length(mut self, length: f32) -> Self {48self.tip_length = length;49self50}5152/// Adds another tip to the arrow, appended in the start point.53/// the default is only one tip at the end point.54pub fn with_double_end(mut self) -> Self {55self.double_ended = true;56self57}58}5960impl<Config, Clear> Drop for ArrowBuilder<'_, Config, Clear>61where62Config: GizmoConfigGroup,63Clear: 'static + Send + Sync,64{65/// Draws the arrow, by drawing lines with the stored [`GizmoBuffer`]66fn drop(&mut self) {67if !self.gizmos.enabled {68return;69}70// first, draw the body of the arrow71self.gizmos.line(self.start, self.end, self.color);72// now the hard part is to draw the head in a sensible way73// put us in a coordinate system where the arrow is pointing towards +x and ends at the origin74let pointing_end = (self.end - self.start).normalize();75let rotation_end = Quat::from_rotation_arc(Vec3::X, pointing_end);76let tips = [77Vec3::new(-1., 1., 0.),78Vec3::new(-1., 0., 1.),79Vec3::new(-1., -1., 0.),80Vec3::new(-1., 0., -1.),81];82// - extend the vectors so their length is `tip_length`83// - rotate the world so +x is facing in the same direction as the arrow84// - translate over to the tip of the arrow85let tips_end = tips.map(|v| rotation_end * (v.normalize() * self.tip_length) + self.end);86for v in tips_end {87// then actually draw the tips88self.gizmos.line(self.end, v, self.color);89}90if self.double_ended {91let pointing_start = (self.start - self.end).normalize();92let rotation_start = Quat::from_rotation_arc(Vec3::X, pointing_start);93let tips_start =94tips.map(|v| rotation_start * (v.normalize() * self.tip_length) + self.start);95for v in tips_start {96// draw the start points tips97self.gizmos.line(self.start, v, self.color);98}99}100}101}102103impl<Config, Clear> GizmoBuffer<Config, Clear>104where105Config: GizmoConfigGroup,106Clear: 'static + Send + Sync,107{108/// Draw an arrow in 3D, from `start` to `end`. Has four tips for convenient viewing from any direction.109///110/// # Example111/// ```112/// # use bevy_gizmos::prelude::*;113/// # use bevy_math::prelude::*;114/// # use bevy_color::palettes::basic::GREEN;115/// fn system(mut gizmos: Gizmos) {116/// gizmos.arrow(Vec3::ZERO, Vec3::ONE, GREEN);117/// }118/// # bevy_ecs::system::assert_is_system(system);119/// ```120pub fn arrow(121&mut self,122start: Vec3,123end: Vec3,124color: impl Into<Color>,125) -> ArrowBuilder<'_, Config, Clear> {126let length = (end - start).length();127ArrowBuilder {128gizmos: self,129start,130end,131color: color.into(),132double_ended: false,133tip_length: length / 10.,134}135}136137/// Draw an arrow in 2D (on the xy plane), from `start` to `end`.138///139/// # Example140/// ```141/// # use bevy_gizmos::prelude::*;142/// # use bevy_math::prelude::*;143/// # use bevy_color::palettes::basic::GREEN;144/// fn system(mut gizmos: Gizmos) {145/// gizmos.arrow_2d(Vec2::ZERO, Vec2::X, GREEN);146/// }147/// # bevy_ecs::system::assert_is_system(system);148/// ```149pub fn arrow_2d(150&mut self,151start: Vec2,152end: Vec2,153color: impl Into<Color>,154) -> ArrowBuilder<'_, Config, Clear> {155self.arrow(start.extend(0.), end.extend(0.), color)156}157}158159impl<Config, Clear> GizmoBuffer<Config, Clear>160where161Config: GizmoConfigGroup,162Clear: 'static + Send + Sync,163{164/// Draw a set of axes local to the given transform (`transform`), with length scaled by a factor165/// of `base_length`.166///167/// # Example168/// ```169/// # use bevy_gizmos::prelude::*;170/// # use bevy_ecs::prelude::*;171/// # use bevy_transform::components::Transform;172/// # #[derive(Component)]173/// # struct MyComponent;174/// fn draw_axes(175/// mut gizmos: Gizmos,176/// query: Query<&Transform, With<MyComponent>>,177/// ) {178/// for &transform in &query {179/// gizmos.axes(transform, 1.);180/// }181/// }182/// # bevy_ecs::system::assert_is_system(draw_axes);183/// ```184pub fn axes(&mut self, transform: impl TransformPoint, base_length: f32) {185let start = transform.transform_point(Vec3::ZERO);186let end_x = transform.transform_point(base_length * Vec3::X);187let end_y = transform.transform_point(base_length * Vec3::Y);188let end_z = transform.transform_point(base_length * Vec3::Z);189190self.arrow(start, end_x, RED);191self.arrow(start, end_y, GREEN);192self.arrow(start, end_z, BLUE);193}194195/// Draw a set of axes local to the given transform (`transform`), with length scaled by a factor196/// of `base_length`.197///198/// # Example199/// ```200/// # use bevy_gizmos::prelude::*;201/// # use bevy_ecs::prelude::*;202/// # use bevy_transform::components::Transform;203/// # #[derive(Component)]204/// # struct AxesComponent;205/// fn draw_axes_2d(206/// mut gizmos: Gizmos,207/// query: Query<&Transform, With<AxesComponent>>,208/// ) {209/// for &transform in &query {210/// gizmos.axes_2d(transform, 1.);211/// }212/// }213/// # bevy_ecs::system::assert_is_system(draw_axes_2d);214/// ```215pub fn axes_2d(&mut self, transform: impl TransformPoint, base_length: f32) {216let start = transform.transform_point(Vec3::ZERO);217let end_x = transform.transform_point(base_length * Vec3::X);218let end_y = transform.transform_point(base_length * Vec3::Y);219220self.arrow_2d(start.xy(), end_x.xy(), RED);221self.arrow_2d(start.xy(), end_y.xy(), GREEN);222}223}224225226