//! 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/// This should be called for each frame the arrow needs to be rendered.111///112/// # Example113/// ```114/// # use bevy_gizmos::prelude::*;115/// # use bevy_math::prelude::*;116/// # use bevy_color::palettes::basic::GREEN;117/// fn system(mut gizmos: Gizmos) {118/// gizmos.arrow(Vec3::ZERO, Vec3::ONE, GREEN);119/// }120/// # bevy_ecs::system::assert_is_system(system);121/// ```122pub fn arrow(123&mut self,124start: Vec3,125end: Vec3,126color: impl Into<Color>,127) -> ArrowBuilder<'_, Config, Clear> {128let length = (end - start).length();129ArrowBuilder {130gizmos: self,131start,132end,133color: color.into(),134double_ended: false,135tip_length: length / 10.,136}137}138139/// Draw an arrow in 2D (on the xy plane), from `start` to `end`.140///141/// This should be called for each frame the arrow needs to be rendered.142///143/// # Example144/// ```145/// # use bevy_gizmos::prelude::*;146/// # use bevy_math::prelude::*;147/// # use bevy_color::palettes::basic::GREEN;148/// fn system(mut gizmos: Gizmos) {149/// gizmos.arrow_2d(Vec2::ZERO, Vec2::X, GREEN);150/// }151/// # bevy_ecs::system::assert_is_system(system);152/// ```153pub fn arrow_2d(154&mut self,155start: Vec2,156end: Vec2,157color: impl Into<Color>,158) -> ArrowBuilder<'_, Config, Clear> {159self.arrow(start.extend(0.), end.extend(0.), color)160}161}162163impl<Config, Clear> GizmoBuffer<Config, Clear>164where165Config: GizmoConfigGroup,166Clear: 'static + Send + Sync,167{168/// Draw a set of axes local to the given transform (`transform`), with length scaled by a factor169/// of `base_length`.170///171/// This should be called for each frame the axes need to be rendered.172///173/// # Example174/// ```175/// # use bevy_gizmos::prelude::*;176/// # use bevy_ecs::prelude::*;177/// # use bevy_transform::components::Transform;178/// # #[derive(Component)]179/// # struct MyComponent;180/// fn draw_axes(181/// mut gizmos: Gizmos,182/// query: Query<&Transform, With<MyComponent>>,183/// ) {184/// for &transform in &query {185/// gizmos.axes(transform, 1.);186/// }187/// }188/// # bevy_ecs::system::assert_is_system(draw_axes);189/// ```190pub fn axes(&mut self, transform: impl TransformPoint, base_length: f32) {191let start = transform.transform_point(Vec3::ZERO);192let end_x = transform.transform_point(base_length * Vec3::X);193let end_y = transform.transform_point(base_length * Vec3::Y);194let end_z = transform.transform_point(base_length * Vec3::Z);195196self.arrow(start, end_x, RED);197self.arrow(start, end_y, GREEN);198self.arrow(start, end_z, BLUE);199}200201/// Draw a set of axes local to the given transform (`transform`), with length scaled by a factor202/// of `base_length`.203///204/// This should be called for each frame the axes need to be rendered.205///206/// # Example207/// ```208/// # use bevy_gizmos::prelude::*;209/// # use bevy_ecs::prelude::*;210/// # use bevy_transform::components::Transform;211/// # #[derive(Component)]212/// # struct AxesComponent;213/// fn draw_axes_2d(214/// mut gizmos: Gizmos,215/// query: Query<&Transform, With<AxesComponent>>,216/// ) {217/// for &transform in &query {218/// gizmos.axes_2d(transform, 1.);219/// }220/// }221/// # bevy_ecs::system::assert_is_system(draw_axes_2d);222/// ```223pub fn axes_2d(&mut self, transform: impl TransformPoint, base_length: f32) {224let start = transform.transform_point(Vec3::ZERO);225let end_x = transform.transform_point(base_length * Vec3::X);226let end_y = transform.transform_point(base_length * Vec3::Y);227228self.arrow_2d(start.xy(), end_x.xy(), RED);229self.arrow_2d(start.xy(), end_y.xy(), GREEN);230}231}232233234