use std::f32::consts::PI;
use super::helpers::*;
use bevy_math::primitives::{
BoxedPolygon, BoxedPolyline2d, Capsule2d, Circle, Direction2d, Ellipse, Line2d, Plane2d,
Polygon, Polyline2d, Primitive2d, Rectangle, RegularPolygon, Segment2d, Triangle2d,
};
use bevy_math::{Mat2, Vec2};
use bevy_render::color::Color;
use crate::prelude::{GizmoConfigGroup, Gizmos};
const MIN_LINE_LEN: f32 = 50.0;
const HALF_MIN_LINE_LEN: f32 = 25.0;
const INFINITE_LEN: f32 = 100_000.0;
pub trait GizmoPrimitive2d<P: Primitive2d> {
type Output<'a>
where
Self: 'a;
fn primitive_2d(
&mut self,
primitive: P,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_>;
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Direction2d> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self : 'a;
fn primitive_2d(
&mut self,
primitive: Direction2d,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let direction = Mat2::from_angle(angle) * *primitive;
let start = position;
let end = position + MIN_LINE_LEN * direction;
self.arrow_2d(start, end, color);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Circle> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Circle,
position: Vec2,
_angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
self.circle_2d(position, primitive.radius, color);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Ellipse> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Ellipse,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
self.ellipse_2d(position, angle, primitive.half_size, color);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Capsule2d> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Capsule2d,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let rotation = Mat2::from_angle(angle);
let [top_left, top_right, bottom_left, bottom_right, top_center, bottom_center] = [
[-1.0, 1.0],
[1.0, 1.0],
[-1.0, -1.0],
[1.0, -1.0],
[0.0, 1.0],
[0.0, -1.0],
]
.map(|[sign_x, sign_y]| Vec2::X * sign_x + Vec2::Y * sign_y)
.map(|reference_point| {
let scaling = Vec2::X * primitive.radius + Vec2::Y * primitive.half_length;
reference_point * scaling
})
.map(rotate_then_translate_2d(angle, position));
self.line_2d(bottom_left, top_left, color);
self.line_2d(bottom_right, top_right, color);
let angle_offset = (rotation * Vec2::Y).angle_between(Vec2::Y);
let start_angle_top = angle_offset;
let start_angle_bottom = PI + angle_offset;
self.arc_2d(top_center, start_angle_top, PI, primitive.radius, color);
self.arc_2d(
bottom_center,
start_angle_bottom,
PI,
primitive.radius,
color,
);
}
}
pub struct Line2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
direction: Direction2d, position: Vec2, rotation: Mat2, color: Color, draw_arrow: bool, }
impl<T: GizmoConfigGroup> Line2dBuilder<'_, '_, '_, T> {
pub fn draw_arrow(mut self, is_enabled: bool) -> Self {
self.draw_arrow = is_enabled;
self
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Line2d> for Gizmos<'w, 's, T> {
type Output<'a> = Line2dBuilder<'a, 'w, 's, T> where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Line2d,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
Line2dBuilder {
gizmos: self,
direction: primitive.direction,
position,
rotation: Mat2::from_angle(angle),
color,
draw_arrow: false,
}
}
}
impl<T: GizmoConfigGroup> Drop for Line2dBuilder<'_, '_, '_, T> {
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
}
let direction = self.rotation * *self.direction;
let [start, end] = [1.0, -1.0]
.map(|sign| sign * INFINITE_LEN)
.map(|length| direction * length)
.map(|offset| self.position + offset);
self.gizmos.line_2d(start, end, self.color);
if self.draw_arrow {
self.gizmos.arrow_2d(
self.position - direction * MIN_LINE_LEN,
self.position,
self.color,
);
}
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Plane2d> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Plane2d,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let rotation = Mat2::from_angle(angle);
let normal = primitive.normal;
let normal_segment = Segment2d {
direction: normal,
half_length: HALF_MIN_LINE_LEN,
};
self.primitive_2d(
normal_segment,
position + HALF_MIN_LINE_LEN * rotation * *normal,
angle,
color,
)
.draw_arrow(true);
let direction = Direction2d::new_unchecked(-normal.perp());
self.primitive_2d(Line2d { direction }, position, angle, color)
.draw_arrow(false);
self.arrow_2d(
position,
position + MIN_LINE_LEN * (rotation * *direction),
color,
);
}
}
pub struct Segment2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
gizmos: &'a mut Gizmos<'w, 's, T>,
direction: Direction2d, half_length: f32, position: Vec2, rotation: Mat2, color: Color, draw_arrow: bool, }
impl<T: GizmoConfigGroup> Segment2dBuilder<'_, '_, '_, T> {
pub fn draw_arrow(mut self, is_enabled: bool) -> Self {
self.draw_arrow = is_enabled;
self
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Segment2d> for Gizmos<'w, 's, T> {
type Output<'a> = Segment2dBuilder<'a, 'w, 's, T> where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Segment2d,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
Segment2dBuilder {
gizmos: self,
direction: primitive.direction,
half_length: primitive.half_length,
position,
rotation: Mat2::from_angle(angle),
color,
draw_arrow: Default::default(),
}
}
}
impl<T: GizmoConfigGroup> Drop for Segment2dBuilder<'_, '_, '_, T> {
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
}
let direction = self.rotation * *self.direction;
let start = self.position - direction * self.half_length;
let end = self.position + direction * self.half_length;
if self.draw_arrow {
self.gizmos.arrow_2d(start, end, self.color);
} else {
self.gizmos.line_2d(start, end, self.color);
}
}
}
impl<'w, 's, const N: usize, T: GizmoConfigGroup> GizmoPrimitive2d<Polyline2d<N>>
for Gizmos<'w, 's, T>
{
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Polyline2d<N>,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
self.linestrip_2d(
primitive
.vertices
.iter()
.copied()
.map(rotate_then_translate_2d(angle, position)),
color,
);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<BoxedPolyline2d> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: BoxedPolyline2d,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
self.linestrip_2d(
primitive
.vertices
.iter()
.copied()
.map(rotate_then_translate_2d(angle, position)),
color,
);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Triangle2d> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Triangle2d,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let [a, b, c] = primitive.vertices;
let positions = [a, b, c, a].map(rotate_then_translate_2d(angle, position));
self.linestrip_2d(positions, color);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<Rectangle> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Rectangle,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let [a, b, c, d] =
[(1.0, 1.0), (1.0, -1.0), (-1.0, -1.0), (-1.0, 1.0)].map(|(sign_x, sign_y)| {
Vec2::new(
primitive.half_size.x * sign_x,
primitive.half_size.y * sign_y,
)
});
let positions = [a, b, c, d, a].map(rotate_then_translate_2d(angle, position));
self.linestrip_2d(positions, color);
}
}
impl<'w, 's, const N: usize, T: GizmoConfigGroup> GizmoPrimitive2d<Polygon<N>>
for Gizmos<'w, 's, T>
{
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: Polygon<N>,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let closing_point = {
let first = primitive.vertices.first();
(primitive.vertices.last() != first)
.then_some(first)
.flatten()
.cloned()
};
self.linestrip_2d(
primitive
.vertices
.iter()
.copied()
.chain(closing_point)
.map(rotate_then_translate_2d(angle, position)),
color,
);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<BoxedPolygon> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: BoxedPolygon,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let closing_point = {
let first = primitive.vertices.first();
(primitive.vertices.last() != first)
.then_some(first)
.flatten()
.cloned()
};
self.linestrip_2d(
primitive
.vertices
.iter()
.copied()
.chain(closing_point)
.map(rotate_then_translate_2d(angle, position)),
color,
);
}
}
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive2d<RegularPolygon> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;
fn primitive_2d(
&mut self,
primitive: RegularPolygon,
position: Vec2,
angle: f32,
color: Color,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let points = (0..=primitive.sides)
.map(|p| single_circle_coordinate(primitive.circumcircle.radius, primitive.sides, p))
.map(rotate_then_translate_2d(angle, position));
self.linestrip_2d(points, color);
}
}