Use Isometry in bevy_gizmos wherever we can (#14676)

# Objective

- Solves the last bullet in and closes #14319
- Make better use of the `Isometry` types
- Prevent issues like #14655
- Probably simplify and clean up a lot of code through the use of Gizmos
as well (i.e. the 3D gizmos for cylinders circles & lines don't connect
well, probably due to wrong rotations)

## Solution

- go through the `bevy_gizmos` crate and give all methods a slight
workover

## Testing

- For all the changed examples I run `git switch main && cargo rr
--example <X> && git switch <BRANCH> && cargo rr --example <X>` and
compare the visual results
- Check if all doc tests are still compiling
- Check the docs in general and update them !!! 

---

## Migration Guide

The gizmos methods function signature changes as follows:

- 2D
- if it took `position` & `rotation_angle` before ->
`Isometry2d::new(position, Rot2::radians(rotation_angle))`
- if it just took `position` before ->
`Isometry2d::from_translation(position)`
- 3D
- if it took `position` & `rotation` before ->
`Isometry3d::new(position, rotation)`
- if it just took `position` before ->
`Isometry3d::from_translation(position)`
This commit is contained in:
Robert Walter 2024-08-28 01:37:19 +00:00 committed by GitHub
parent 45281e62d7
commit 8895113784
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 627 additions and 785 deletions

View file

@ -6,7 +6,7 @@
use crate::circles::DEFAULT_CIRCLE_RESOLUTION;
use crate::prelude::{GizmoConfigGroup, Gizmos};
use bevy_color::Color;
use bevy_math::{Isometry2d, Quat, Vec2, Vec3};
use bevy_math::{Isometry2d, Isometry3d, Quat, Vec2, Vec3};
use std::f32::consts::{FRAC_PI_2, TAU};
// === 2D ===
@ -140,9 +140,9 @@ where
/// - `angle`: sets how much of a circle circumference is passed, e.g. PI is half a circle. This
/// value should be in the range (-2 * PI..=2 * PI)
/// - `radius`: distance between the arc and its center point
/// - `position`: position of the arcs center point
/// - `rotation`: defines orientation of the arc, by default we assume the arc is contained in a
/// plane parallel to the XZ plane and the default starting point is (`position + Vec3::X`)
/// - `isometry` defines the translation and rotation of the arc.
/// - the translation specifies the center of the arc
/// - the rotation is counter-clockwise starting from `Vec3::Y`
/// - `color`: color of the arc
///
/// # Builder methods
@ -163,8 +163,7 @@ where
/// .arc_3d(
/// 270.0_f32.to_radians(),
/// 0.25,
/// Vec3::ONE,
/// rotation,
/// Isometry3d::new(Vec3::ONE, rotation),
/// ORANGE
/// )
/// .resolution(100);
@ -176,15 +175,13 @@ where
&mut self,
angle: f32,
radius: f32,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
Arc3dBuilder {
gizmos: self,
start_vertex: Vec3::X,
center: position,
rotation,
isometry,
angle,
radius,
color: color.into(),
@ -317,8 +314,7 @@ where
Arc3dBuilder {
gizmos: self,
start_vertex,
center,
rotation,
isometry: Isometry3d::new(center, rotation),
angle,
radius,
color: color.into(),
@ -344,8 +340,7 @@ where
//
// DO NOT expose this field to users as it is easy to mess this up
start_vertex: Vec3,
center: Vec3,
rotation: Quat,
isometry: Isometry3d,
angle: f32,
radius: f32,
color: Color,
@ -380,8 +375,7 @@ where
let positions = arc_3d_inner(
self.start_vertex,
self.center,
self.rotation,
self.isometry,
self.angle,
self.radius,
resolution,
@ -392,8 +386,7 @@ where
fn arc_3d_inner(
start_vertex: Vec3,
center: Vec3,
rotation: Quat,
isometry: Isometry3d,
angle: f32,
radius: f32,
resolution: u32,
@ -406,7 +399,8 @@ fn arc_3d_inner(
.map(move |frac| frac as f32 / resolution as f32)
.map(move |percentage| angle * percentage)
.map(move |frac_angle| Quat::from_axis_angle(Vec3::Y, frac_angle) * start_vertex)
.map(move |p| rotation * (p * radius) + center)
.map(move |vec3| vec3 * radius)
.map(move |vec3| isometry * vec3)
}
// helper function for getting a default value for the resolution parameter

View file

@ -5,8 +5,8 @@
use crate::prelude::{GizmoConfigGroup, Gizmos};
use bevy_color::Color;
use bevy_math::Mat2;
use bevy_math::{Dir3, Quat, Vec2, Vec3};
use bevy_math::{Isometry2d, Isometry3d};
use bevy_math::{Quat, Vec2, Vec3};
use std::f32::consts::TAU;
pub(crate) const DEFAULT_CIRCLE_RESOLUTION: u32 = 32;
@ -24,7 +24,12 @@ where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Draw an ellipse in 3D at `position` with the flat side facing `normal`.
/// Draw an ellipse in 3D with the given `isometry` applied.
///
/// If `isometry == Isometry3d::IDENTITY` then
///
/// - the center is at `Vec3::ZERO`
/// - the `half_sizes` are aligned with the `Vec3::X` and `Vec3::Y` axes.
///
/// This should be called for each frame the ellipse needs to be rendered.
///
@ -34,12 +39,12 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::{RED, GREEN};
/// fn system(mut gizmos: Gizmos) {
/// gizmos.ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(1., 2.), GREEN);
/// gizmos.ellipse(Isometry3d::IDENTITY, Vec2::new(1., 2.), GREEN);
///
/// // Ellipses have 32 line-segments by default.
/// // You may want to increase this for larger ellipses.
/// gizmos
/// .ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(5., 1.), RED)
/// .ellipse(Isometry3d::IDENTITY, Vec2::new(5., 1.), RED)
/// .resolution(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
@ -47,22 +52,25 @@ where
#[inline]
pub fn ellipse(
&mut self,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
half_size: Vec2,
color: impl Into<Color>,
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
EllipseBuilder {
gizmos: self,
position,
rotation,
isometry,
half_size,
color: color.into(),
resolution: DEFAULT_CIRCLE_RESOLUTION,
}
}
/// Draw an ellipse in 2D.
/// Draw an ellipse in 2D with the given `isometry` applied.
///
/// If `isometry == Isometry2d::IDENTITY` then
///
/// - the center is at `Vec2::ZERO`
/// - the `half_sizes` are aligned with the `Vec2::X` and `Vec2::Y` axes.
///
/// This should be called for each frame the ellipse needs to be rendered.
///
@ -72,12 +80,12 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::{RED, GREEN};
/// fn system(mut gizmos: Gizmos) {
/// gizmos.ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(2., 1.), GREEN);
/// gizmos.ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(2., 1.), GREEN);
///
/// // Ellipses have 32 line-segments by default.
/// // You may want to increase this for larger ellipses.
/// gizmos
/// .ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(5., 1.), RED)
/// .ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(5., 1.), RED)
/// .resolution(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
@ -85,24 +93,25 @@ where
#[inline]
pub fn ellipse_2d(
&mut self,
position: Vec2,
angle: f32,
isometry: Isometry2d,
half_size: Vec2,
color: impl Into<Color>,
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
Ellipse2dBuilder {
gizmos: self,
position,
rotation: Mat2::from_angle(angle),
isometry,
half_size,
color: color.into(),
resolution: DEFAULT_CIRCLE_RESOLUTION,
}
}
/// Draw a circle in 3D at `position` with the flat side facing `normal`.
/// Draw a circle in 3D with the given `isometry` applied.
///
/// This should be called for each frame the circle needs to be rendered.
/// If `isometry == Isometry3d::IDENTITY` then
///
/// - the center is at `Vec3::ZERO`
/// - the radius is aligned with the `Vec3::X` and `Vec3::Y` axes.
///
/// # Example
/// ```
@ -110,12 +119,12 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::{RED, GREEN};
/// fn system(mut gizmos: Gizmos) {
/// gizmos.circle(Vec3::ZERO, Dir3::Z, 1., GREEN);
/// gizmos.circle(Isometry3d::IDENTITY, 1., GREEN);
///
/// // Circles have 32 line-segments by default.
/// // You may want to increase this for larger circles.
/// gizmos
/// .circle(Vec3::ZERO, Dir3::Z, 5., RED)
/// .circle(Isometry3d::IDENTITY, 5., RED)
/// .resolution(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
@ -123,22 +132,25 @@ where
#[inline]
pub fn circle(
&mut self,
position: Vec3,
normal: Dir3,
isometry: Isometry3d,
radius: f32,
color: impl Into<Color>,
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
EllipseBuilder {
gizmos: self,
position,
rotation: Quat::from_rotation_arc(Vec3::Z, *normal),
isometry,
half_size: Vec2::splat(radius),
color: color.into(),
resolution: DEFAULT_CIRCLE_RESOLUTION,
}
}
/// Draw a circle in 2D.
/// Draw a circle in 2D with the given `isometry` applied.
///
/// If `isometry == Isometry2d::IDENTITY` then
///
/// - the center is at `Vec2::ZERO`
/// - the radius is aligned with the `Vec2::X` and `Vec2::Y` axes.
///
/// This should be called for each frame the circle needs to be rendered.
///
@ -148,12 +160,12 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::{RED, GREEN};
/// fn system(mut gizmos: Gizmos) {
/// gizmos.circle_2d(Vec2::ZERO, 1., GREEN);
/// gizmos.circle_2d(Isometry2d::IDENTITY, 1., GREEN);
///
/// // Circles have 32 line-segments by default.
/// // You may want to increase this for larger circles.
/// gizmos
/// .circle_2d(Vec2::ZERO, 5., RED)
/// .circle_2d(Isometry2d::IDENTITY, 5., RED)
/// .resolution(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
@ -161,21 +173,26 @@ where
#[inline]
pub fn circle_2d(
&mut self,
position: Vec2,
isometry: Isometry2d,
radius: f32,
color: impl Into<Color>,
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
Ellipse2dBuilder {
gizmos: self,
position,
rotation: Mat2::IDENTITY,
isometry,
half_size: Vec2::splat(radius),
color: color.into(),
resolution: DEFAULT_CIRCLE_RESOLUTION,
}
}
/// Draw a wireframe sphere in 3D made out of 3 circles around the axes.
/// Draw a wireframe sphere in 3D made out of 3 circles around the axes with the given
/// `isometry` applied.
///
/// If `isometry == Isometry3d::IDENTITY` then
///
/// - the center is at `Vec3::ZERO`
/// - the 3 circles are in the XY, YZ and XZ planes.
///
/// This should be called for each frame the sphere needs to be rendered.
///
@ -185,12 +202,12 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::Color;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.sphere(Vec3::ZERO, Quat::IDENTITY, 1., Color::BLACK);
/// gizmos.sphere(Isometry3d::IDENTITY, 1., Color::BLACK);
///
/// // Each circle has 32 line-segments by default.
/// // You may want to increase this for larger spheres.
/// gizmos
/// .sphere(Vec3::ZERO, Quat::IDENTITY, 5., Color::BLACK)
/// .sphere(Isometry3d::IDENTITY, 5., Color::BLACK)
/// .resolution(64);
/// }
/// # bevy_ecs::system::assert_is_system(system);
@ -198,16 +215,14 @@ where
#[inline]
pub fn sphere(
&mut self,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
radius: f32,
color: impl Into<Color>,
) -> SphereBuilder<'_, 'w, 's, Config, Clear> {
SphereBuilder {
gizmos: self,
radius,
position,
rotation: rotation.normalize(),
isometry,
color: color.into(),
resolution: DEFAULT_CIRCLE_RESOLUTION,
}
@ -221,8 +236,7 @@ where
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
half_size: Vec2,
color: Color,
resolution: u32,
@ -251,8 +265,7 @@ where
}
let positions = ellipse_inner(self.half_size, self.resolution)
.map(|vec2| self.rotation * vec2.extend(0.))
.map(|vec3| vec3 + self.position);
.map(|vec2| self.isometry * vec2.extend(0.));
self.gizmos.linestrip(positions, self.color);
}
}
@ -264,8 +277,7 @@ where
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec2,
rotation: Mat2,
isometry: Isometry2d,
half_size: Vec2,
color: Color,
resolution: u32,
@ -294,9 +306,8 @@ where
return;
};
let positions = ellipse_inner(self.half_size, self.resolution)
.map(|vec2| self.rotation * vec2)
.map(|vec2| vec2 + self.position);
let positions =
ellipse_inner(self.half_size, self.resolution).map(|vec2| self.isometry * vec2);
self.gizmos.linestrip_2d(positions, self.color);
}
}
@ -312,10 +323,7 @@ where
// Radius of the sphere
radius: f32,
// Rotation of the sphere around the origin in 3D space
rotation: Quat,
// Center position of the sphere in 3D space
position: Vec3,
isometry: Isometry3d,
// Color of the sphere
color: Color,
@ -345,21 +353,12 @@ where
return;
}
let SphereBuilder {
radius,
position: center,
rotation,
color,
resolution,
..
} = self;
// draws one great circle around each of the local axes
Vec3::AXES.into_iter().for_each(|axis| {
let normal = *rotation * axis;
let axis_rotation = Isometry3d::from_rotation(Quat::from_rotation_arc(Vec3::Z, axis));
self.gizmos
.circle(*center, Dir3::new_unchecked(normal), *radius, *color)
.resolution(*resolution);
.circle(self.isometry * axis_rotation, self.radius, self.color)
.resolution(self.resolution);
});
}
}

View file

@ -5,13 +5,18 @@
use crate::prelude::{GizmoConfigGroup, Gizmos};
use bevy_color::Color;
use bevy_math::{Mat2, Mat3, Quat, Vec2, Vec3};
use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3};
impl<Config> Gizmos<'_, '_, Config>
where
Config: GizmoConfigGroup,
{
/// Draw a cross in 3D at `position`.
/// Draw a cross in 3D with the given `isometry` applied.
///
/// If `isometry == Isometry3d::IDENTITY` then
///
/// - the center is at `Vec3::ZERO`
/// - the `half_size`s are aligned with the `Vec3::X`, `Vec3::Y` and `Vec3::Z` axes.
///
/// This should be called for each frame the cross needs to be rendered.
///
@ -21,29 +26,26 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::WHITE;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.cross(Vec3::ZERO, Quat::IDENTITY, 0.5, WHITE);
/// gizmos.cross(Isometry3d::IDENTITY, 0.5, WHITE);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
pub fn cross(
&mut self,
position: Vec3,
rotation: Quat,
half_size: f32,
color: impl Into<Color>,
) {
let axes = half_size * Mat3::from_quat(rotation);
let local_x = axes.col(0);
let local_y = axes.col(1);
let local_z = axes.col(2);
pub fn cross(&mut self, isometry: Isometry3d, half_size: f32, color: impl Into<Color>) {
let color: Color = color.into();
self.line(position + local_x, position - local_x, color);
self.line(position + local_y, position - local_y, color);
self.line(position + local_z, position - local_z, color);
[Vec3::X, Vec3::Y, Vec3::Z]
.map(|axis| axis * half_size)
.into_iter()
.for_each(|axis| {
self.line(isometry * axis, isometry * (-axis), color);
});
}
/// Draw a cross in 2D (on the xy plane) at `position`.
/// Draw a cross in 2D with the given `isometry` applied.
///
/// If `isometry == Isometry2d::IDENTITY` then
///
/// - the center is at `Vec3::ZERO`
/// - the `half_size`s are aligned with the `Vec3::X` and `Vec3::Y` axes.
///
/// This should be called for each frame the cross needs to be rendered.
///
@ -53,23 +55,17 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::WHITE;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.cross_2d(Vec2::ZERO, 0.0, 0.5, WHITE);
/// gizmos.cross_2d(Isometry2d::IDENTITY, 0.5, WHITE);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
pub fn cross_2d(
&mut self,
position: Vec2,
angle: f32,
half_size: f32,
color: impl Into<Color>,
) {
let axes = half_size * Mat2::from_angle(angle);
let local_x = axes.col(0);
let local_y = axes.col(1);
pub fn cross_2d(&mut self, isometry: Isometry2d, half_size: f32, color: impl Into<Color>) {
let color: Color = color.into();
self.line_2d(position + local_x, position - local_x, color);
self.line_2d(position + local_y, position - local_y, color);
[Vec2::X, Vec2::Y]
.map(|axis| axis * half_size)
.into_iter()
.for_each(|axis| {
self.line_2d(isometry * axis, isometry * (-axis), color);
});
}
}

View file

@ -8,7 +8,7 @@ use bevy_ecs::{
system::{Deferred, ReadOnlySystemParam, Res, Resource, SystemBuffer, SystemMeta, SystemParam},
world::{unsafe_world_cell::UnsafeWorldCell, World},
};
use bevy_math::{Quat, Rot2, Vec2, Vec3};
use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3};
use bevy_transform::TransformPoint;
use bevy_utils::default;
@ -452,7 +452,12 @@ where
strip_colors.push(LinearRgba::NAN);
}
/// Draw a wireframe rectangle in 3D.
/// Draw a wireframe rectangle in 3D with the given `isometry` applied.
///
/// If `isometry == Isometry3d::IDENTITY` then
///
/// - the center is at `Vec3::ZERO`
/// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.
///
/// This should be called for each frame the rectangle needs to be rendered.
///
@ -462,16 +467,16 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.rect(Vec3::ZERO, Quat::IDENTITY, Vec2::ONE, GREEN);
/// gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
#[inline]
pub fn rect(&mut self, position: Vec3, rotation: Quat, size: Vec2, color: impl Into<Color>) {
pub fn rect(&mut self, isometry: Isometry3d, size: Vec2, color: impl Into<Color>) {
if !self.enabled {
return;
}
let [tl, tr, br, bl] = rect_inner(size).map(|vec2| position + rotation * vec2.extend(0.));
let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));
self.linestrip([tl, tr, br, bl, tl], color);
}
@ -674,7 +679,12 @@ where
self.line_gradient_2d(start, start + vector, start_color, end_color);
}
/// Draw a wireframe rectangle in 2D.
/// Draw a wireframe rectangle in 2D with the given `isometry` applied.
///
/// If `isometry == Isometry2d::IDENTITY` then
///
/// - the center is at `Vec2::ZERO`
/// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.
///
/// This should be called for each frame the rectangle needs to be rendered.
///
@ -684,23 +694,16 @@ where
/// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.rect_2d(Vec2::ZERO, 0., Vec2::ONE, GREEN);
/// gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
#[inline]
pub fn rect_2d(
&mut self,
position: Vec2,
rotation: impl Into<Rot2>,
size: Vec2,
color: impl Into<Color>,
) {
pub fn rect_2d(&mut self, isometry: Isometry2d, size: Vec2, color: impl Into<Color>) {
if !self.enabled {
return;
}
let rotation: Rot2 = rotation.into();
let [tl, tr, br, bl] = rect_inner(size).map(|vec2| position + rotation * vec2);
let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);
self.linestrip_2d([tl, tr, br, bl, tl], color);
}

View file

@ -5,7 +5,8 @@
use crate::prelude::{GizmoConfigGroup, Gizmos};
use bevy_color::Color;
use bevy_math::{Quat, UVec2, UVec3, Vec2, Vec3, Vec3Swizzles};
use bevy_math::Vec3Swizzles;
use bevy_math::{Isometry2d, Isometry3d, Quat, UVec2, UVec3, Vec2, Vec3};
/// A builder returned by [`Gizmos::grid_3d`]
pub struct GridBuilder3d<'a, 'w, 's, Config, Clear>
@ -14,8 +15,7 @@ where
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
spacing: Vec3,
cell_count: UVec3,
skew: Vec3,
@ -29,8 +29,7 @@ where
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
spacing: Vec2,
cell_count: UVec2,
skew: Vec2,
@ -147,8 +146,7 @@ where
fn drop(&mut self) {
draw_grid(
self.gizmos,
self.position,
self.rotation,
self.isometry,
self.spacing,
self.cell_count,
self.skew,
@ -166,8 +164,7 @@ where
fn drop(&mut self) {
draw_grid(
self.gizmos,
self.position,
self.rotation,
self.isometry,
self.spacing.extend(0.),
self.cell_count.extend(0),
self.skew.extend(0.),
@ -187,8 +184,11 @@ where
///
/// # Arguments
///
/// - `position`: The center point of the grid.
/// - `rotation`: defines the orientation of the grid, by default we assume the grid is contained in a plane parallel to the XY plane.
/// - `isometry` defines the translation and rotation of the grid.
/// - the translation specifies the center of the grid
/// - defines the orientation of the grid, by default
/// we assume the grid is contained in a plane parallel
/// to the XY plane
/// - `cell_count`: defines the amount of cells in the x and y axes
/// - `spacing`: defines the distance between cells along the x and y axes
/// - `color`: color of the grid
@ -205,8 +205,7 @@ where
/// # use bevy_color::palettes::basic::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.grid(
/// Vec3::ZERO,
/// Quat::IDENTITY,
/// Isometry3d::IDENTITY,
/// UVec2::new(10, 10),
/// Vec2::splat(2.),
/// GREEN
@ -218,16 +217,14 @@ where
/// ```
pub fn grid(
&mut self,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
cell_count: UVec2,
spacing: Vec2,
color: impl Into<Color>,
) -> GridBuilder2d<'_, 'w, 's, Config, Clear> {
GridBuilder2d {
gizmos: self,
position,
rotation,
isometry,
spacing,
cell_count,
skew: Vec2::ZERO,
@ -242,8 +239,10 @@ where
///
/// # Arguments
///
/// - `position`: The center point of the grid.
/// - `rotation`: defines the orientation of the grid, by default we assume the grid is contained in a plane parallel to the XY plane.
/// - `isometry` defines the translation and rotation of the grid.
/// - the translation specifies the center of the grid
/// - defines the orientation of the grid, by default
/// we assume the grid is aligned with all axes
/// - `cell_count`: defines the amount of cells in the x, y and z axes
/// - `spacing`: defines the distance between cells along the x, y and z axes
/// - `color`: color of the grid
@ -260,8 +259,7 @@ where
/// # use bevy_color::palettes::basic::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.grid_3d(
/// Vec3::ZERO,
/// Quat::IDENTITY,
/// Isometry3d::IDENTITY,
/// UVec3::new(10, 2, 10),
/// Vec3::splat(2.),
/// GREEN
@ -273,16 +271,14 @@ where
/// ```
pub fn grid_3d(
&mut self,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
cell_count: UVec3,
spacing: Vec3,
color: impl Into<Color>,
) -> GridBuilder3d<'_, 'w, 's, Config, Clear> {
GridBuilder3d {
gizmos: self,
position,
rotation,
isometry,
spacing,
cell_count,
skew: Vec3::ZERO,
@ -297,8 +293,10 @@ where
///
/// # Arguments
///
/// - `position`: The center point of the grid.
/// - `rotation`: defines the orientation of the grid.
/// - `isometry` defines the translation and rotation of the grid.
/// - the translation specifies the center of the grid
/// - defines the orientation of the grid, by default
/// we assume the grid is aligned with all axes
/// - `cell_count`: defines the amount of cells in the x and y axes
/// - `spacing`: defines the distance between cells along the x and y axes
/// - `color`: color of the grid
@ -315,8 +313,7 @@ where
/// # use bevy_color::palettes::basic::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.grid_2d(
/// Vec2::ZERO,
/// 0.0,
/// Isometry2d::IDENTITY,
/// UVec2::new(10, 10),
/// Vec2::splat(1.),
/// GREEN
@ -328,16 +325,17 @@ where
/// ```
pub fn grid_2d(
&mut self,
position: Vec2,
rotation: f32,
isometry: Isometry2d,
cell_count: UVec2,
spacing: Vec2,
color: impl Into<Color>,
) -> GridBuilder2d<'_, 'w, 's, Config, Clear> {
GridBuilder2d {
gizmos: self,
position: position.extend(0.),
rotation: Quat::from_rotation_z(rotation),
isometry: Isometry3d::new(
isometry.translation.extend(0.0),
Quat::from_rotation_z(isometry.rotation.as_radians()),
),
spacing,
cell_count,
skew: Vec2::ZERO,
@ -350,8 +348,7 @@ where
#[allow(clippy::too_many_arguments)]
fn draw_grid<Config, Clear>(
gizmos: &mut Gizmos<'_, '_, Config, Clear>,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
spacing: Vec3,
cell_count: UVec3,
skew: Vec3,
@ -428,7 +425,7 @@ fn draw_grid<Config, Clear>(
x_lines
.chain(y_lines)
.chain(z_lines)
.map(|ps| ps.map(|p| position + rotation * p))
.map(|vec3s| vec3s.map(|vec3| isometry * vec3))
.for_each(|[start, end]| {
gizmos.line(start, end, color);
});

View file

@ -19,7 +19,7 @@ use bevy_ecs::{
};
use bevy_math::{
primitives::{Cone, Sphere},
Quat, Vec3,
Isometry3d, Quat, Vec3,
};
use bevy_pbr::{DirectionalLight, PointLight, SpotLight};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
@ -44,13 +44,16 @@ fn point_light_gizmo(
&Sphere {
radius: point_light.radius,
},
position,
Quat::IDENTITY,
Isometry3d::from_translation(position),
color,
)
.resolution(16);
gizmos
.sphere(position, Quat::IDENTITY, point_light.range, color)
.sphere(
Isometry3d::from_translation(position),
point_light.range,
color,
)
.resolution(32);
}
@ -68,8 +71,7 @@ fn spot_light_gizmo(
&Sphere {
radius: spot_light.radius,
},
translation,
Quat::IDENTITY,
Isometry3d::from_translation(translation),
color,
)
.resolution(16);
@ -84,8 +86,7 @@ fn spot_light_gizmo(
radius: spot_light.range * angle.sin(),
height,
},
position,
rotation * Quat::from_rotation_x(PI / 2.0),
Isometry3d::new(position, rotation * Quat::from_rotation_x(PI / 2.0)),
color,
)
.height_resolution(4)
@ -105,8 +106,7 @@ fn spot_light_gizmo(
.arc_3d(
2.0 * spot_light.outer_angle,
spot_light.range,
translation,
rotation * arc_rotation,
Isometry3d::new(translation, rotation * arc_rotation),
color,
)
.resolution(16);

View file

@ -10,7 +10,7 @@ use bevy_math::primitives::{
CircularSegment, Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Primitive2d, Rectangle,
RegularPolygon, Rhombus, Segment2d, Triangle2d,
};
use bevy_math::{Dir2, Isometry2d, Mat2, Rot2, Vec2};
use bevy_math::{Dir2, Isometry2d, Rot2, Vec2};
use crate::prelude::{GizmoConfigGroup, Gizmos};
@ -31,8 +31,7 @@ pub trait GizmoPrimitive2d<P: Primitive2d> {
fn primitive_2d(
&mut self,
primitive: &P,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_>;
}
@ -49,19 +48,15 @@ where
fn primitive_2d(
&mut self,
primitive: &Dir2,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<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);
let start = Vec2::ZERO;
let end = *primitive * MIN_LINE_LEN;
self.arrow_2d(isometry * start, isometry * end, color);
}
}
@ -77,16 +72,17 @@ where
fn primitive_2d(
&mut self,
primitive: &Arc2d,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let start_iso = isometry * Isometry2d::from_rotation(Rot2::radians(-primitive.half_angle));
self.arc_2d(
Isometry2d::new(position, Rot2::radians(angle - primitive.half_angle)),
start_iso,
primitive.half_angle * 2.0,
primitive.radius,
color,
@ -106,11 +102,10 @@ where
fn primitive_2d(
&mut self,
primitive: &Circle,
position: Vec2,
_angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
self.circle_2d(position, primitive.radius, color)
self.circle_2d(isometry, primitive.radius, color)
}
}
@ -126,8 +121,7 @@ where
fn primitive_2d(
&mut self,
primitive: &CircularSector,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -136,20 +130,21 @@ where
let color = color.into();
let start_iso =
isometry * Isometry2d::from_rotation(Rot2::radians(-primitive.arc.half_angle));
let end_iso = isometry * Isometry2d::from_rotation(Rot2::radians(primitive.arc.half_angle));
// we need to draw the arc part of the sector, and the two lines connecting the arc and the center
self.arc_2d(
Isometry2d::new(position, Rot2::radians(angle - primitive.arc.half_angle)),
start_iso,
primitive.arc.half_angle * 2.0,
primitive.arc.radius,
color,
);
let start = position
+ primitive.arc.radius * Mat2::from_angle(angle - primitive.arc.half_angle) * Vec2::Y;
let end = position
+ primitive.arc.radius * Mat2::from_angle(angle + primitive.arc.half_angle) * Vec2::Y;
self.line_2d(position, start, color);
self.line_2d(position, end, color);
let end_position = primitive.arc.radius * Vec2::Y;
self.line_2d(isometry * Vec2::ZERO, start_iso * end_position, color);
self.line_2d(isometry * Vec2::ZERO, end_iso * end_position, color);
}
}
@ -165,8 +160,7 @@ where
fn primitive_2d(
&mut self,
primitive: &CircularSegment,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -175,19 +169,20 @@ where
let color = color.into();
let start_iso =
isometry * Isometry2d::from_rotation(Rot2::radians(-primitive.arc.half_angle));
let end_iso = isometry * Isometry2d::from_rotation(Rot2::radians(primitive.arc.half_angle));
// we need to draw the arc part of the segment, and the line connecting the two ends
self.arc_2d(
Isometry2d::new(position, Rot2::radians(angle - primitive.arc.half_angle)),
start_iso,
primitive.arc.half_angle * 2.0,
primitive.arc.radius,
color,
);
let start = position
+ primitive.arc.radius * Mat2::from_angle(angle - primitive.arc.half_angle) * Vec2::Y;
let end = position
+ primitive.arc.radius * Mat2::from_angle(angle + primitive.arc.half_angle) * Vec2::Y;
self.line_2d(end, start, color);
let position = primitive.arc.radius * Vec2::Y;
self.line_2d(start_iso * position, end_iso * position, color);
}
}
@ -203,11 +198,10 @@ where
fn primitive_2d<'a>(
&mut self,
primitive: &Ellipse,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
self.ellipse_2d(position, angle, primitive.half_size, color)
self.ellipse_2d(isometry, primitive.half_size, color)
}
}
@ -220,7 +214,7 @@ where
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
position: Vec2,
isometry: Isometry2d,
inner_radius: f32,
outer_radius: f32,
color: Color,
@ -263,13 +257,12 @@ where
fn primitive_2d(
&mut self,
primitive: &Annulus,
position: Vec2,
_angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Annulus2dBuilder {
gizmos: self,
position,
isometry,
inner_radius: primitive.inner_circle.radius,
outer_radius: primitive.outer_circle.radius,
color: color.into(),
@ -291,7 +284,7 @@ where
let Annulus2dBuilder {
gizmos,
position,
isometry,
inner_radius,
outer_radius,
inner_resolution,
@ -301,10 +294,10 @@ where
} = self;
gizmos
.circle_2d(*position, *outer_radius, *color)
.circle_2d(*isometry, *outer_radius, *color)
.resolution(*outer_resolution);
gizmos
.circle_2d(*position, *inner_radius, *color)
.circle_2d(*isometry, *inner_radius, *color)
.resolution(*inner_resolution);
}
}
@ -321,8 +314,7 @@ where
fn primitive_2d(
&mut self,
primitive: &Rhombus,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -335,7 +327,7 @@ where
primitive.half_diagonals.y * sign_y,
)
});
let positions = [a, b, c, d, a].map(rotate_then_translate_2d(angle, position));
let positions = [a, b, c, d, a].map(|vec2| isometry * vec2);
self.linestrip_2d(positions, color);
}
}
@ -352,8 +344,7 @@ where
fn primitive_2d(
&mut self,
primitive: &Capsule2d,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
let polymorphic_color: Color = color.into();
@ -377,14 +368,14 @@ where
let scaling = Vec2::X * primitive.radius + Vec2::Y * primitive.half_length;
reference_point * scaling
})
.map(rotate_then_translate_2d(angle, position));
.map(|vec2| isometry * vec2);
// draw left and right side of capsule "rectangle"
self.line_2d(bottom_left, top_left, polymorphic_color);
self.line_2d(bottom_right, top_right, polymorphic_color);
let start_angle_top = angle - FRAC_PI_2;
let start_angle_bottom = angle + FRAC_PI_2;
let start_angle_top = isometry.rotation.as_radians() - FRAC_PI_2;
let start_angle_bottom = isometry.rotation.as_radians() + FRAC_PI_2;
// draw arcs
self.arc_2d(
@ -414,9 +405,8 @@ where
direction: Dir2, // Direction of the line
position: Vec2, // position of the center of the line
rotation: Mat2, // rotation of the line
color: Color, // color of the line
isometry: Isometry2d,
color: Color, // color of the line
draw_arrow: bool, // decides whether to indicate the direction of the line with an arrow
}
@ -443,15 +433,13 @@ where
fn primitive_2d(
&mut self,
primitive: &Line2d,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Line2dBuilder {
gizmos: self,
direction: primitive.direction,
position,
rotation: Mat2::from_angle(angle),
isometry,
color: color.into(),
draw_arrow: false,
}
@ -468,22 +456,20 @@ where
return;
}
let direction = self.rotation * *self.direction;
let [start, end] = [1.0, -1.0]
.map(|sign| sign * INFINITE_LEN)
// offset the line from the origin infinitely into the given direction
.map(|length| direction * length)
// translate the line to the given position
.map(|offset| self.position + offset);
.map(|length| self.direction * length)
// transform the line with the given isometry
.map(|offset| self.isometry * offset);
self.gizmos.line_2d(start, end, self.color);
// optionally draw an arrow head at the center of the line
if self.draw_arrow {
self.gizmos.arrow_2d(
self.position - direction * MIN_LINE_LEN,
self.position,
self.isometry * (-self.direction * MIN_LINE_LEN),
self.isometry * Vec2::ZERO,
self.color,
);
}
@ -502,8 +488,7 @@ where
fn primitive_2d(
&mut self,
primitive: &Plane2d,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
let polymorphic_color: Color = color.into();
@ -511,8 +496,6 @@ where
if !self.enabled {
return;
}
let rotation = Mat2::from_angle(angle);
// draw normal of the plane (orthogonal to the plane itself)
let normal = primitive.normal;
let normal_segment = Segment2d {
@ -522,22 +505,21 @@ where
self.primitive_2d(
&normal_segment,
// offset the normal so it starts on the plane line
position + HALF_MIN_LINE_LEN * rotation * *normal,
angle,
Isometry2d::new(isometry * (HALF_MIN_LINE_LEN * normal), isometry.rotation),
polymorphic_color,
)
.draw_arrow(true);
// draw the plane line
let direction = Dir2::new_unchecked(-normal.perp());
self.primitive_2d(&Line2d { direction }, position, angle, polymorphic_color)
self.primitive_2d(&Line2d { direction }, isometry, polymorphic_color)
.draw_arrow(false);
// draw an arrow such that the normal is always left side of the plane with respect to the
// planes direction. This is to follow the "counter-clockwise" convention
self.arrow_2d(
position,
position + MIN_LINE_LEN * (rotation * *direction),
isometry * Vec2::ZERO,
isometry * (MIN_LINE_LEN * direction),
polymorphic_color,
);
}
@ -556,9 +538,8 @@ where
direction: Dir2, // Direction of the line segment
half_length: f32, // Half-length of the line segment
position: Vec2, // position of the center of the line segment
rotation: Mat2, // rotation of the line segment
color: Color, // color of the line segment
isometry: Isometry2d, // isometric transformation of the line segment
color: Color, // color of the line segment
draw_arrow: bool, // decides whether to draw just a line or an arrow
}
@ -585,8 +566,7 @@ where
fn primitive_2d(
&mut self,
primitive: &Segment2d,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Segment2dBuilder {
@ -594,8 +574,7 @@ where
direction: primitive.direction,
half_length: primitive.half_length,
position,
rotation: Mat2::from_angle(angle),
isometry,
color: color.into(),
draw_arrow: Default::default(),
@ -613,9 +592,9 @@ where
return;
}
let direction = self.rotation * *self.direction;
let start = self.position - direction * self.half_length;
let end = self.position + direction * self.half_length;
let direction = self.direction * self.half_length;
let start = self.isometry * (-direction);
let end = self.isometry * direction;
if self.draw_arrow {
self.gizmos.arrow_2d(start, end, self.color);
@ -638,8 +617,7 @@ where
fn primitive_2d(
&mut self,
primitive: &Polyline2d<N>,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -651,7 +629,7 @@ where
.vertices
.iter()
.copied()
.map(rotate_then_translate_2d(angle, position)),
.map(|vec2| isometry * vec2),
color,
);
}
@ -669,8 +647,7 @@ where
fn primitive_2d(
&mut self,
primitive: &BoxedPolyline2d,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -682,7 +659,7 @@ where
.vertices
.iter()
.copied()
.map(rotate_then_translate_2d(angle, position)),
.map(|vec2| isometry * vec2),
color,
);
}
@ -700,15 +677,14 @@ where
fn primitive_2d(
&mut self,
primitive: &Triangle2d,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<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));
let positions = [a, b, c, a].map(|vec2| isometry * vec2);
self.linestrip_2d(positions, color);
}
}
@ -725,8 +701,7 @@ where
fn primitive_2d(
&mut self,
primitive: &Rectangle,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -740,7 +715,7 @@ where
primitive.half_size.y * sign_y,
)
});
let positions = [a, b, c, d, a].map(rotate_then_translate_2d(angle, position));
let positions = [a, b, c, d, a].map(|vec2| isometry * vec2);
self.linestrip_2d(positions, color);
}
}
@ -758,8 +733,7 @@ where
fn primitive_2d(
&mut self,
primitive: &Polygon<N>,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -781,7 +755,7 @@ where
.iter()
.copied()
.chain(closing_point)
.map(rotate_then_translate_2d(angle, position)),
.map(|vec2| isometry * vec2),
color,
);
}
@ -799,8 +773,7 @@ where
fn primitive_2d(
&mut self,
primitive: &BoxedPolygon,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -820,7 +793,7 @@ where
.iter()
.copied()
.chain(closing_point)
.map(rotate_then_translate_2d(angle, position)),
.map(|vec2| isometry * vec2),
color,
);
}
@ -838,8 +811,7 @@ where
fn primitive_2d(
&mut self,
primitive: &RegularPolygon,
position: Vec2,
angle: f32,
isometry: Isometry2d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -847,8 +819,8 @@ where
}
let points = (0..=primitive.sides)
.map(|p| single_circle_coordinate(primitive.circumcircle.radius, primitive.sides, p))
.map(rotate_then_translate_2d(angle, position));
.map(|n| single_circle_coordinate(primitive.circumcircle.radius, primitive.sides, n))
.map(|vec2| isometry * vec2);
self.linestrip_2d(points, color);
}
}

View file

@ -1,14 +1,14 @@
//! A module for rendering each of the 3D [`bevy_math::primitives`] with [`Gizmos`].
use super::helpers::*;
use std::f32::consts::{FRAC_PI_2, PI, TAU};
use std::f32::consts::TAU;
use bevy_color::Color;
use bevy_math::primitives::{
BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d,
Polyline3d, Primitive3d, Segment3d, Sphere, Tetrahedron, Torus, Triangle3d,
};
use bevy_math::{Dir3, Quat, Vec3};
use bevy_math::{Dir3, Isometry3d, Quat, Vec3};
use crate::circles::SphereBuilder;
use crate::prelude::{GizmoConfigGroup, Gizmos};
@ -28,8 +28,7 @@ pub trait GizmoPrimitive3d<P: Primitive3d> {
fn primitive_3d(
&mut self,
primitive: &P,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_>;
}
@ -46,11 +45,12 @@ where
fn primitive_3d(
&mut self,
primitive: &Dir3,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
self.arrow(position, position + (rotation * **primitive), color);
let start = Vec3::ZERO;
let end = primitive.as_vec3();
self.arrow(isometry * start, isometry * end, color);
}
}
@ -66,11 +66,10 @@ where
fn primitive_3d(
&mut self,
primitive: &Sphere,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
self.sphere(position, rotation, primitive.radius, color)
self.sphere(isometry, primitive.radius, color)
}
}
@ -87,10 +86,7 @@ where
// direction of the normal orthogonal to the plane
normal: Dir3,
// Rotation of the plane around the origin in 3D space
rotation: Quat,
// Center position of the plane in 3D space
position: Vec3,
isometry: Isometry3d,
// Color of the plane
color: Color,
@ -136,15 +132,13 @@ where
fn primitive_3d(
&mut self,
primitive: &Plane3d,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Plane3dBuilder {
gizmos: self,
normal: primitive.normal,
rotation,
position,
isometry,
color: color.into(),
axis_count: 4,
segment_count: 3,
@ -164,37 +158,33 @@ where
}
// draws the normal
let normal = self.rotation * *self.normal;
self.gizmos
.primitive_3d(&self.normal, self.position, self.rotation, self.color);
let normals_normal = self.rotation * self.normal.any_orthonormal_vector();
.primitive_3d(&self.normal, self.isometry, self.color);
// draws the axes
// get rotation for each direction
let normals_normal = self.normal.any_orthonormal_vector();
(0..self.axis_count)
.map(|i| i as f32 * (1.0 / self.axis_count as f32) * TAU)
.map(|angle| Quat::from_axis_angle(normal, angle))
.for_each(|quat| {
let axis_direction = quat * normals_normal;
let direction = Dir3::new_unchecked(axis_direction);
.map(|angle| Quat::from_axis_angle(self.normal.as_vec3(), angle))
.flat_map(|quat| {
let segment_length = self.segment_length;
let isometry = self.isometry;
// for each axis draw dotted line
(0..)
.filter(|i| i % 2 != 0)
.map(|percent| (percent as f32 + 0.5) * self.segment_length * axis_direction)
.map(|position| position + self.position)
.take(self.segment_count as usize)
.for_each(|position| {
self.gizmos.primitive_3d(
&Segment3d {
direction,
half_length: self.segment_length * 0.5,
},
position,
Quat::IDENTITY,
self.color,
);
});
.map(|i| [i, i + 1])
.map(move |percents| {
percents
.map(|percent| percent as f32 + 0.5)
.map(|percent| percent * segment_length * normals_normal)
.map(|vec3| quat * vec3)
.map(|vec3| isometry * vec3)
})
})
.for_each(|[start, end]| {
self.gizmos.line(start, end, self.color);
});
}
}
@ -211,8 +201,7 @@ where
fn primitive_3d(
&mut self,
primitive: &Line3d,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -220,13 +209,13 @@ where
}
let color = color.into();
let direction = rotation * *primitive.direction;
self.arrow(position, position + direction, color);
let direction = primitive.direction.as_vec3();
self.arrow(isometry * Vec3::ZERO, isometry * direction, color);
let [start, end] = [1.0, -1.0]
.map(|sign| sign * INFINITE_LEN)
.map(|length| direction * length)
.map(|offset| position + offset);
.map(|length| primitive.direction * length)
.map(|offset| isometry * offset);
self.line(start, end, color);
}
}
@ -243,18 +232,15 @@ where
fn primitive_3d(
&mut self,
primitive: &Segment3d,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let direction = rotation * *primitive.direction;
let start = position - direction * primitive.half_length;
let end = position + direction * primitive.half_length;
self.line(start, end, color);
let direction = primitive.direction.as_vec3();
self.line(isometry * direction, isometry * (-direction), color);
}
}
@ -271,20 +257,14 @@ where
fn primitive_3d(
&mut self,
primitive: &Polyline3d<N>,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
self.linestrip(
primitive
.vertices
.map(rotate_then_translate_3d(rotation, position)),
color,
);
self.linestrip(primitive.vertices.map(|vec3| isometry * vec3), color);
}
}
@ -300,8 +280,7 @@ where
fn primitive_3d(
&mut self,
primitive: &BoxedPolyline3d,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -313,7 +292,7 @@ where
.vertices
.iter()
.copied()
.map(rotate_then_translate_3d(rotation, position)),
.map(|vec3| isometry * vec3),
color,
);
}
@ -331,8 +310,7 @@ where
fn primitive_3d(
&mut self,
primitive: &Triangle3d,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
@ -340,10 +318,7 @@ where
}
let [a, b, c] = primitive.vertices;
self.linestrip(
[a, b, c, a].map(rotate_then_translate_3d(rotation, position)),
color,
);
self.linestrip([a, b, c, a].map(|vec3| isometry * vec3), color);
}
}
@ -359,16 +334,13 @@ where
fn primitive_3d(
&mut self,
primitive: &Cuboid,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let [half_extend_x, half_extend_y, half_extend_z] = primitive.half_size.to_array();
// transform the points from the reference unit cube to the cuboid coords
let vertices @ [a, b, c, d, e, f, g, h] = [
[1.0, 1.0, 1.0],
@ -380,8 +352,9 @@ where
[-1.0, -1.0, -1.0],
[1.0, -1.0, -1.0],
]
.map(|[sx, sy, sz]| Vec3::new(sx * half_extend_x, sy * half_extend_y, sz * half_extend_z))
.map(rotate_then_translate_3d(rotation, position));
.map(Vec3::from)
.map(|vec3| vec3 * primitive.half_size)
.map(|vec3| isometry * vec3);
// lines for the upper rectangle of the cuboid
let upper = [a, b, c, d]
@ -421,12 +394,7 @@ where
// Half height of the cylinder
half_height: f32,
// Center position of the cylinder
position: Vec3,
// Rotation of the cylinder
//
// default orientation is: the cylinder is aligned with `Vec3::Y` axis
rotation: Quat,
isometry: Isometry3d,
// Color of the cylinder
color: Color,
@ -456,16 +424,14 @@ where
fn primitive_3d(
&mut self,
primitive: &Cylinder,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Cylinder3dBuilder {
gizmos: self,
radius: primitive.radius,
half_height: primitive.half_height,
position,
rotation,
isometry,
color: color.into(),
resolution: DEFAULT_RESOLUTION,
}
@ -482,37 +448,17 @@ where
return;
}
let Cylinder3dBuilder {
gizmos,
radius,
half_height,
position,
rotation,
color,
resolution,
} = self;
let normal = Dir3::new_unchecked(*rotation * Vec3::Y);
let up = normal.as_vec3() * *half_height;
// draw upper and lower circle of the cylinder
[-1.0, 1.0].into_iter().for_each(|sign| {
gizmos
.circle(*position + sign * up, normal, *radius, *color)
.resolution(*resolution);
});
// draw lines connecting the two cylinder circles
[Vec3::NEG_X, Vec3::NEG_Z, Vec3::X, Vec3::Z]
.into_iter()
.for_each(|axis| {
let axis = *rotation * axis;
gizmos.line(
*position + up + axis * *radius,
*position - up + axis * *radius,
*color,
);
});
self.gizmos
.primitive_3d(
&ConicalFrustum {
radius_top: self.radius,
radius_bottom: self.radius,
height: self.half_height * 2.0,
},
self.isometry,
self.color,
)
.resolution(self.resolution);
}
}
@ -531,12 +477,7 @@ where
// Half length of the capsule
half_length: f32,
// Center position of the capsule
position: Vec3,
// Rotation of the capsule
//
// default orientation is: the capsule is aligned with `Vec3::Y` axis
rotation: Quat,
isometry: Isometry3d,
// Color of the capsule
color: Color,
@ -566,16 +507,14 @@ where
fn primitive_3d(
&mut self,
primitive: &Capsule3d,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Capsule3dBuilder {
gizmos: self,
radius: primitive.radius,
half_length: primitive.half_length,
position,
rotation,
isometry,
color: color.into(),
resolution: DEFAULT_RESOLUTION,
}
@ -592,66 +531,39 @@ where
return;
}
let Capsule3dBuilder {
gizmos,
radius,
half_length,
position,
rotation,
color,
resolution,
} = self;
let [upper_apex, lower_apex] = [-1.0, 1.0]
.map(|sign| Vec3::Y * sign * (self.half_length + self.radius))
.map(|vec3| self.isometry * vec3);
let [upper_center, lower_center] = [-1.0, 1.0]
.map(|sign| Vec3::Y * sign * self.half_length)
.map(|vec3| self.isometry * vec3);
let [upper_points, lower_points] = [-1.0, 1.0]
.map(|sign| Vec3::Y * sign * self.half_length)
.map(|vec3| {
circle_coordinates_closed(self.radius, self.resolution)
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + vec3)
.map(|vec3| self.isometry * vec3)
.collect::<Vec<_>>()
});
// Draw the circles at the top and bottom of the cylinder
let y_offset = *rotation * Vec3::Y;
gizmos
.circle(
*position + y_offset * *half_length,
Dir3::new_unchecked(y_offset),
*radius,
*color,
)
.resolution(*resolution);
gizmos
.circle(
*position - y_offset * *half_length,
Dir3::new_unchecked(y_offset),
*radius,
*color,
)
.resolution(*resolution);
let y_offset = y_offset * *half_length;
upper_points.iter().skip(1).copied().for_each(|start| {
self.gizmos
.short_arc_3d_between(upper_center, start, upper_apex, self.color);
});
lower_points.iter().skip(1).copied().for_each(|start| {
self.gizmos
.short_arc_3d_between(lower_center, start, lower_apex, self.color);
});
// Draw the vertical lines and the cap semicircles
[Vec3::X, Vec3::Z].into_iter().for_each(|axis| {
let normal = *rotation * axis;
let upper_lines = upper_points.windows(2).map(|win| (win[0], win[1]));
let lower_lines = lower_points.windows(2).map(|win| (win[0], win[1]));
upper_lines.chain(lower_lines).for_each(|(start, end)| {
self.gizmos.line(start, end, self.color);
});
gizmos.line(
*position + normal * *radius + y_offset,
*position + normal * *radius - y_offset,
*color,
);
gizmos.line(
*position - normal * *radius + y_offset,
*position - normal * *radius - y_offset,
*color,
);
let rotation = *rotation
* Quat::from_euler(bevy_math::EulerRot::ZYX, 0., axis.z * FRAC_PI_2, FRAC_PI_2);
gizmos
.arc_3d(PI, *radius, *position + y_offset, rotation, *color)
.resolution(*resolution / 2);
gizmos
.arc_3d(
PI,
*radius,
*position - y_offset,
rotation * Quat::from_rotation_y(PI),
*color,
)
.resolution(*resolution / 2);
let connection_lines = upper_points.into_iter().zip(lower_points).skip(1);
connection_lines.for_each(|(start, end)| {
self.gizmos.line(start, end, self.color);
});
}
}
@ -671,12 +583,7 @@ where
// Height of the cone
height: f32,
// Center of the cone, half-way between the tip and the base
position: Vec3,
// Rotation of the cone
//
// default orientation is: cone base normal is aligned with the `Vec3::Y` axis
rotation: Quat,
isometry: Isometry3d,
// Color of the cone
color: Color,
@ -728,16 +635,14 @@ where
fn primitive_3d(
&mut self,
primitive: &Cone,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Cone3dBuilder {
gizmos: self,
radius: primitive.radius,
height: primitive.height,
position,
rotation,
isometry,
color: color.into(),
base_resolution: DEFAULT_RESOLUTION,
height_resolution: DEFAULT_RESOLUTION,
@ -755,37 +660,29 @@ where
return;
}
let Cone3dBuilder {
gizmos,
radius,
height,
position,
rotation,
color,
base_resolution,
height_resolution,
} = self;
let half_height = self.height * 0.5;
let apex = self.isometry * (Vec3::Y * half_height);
let circle_center = half_height * Vec3::NEG_Y;
let circle_coords = circle_coordinates_closed(self.radius, self.height_resolution)
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + circle_center)
.map(|vec3| self.isometry * vec3)
.collect::<Vec<_>>();
let half_height = *height * 0.5;
// connections to apex
circle_coords
.iter()
.skip(1)
.map(|vec3| (*vec3, apex))
.for_each(|(start, end)| {
self.gizmos.line(start, end, self.color);
});
// draw the base circle of the cone
draw_circle_3d(
gizmos,
*radius,
*base_resolution,
*rotation,
*position - *rotation * Vec3::Y * half_height,
*color,
);
// connect the base circle with the tip of the cone
let end = Vec3::Y * half_height;
circle_coordinates(*radius, *height_resolution)
.map(|p| Vec3::new(p.x, -half_height, p.y))
.map(move |p| [p, end])
.map(|ps| ps.map(rotate_then_translate_3d(*rotation, *position)))
.for_each(|[start, end]| {
gizmos.line(start, end, *color);
// base circle
circle_coords
.windows(2)
.map(|win| (win[0], win[1]))
.for_each(|(start, end)| {
self.gizmos.line(start, end, self.color);
});
}
}
@ -807,12 +704,7 @@ where
// Height of the conical frustum
height: f32,
// Center of conical frustum, half-way between the top and the bottom
position: Vec3,
// Rotation of the conical frustum
//
// default orientation is: conical frustum base shape normals are aligned with `Vec3::Y` axis
rotation: Quat,
isometry: Isometry3d,
// Color of the conical frustum
color: Color,
@ -842,8 +734,7 @@ where
fn primitive_3d(
&mut self,
primitive: &ConicalFrustum,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
ConicalFrustum3dBuilder {
@ -851,8 +742,7 @@ where
radius_top: primitive.radius_top,
radius_bottom: primitive.radius_bottom,
height: primitive.height,
position,
rotation,
isometry,
color: color.into(),
resolution: DEFAULT_RESOLUTION,
}
@ -869,46 +759,26 @@ where
return;
}
let ConicalFrustum3dBuilder {
gizmos,
radius_top,
radius_bottom,
height,
position,
rotation,
color,
resolution,
} = self;
let half_height = *height * 0.5;
let normal = *rotation * Vec3::Y;
// draw the two circles of the conical frustum
[(*radius_top, half_height), (*radius_bottom, -half_height)]
.into_iter()
.for_each(|(radius, height)| {
draw_circle_3d(
gizmos,
radius,
*resolution,
*rotation,
*position + height * normal,
*color,
);
let half_height = self.height * 0.5;
let [upper_points, lower_points] = [(-1.0, self.radius_bottom), (1.0, self.radius_top)]
.map(|(sign, radius)| {
let translation = Vec3::Y * sign * half_height;
circle_coordinates_closed(radius, self.resolution)
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation)
.map(|vec3| self.isometry * vec3)
.collect::<Vec<_>>()
});
// connect the two circles of the conical frustum
circle_coordinates(*radius_top, *resolution)
.map(move |p| Vec3::new(p.x, half_height, p.y))
.zip(
circle_coordinates(*radius_bottom, *resolution)
.map(|p| Vec3::new(p.x, -half_height, p.y)),
)
.map(|(start, end)| [start, end])
.map(|ps| ps.map(rotate_then_translate_3d(*rotation, *position)))
.for_each(|[start, end]| {
gizmos.line(start, end, *color);
});
let upper_lines = upper_points.windows(2).map(|win| (win[0], win[1]));
let lower_lines = lower_points.windows(2).map(|win| (win[0], win[1]));
upper_lines.chain(lower_lines).for_each(|(start, end)| {
self.gizmos.line(start, end, self.color);
});
let connection_lines = upper_points.into_iter().zip(lower_points).skip(1);
connection_lines.for_each(|(start, end)| {
self.gizmos.line(start, end, self.color);
});
}
}
@ -927,12 +797,7 @@ where
// Radius of the major circle (ring)
major_radius: f32,
// Center of the torus
position: Vec3,
// Rotation of the conical frustum
//
// default orientation is: major circle normal is aligned with `Vec3::Y` axis
rotation: Quat,
isometry: Isometry3d,
// Color of the torus
color: Color,
@ -970,16 +835,14 @@ where
fn primitive_3d(
&mut self,
primitive: &Torus,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
Torus3dBuilder {
gizmos: self,
minor_radius: primitive.minor_radius,
major_radius: primitive.major_radius,
position,
rotation,
isometry,
color: color.into(),
minor_resolution: DEFAULT_RESOLUTION,
major_resolution: DEFAULT_RESOLUTION,
@ -997,62 +860,42 @@ where
return;
}
let Torus3dBuilder {
gizmos,
minor_radius,
major_radius,
position,
rotation,
color,
minor_resolution,
major_resolution,
} = self;
let normal = *rotation * Vec3::Y;
// draw 4 circles with major_radius
[
(*major_radius - *minor_radius, 0.0),
(*major_radius + *minor_radius, 0.0),
(*major_radius, *minor_radius),
(*major_radius, -*minor_radius),
let [inner, outer, top, bottom] = [
(self.major_radius - self.minor_radius, 0.0),
(self.major_radius + self.minor_radius, 0.0),
(self.major_radius, self.minor_radius),
(self.major_radius, -self.minor_radius),
]
.into_iter()
.for_each(|(radius, height)| {
draw_circle_3d(
gizmos,
radius,
*major_resolution,
*rotation,
*position + height * normal,
*color,
);
.map(|(radius, height)| {
let translation = height * Vec3::Y;
circle_coordinates_closed(radius, self.major_resolution)
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation)
.map(|vec3| self.isometry * vec3)
.collect::<Vec<_>>()
});
// along the major circle draw orthogonal minor circles
let affine = rotate_then_translate_3d(*rotation, *position);
circle_coordinates(*major_radius, *major_resolution)
.map(|p| Vec3::new(p.x, 0.0, p.y))
.flat_map(|major_circle_point| {
let minor_center = affine(major_circle_point);
[&inner, &outer, &top, &bottom]
.iter()
.flat_map(|points| points.windows(2).map(|win| (win[0], win[1])))
.for_each(|(start, end)| {
self.gizmos.line(start, end, self.color);
});
// direction facing from the center of the torus towards the minor circles center
let dir_to_translation = (minor_center - *position).normalize();
// the minor circle is draw with 4 arcs this is done to make the minor circle
// connect properly with each of the major circles
let circle_points = [dir_to_translation, normal, -dir_to_translation, -normal]
.map(|offset| minor_center + offset.normalize() * *minor_radius);
circle_points
.into_iter()
.zip(circle_points.into_iter().cycle().skip(1))
.map(move |(from, to)| (minor_center, from, to))
.collect::<Vec<_>>()
inner
.into_iter()
.zip(top)
.zip(outer)
.zip(bottom)
.flat_map(|(((inner, top), outer), bottom)| {
let center = (inner + top + outer + bottom) * 0.25;
[(inner, top), (top, outer), (outer, bottom), (bottom, inner)]
.map(|(start, end)| (start, end, center))
})
.for_each(|(center, from, to)| {
gizmos
.short_arc_3d_between(center, from, to, *color)
.resolution(*minor_resolution);
.for_each(|(from, to, center)| {
self.gizmos
.short_arc_3d_between(center, from, to, self.color)
.resolution(self.minor_resolution);
});
}
}
@ -1065,23 +908,20 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d<Tetrahedron> for Gizmos<'w, '
fn primitive_3d(
&mut self,
primitive: &Tetrahedron,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
return;
}
let [a, b, c, d] = primitive
.vertices
.map(rotate_then_translate_3d(rotation, position));
let [a, b, c, d] = primitive.vertices.map(|vec3| isometry * vec3);
let lines = [(a, b), (a, c), (a, d), (b, c), (b, d), (c, d)];
let color = color.into();
for (a, b) in lines.into_iter() {
self.line(a, b, color);
}
lines.into_iter().for_each(|(start, end)| {
self.line(start, end, color);
});
}
}

View file

@ -1,28 +1,6 @@
use std::f32::consts::TAU;
use bevy_color::Color;
use bevy_math::{Mat2, Quat, Vec2, Vec3};
use crate::prelude::{GizmoConfigGroup, Gizmos};
/// Performs an isometric transformation on 2D vectors.
///
/// This function takes angle and a position vector, and returns a closure that applies
/// the isometric transformation to any given 2D vector. The transformation involves rotating
/// the vector by the specified angle and then translating it by the given position.
pub(crate) fn rotate_then_translate_2d(angle: f32, position: Vec2) -> impl Fn(Vec2) -> Vec2 {
move |v| Mat2::from_angle(angle) * v + position
}
/// Performs an isometric transformation on 3D vectors.
///
/// This function takes a quaternion representing rotation and a 3D vector representing
/// translation, and returns a closure that applies the isometric transformation to any
/// given 3D vector. The transformation involves rotating the vector by the specified
/// quaternion and then translating it by the given translation vector.
pub(crate) fn rotate_then_translate_3d(rotation: Quat, translation: Vec3) -> impl Fn(Vec3) -> Vec3 {
move |v| rotation * v + translation
}
use bevy_math::Vec2;
/// Calculates the `nth` coordinate of a circle.
///
@ -37,6 +15,8 @@ pub(crate) fn single_circle_coordinate(radius: f32, resolution: u32, nth_point:
/// Generates an iterator over the coordinates of a circle.
///
/// The coordinates form a open circle, meaning the first and last points aren't the same.
///
/// This function creates an iterator that yields the positions of points approximating a
/// circle with the given radius, divided into linear segments. The iterator produces `resolution`
/// number of points.
@ -46,27 +26,18 @@ pub(crate) fn circle_coordinates(radius: f32, resolution: u32) -> impl Iterator<
.take(resolution as usize)
}
/// Draws a circle in 3D space.
/// Generates an iterator over the coordinates of a circle.
///
/// # Note
/// The coordinates form a closed circle, meaning the first and last points are the same.
///
/// This function is necessary to use instead of `gizmos.circle` for certain primitives to ensure that points align correctly. For example, the major circles of a torus are drawn with this method, and using `gizmos.circle` would result in the minor circles not being positioned precisely on the major circles' segment points.
pub(crate) fn draw_circle_3d<Config, Clear>(
gizmos: &mut Gizmos<'_, '_, Config, Clear>,
/// This function creates an iterator that yields the positions of points approximating a
/// circle with the given radius, divided into linear segments. The iterator produces `resolution`
/// number of points.
pub(crate) fn circle_coordinates_closed(
radius: f32,
resolution: u32,
rotation: Quat,
translation: Vec3,
color: Color,
) where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
let positions = (0..=resolution)
.map(|frac| frac as f32 / resolution as f32)
.map(|percentage| percentage * TAU)
.map(|angle| Vec2::from(angle.sin_cos()) * radius)
.map(|p| Vec3::new(p.x, 0.0, p.y))
.map(rotate_then_translate_3d(rotation, translation));
gizmos.linestrip(positions, color);
) -> impl Iterator<Item = Vec2> {
circle_coordinates(radius, resolution).chain(std::iter::once(single_circle_coordinate(
radius, resolution, resolution,
)))
}

View file

@ -7,7 +7,7 @@ use std::f32::consts::FRAC_PI_2;
use crate::prelude::{GizmoConfigGroup, Gizmos};
use bevy_color::Color;
use bevy_math::{Quat, Vec2, Vec3};
use bevy_math::{Isometry2d, Isometry3d, Quat, Vec2, Vec3};
use bevy_transform::components::Transform;
/// A builder returned by [`Gizmos::rounded_rect`] and [`Gizmos::rounded_rect_2d`]
@ -23,8 +23,7 @@ pub struct RoundedCuboidBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
config: RoundedBoxConfig,
}
struct RoundedBoxConfig {
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
color: Color,
corner_radius: f32,
arc_resolution: u32,
@ -82,15 +81,14 @@ impl<T: GizmoConfigGroup> Drop for RoundedRectBuilder<'_, '_, '_, T> {
// Handle cases where the rectangle collapses into simpler shapes
if outer_half_size.x * outer_half_size.y == 0. {
self.gizmos.line(
config.position + config.rotation * -outer_half_size.extend(0.),
config.position + config.rotation * outer_half_size.extend(0.),
config.isometry * -outer_half_size.extend(0.),
config.isometry * outer_half_size.extend(0.),
config.color,
);
return;
}
if corner_radius == 0. {
self.gizmos
.rect(config.position, config.rotation, self.size, config.color);
self.gizmos.rect(config.isometry, self.size, config.color);
return;
}
@ -112,7 +110,7 @@ impl<T: GizmoConfigGroup> Drop for RoundedRectBuilder<'_, '_, '_, T> {
Vec3::new(-inner_half_size.x, inner_half_size.y, 0.),
Vec3::new(-inner_half_size.x, outer_half_size.y, 0.),
]
.map(|v| config.position + config.rotation * v);
.map(|vec3| config.isometry * vec3);
for chunk in vertices.chunks_exact(3) {
self.gizmos
@ -159,8 +157,8 @@ impl<T: GizmoConfigGroup> Drop for RoundedCuboidBuilder<'_, '_, '_, T> {
// Handle cases where the rounded cuboid collapses into simpler shapes
if edge_radius == 0.0 {
let transform = Transform::from_translation(config.position)
.with_rotation(config.rotation)
let transform = Transform::from_translation(config.isometry.translation.into())
.with_rotation(config.isometry.rotation)
.with_scale(self.size);
self.gizmos.cuboid(transform, config.color);
return;
@ -181,12 +179,10 @@ impl<T: GizmoConfigGroup> Drop for RoundedCuboidBuilder<'_, '_, '_, T> {
];
for (position, size, rotation) in rects {
let world_rotation = config.rotation * rotation;
let local_position = config.rotation * (position * inner_half_size);
let local_position = position * inner_half_size;
self.gizmos
.rounded_rect(
config.position + local_position,
world_rotation,
config.isometry * Isometry3d::new(local_position, rotation),
size,
config.color,
)
@ -195,8 +191,7 @@ impl<T: GizmoConfigGroup> Drop for RoundedCuboidBuilder<'_, '_, '_, T> {
self.gizmos
.rounded_rect(
config.position - local_position,
world_rotation,
config.isometry * Isometry3d::new(-local_position, rotation),
size,
config.color,
)
@ -213,8 +208,11 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
///
/// # Arguments
///
/// - `position`: The center point of the rectangle.
/// - `rotation`: defines orientation of the rectangle, by default we assume the rectangle is contained in a plane parallel to the XY plane.
/// - `isometry` defines the translation and rotation of the rectangle.
/// - the translation specifies the center of the rectangle
/// - defines orientation of the rectangle, by default we
/// assume the rectangle is contained in a plane parallel
/// to the XY plane.
/// - `size`: defines the size of the rectangle. This refers to the 'outer size', similar to a bounding box.
/// - `color`: color of the rectangle
///
@ -231,8 +229,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// # use bevy_color::palettes::css::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.rounded_rect(
/// Vec3::ZERO,
/// Quat::IDENTITY,
/// Isometry3d::IDENTITY,
/// Vec2::ONE,
/// GREEN
/// )
@ -243,8 +240,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// ```
pub fn rounded_rect(
&mut self,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
size: Vec2,
color: impl Into<Color>,
) -> RoundedRectBuilder<'_, 'w, 's, T> {
@ -252,8 +248,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
RoundedRectBuilder {
gizmos: self,
config: RoundedBoxConfig {
position,
rotation,
isometry,
color: color.into(),
corner_radius,
arc_resolution: DEFAULT_ARC_RESOLUTION,
@ -268,8 +263,10 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
///
/// # Arguments
///
/// - `position`: The center point of the rectangle.
/// - `rotation`: defines orientation of the rectangle.
/// - `isometry` defines the translation and rotation of the rectangle.
/// - the translation specifies the center of the rectangle
/// - defines orientation of the rectangle, by default we
/// assume the rectangle aligned with all axes.
/// - `size`: defines the size of the rectangle. This refers to the 'outer size', similar to a bounding box.
/// - `color`: color of the rectangle
///
@ -286,8 +283,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// # use bevy_color::palettes::css::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.rounded_rect_2d(
/// Vec2::ZERO,
/// 0.,
/// Isometry2d::IDENTITY,
/// Vec2::ONE,
/// GREEN
/// )
@ -298,8 +294,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// ```
pub fn rounded_rect_2d(
&mut self,
position: Vec2,
rotation: f32,
isometry: Isometry2d,
size: Vec2,
color: impl Into<Color>,
) -> RoundedRectBuilder<'_, 'w, 's, T> {
@ -307,8 +302,10 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
RoundedRectBuilder {
gizmos: self,
config: RoundedBoxConfig {
position: position.extend(0.),
rotation: Quat::from_rotation_z(rotation),
isometry: Isometry3d::new(
isometry.translation.extend(0.0),
Quat::from_rotation_z(isometry.rotation.as_radians()),
),
color: color.into(),
corner_radius,
arc_resolution: DEFAULT_ARC_RESOLUTION,
@ -323,8 +320,10 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
///
/// # Arguments
///
/// - `position`: The center point of the cuboid.
/// - `rotation`: defines orientation of the cuboid.
/// - `isometry` defines the translation and rotation of the cuboid.
/// - the translation specifies the center of the cuboid
/// - defines orientation of the cuboid, by default we
/// assume the cuboid aligned with all axes.
/// - `size`: defines the size of the cuboid. This refers to the 'outer size', similar to a bounding box.
/// - `color`: color of the cuboid
///
@ -341,8 +340,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// # use bevy_color::palettes::css::GREEN;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.rounded_cuboid(
/// Vec3::ZERO,
/// Quat::IDENTITY,
/// Isometry3d::IDENTITY,
/// Vec3::ONE,
/// GREEN
/// )
@ -353,8 +351,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
/// ```
pub fn rounded_cuboid(
&mut self,
position: Vec3,
rotation: Quat,
isometry: Isometry3d,
size: Vec3,
color: impl Into<Color>,
) -> RoundedCuboidBuilder<'_, 'w, 's, T> {
@ -362,8 +359,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
RoundedCuboidBuilder {
gizmos: self,
config: RoundedBoxConfig {
position,
rotation,
isometry,
color: color.into(),
corner_radius,
arc_resolution: DEFAULT_ARC_RESOLUTION,

View file

@ -1,6 +1,6 @@
//! This example demonstrates how to use the `Camera::viewport_to_world_2d` method.
use bevy::{color::palettes::basic::WHITE, prelude::*};
use bevy::{color::palettes::basic::WHITE, math::Isometry2d, prelude::*};
fn main() {
App::new()
@ -30,7 +30,7 @@ fn draw_cursor(
return;
};
gizmos.circle_2d(point, 10., WHITE);
gizmos.circle_2d(Isometry2d::from_translation(point), 10., WHITE);
}
fn setup(mut commands: Commands) {

View file

@ -105,24 +105,25 @@ fn render_shapes(mut gizmos: Gizmos, query: Query<(&Shape, &Transform)>) {
for (shape, transform) in query.iter() {
let translation = transform.translation.xy();
let rotation = transform.rotation.to_euler(EulerRot::YXZ).2;
let isometry = Isometry2d::new(translation, Rot2::radians(rotation));
match shape {
Shape::Rectangle(r) => {
gizmos.primitive_2d(r, translation, rotation, color);
gizmos.primitive_2d(r, isometry, color);
}
Shape::Circle(c) => {
gizmos.primitive_2d(c, translation, rotation, color);
gizmos.primitive_2d(c, isometry, color);
}
Shape::Triangle(t) => {
gizmos.primitive_2d(t, translation, rotation, color);
gizmos.primitive_2d(t, isometry, color);
}
Shape::Line(l) => {
gizmos.primitive_2d(l, translation, rotation, color);
gizmos.primitive_2d(l, isometry, color);
}
Shape::Capsule(c) => {
gizmos.primitive_2d(c, translation, rotation, color);
gizmos.primitive_2d(c, isometry, color);
}
Shape::Polygon(p) => {
gizmos.primitive_2d(p, translation, rotation, color);
gizmos.primitive_2d(p, isometry, color);
}
}
}
@ -185,10 +186,14 @@ fn render_volumes(mut gizmos: Gizmos, query: Query<(&CurrentVolume, &Intersects)
let color = if **intersects { AQUA } else { ORANGE_RED };
match volume {
CurrentVolume::Aabb(a) => {
gizmos.rect_2d(a.center(), 0., a.half_size() * 2., color);
gizmos.rect_2d(
Isometry2d::from_translation(a.center()),
a.half_size() * 2.,
color,
);
}
CurrentVolume::Circle(c) => {
gizmos.circle_2d(c.center(), c.radius(), color);
gizmos.circle_2d(Isometry2d::from_translation(c.center()), c.radius(), color);
}
}
}
@ -283,7 +288,7 @@ fn setup(mut commands: Commands) {
fn draw_filled_circle(gizmos: &mut Gizmos, position: Vec2, color: Srgba) {
for r in [1., 2., 3.] {
gizmos.circle_2d(position, r, color);
gizmos.circle_2d(Isometry2d::from_translation(position), r, color);
}
}
@ -353,8 +358,9 @@ fn aabb_cast_system(
**intersects = toi.is_some();
if let Some(toi) = toi {
gizmos.rect_2d(
aabb_cast.ray.ray.origin + *aabb_cast.ray.ray.direction * toi,
0.,
Isometry2d::from_translation(
aabb_cast.ray.ray.origin + *aabb_cast.ray.ray.direction * toi,
),
aabb_cast.aabb.half_size() * 2.,
LIME,
);
@ -382,7 +388,9 @@ fn bounding_circle_cast_system(
**intersects = toi.is_some();
if let Some(toi) = toi {
gizmos.circle_2d(
circle_cast.ray.ray.origin + *circle_cast.ray.ray.direction * toi,
Isometry2d::from_translation(
circle_cast.ray.ray.origin + *circle_cast.ray.ray.direction * toi,
),
circle_cast.circle.radius(),
LIME,
);
@ -403,7 +411,11 @@ fn aabb_intersection_system(
) {
let center = get_intersection_position(&time);
let aabb = Aabb2d::new(center, Vec2::splat(50.));
gizmos.rect_2d(center, 0., aabb.half_size() * 2., YELLOW);
gizmos.rect_2d(
Isometry2d::from_translation(center),
aabb.half_size() * 2.,
YELLOW,
);
for (volume, mut intersects) in volumes.iter_mut() {
let hit = match volume {
@ -422,7 +434,11 @@ fn circle_intersection_system(
) {
let center = get_intersection_position(&time);
let circle = BoundingCircle::new(center, 50.);
gizmos.circle_2d(center, circle.radius(), YELLOW);
gizmos.circle_2d(
Isometry2d::from_translation(center),
circle.radius(),
YELLOW,
);
for (volume, mut intersects) in volumes.iter_mut() {
let hit = match volume {

View file

@ -120,9 +120,17 @@ fn draw_bounds<Shape: Bounded2d + Send + Sync + 'static>(
let isometry = Isometry2d::new(translation, Rot2::radians(rotation));
let aabb = shape.0.aabb_2d(isometry);
gizmos.rect_2d(aabb.center(), 0.0, aabb.half_size() * 2.0, RED);
gizmos.rect_2d(
Isometry2d::from_translation(aabb.center()),
aabb.half_size() * 2.0,
RED,
);
let bounding_circle = shape.0.bounding_circle(isometry);
gizmos.circle_2d(bounding_circle.center, bounding_circle.radius(), BLUE);
gizmos.circle_2d(
Isometry2d::from_translation(bounding_circle.center),
bounding_circle.radius(),
BLUE,
);
}
}

View file

@ -37,7 +37,14 @@ fn draw_cursor(
let point = ray.get_point(distance);
// Draw a circle just above the ground plane at that position.
gizmos.circle(point + ground.up() * 0.01, ground.up(), 0.2, Color::WHITE);
gizmos.circle(
Isometry3d::new(
point + ground.up() * 0.01,
Quat::from_rotation_arc(Vec3::Z, ground.up().as_vec3()),
),
0.2,
Color::WHITE,
);
}
#[derive(Component)]

View file

@ -1,6 +1,7 @@
//! Demonstrates how to observe life-cycle triggers as well as define custom ones.
use bevy::{
math::Isometry2d,
prelude::*,
utils::{HashMap, HashSet},
};
@ -165,7 +166,7 @@ fn explode_mine(trigger: Trigger<Explode>, query: Query<&Mine>, mut commands: Co
fn draw_shapes(mut gizmos: Gizmos, mines: Query<&Mine>) {
for mine in &mines {
gizmos.circle_2d(
mine.pos,
Isometry2d::from_translation(mine.pos),
mine.size,
Color::hsl((mine.size - 4.0) / 16.0 * 360.0, 1.0, 0.8),
);

View file

@ -49,8 +49,7 @@ fn draw_example_collection(
gizmos
.grid_2d(
Vec2::ZERO,
0.0,
Isometry2d::IDENTITY,
UVec2::new(16, 9),
Vec2::new(80., 80.),
// Dark gray
@ -66,21 +65,26 @@ fn draw_example_collection(
(Vec2::Y * 300., BLUE),
]);
gizmos.rect_2d(Vec2::ZERO, 0., Vec2::splat(650.), BLACK);
gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::splat(650.), BLACK);
gizmos.cross_2d(Vec2::new(-160., 120.), 0., 12., FUCHSIA);
gizmos.cross_2d(
Isometry2d::from_translation(Vec2::new(-160., 120.)),
12.,
FUCHSIA,
);
my_gizmos
.rounded_rect_2d(Vec2::ZERO, 0., Vec2::splat(630.), BLACK)
.rounded_rect_2d(Isometry2d::IDENTITY, Vec2::splat(630.), BLACK)
.corner_radius((time.elapsed_seconds() / 3.).cos() * 100.);
// Circles have 32 line-segments by default.
// You may want to increase this for larger circles.
my_gizmos.circle_2d(Vec2::ZERO, 300., NAVY).resolution(64);
my_gizmos
.circle_2d(Isometry2d::from_translation(Vec2::ZERO), 300., NAVY)
.resolution(64);
my_gizmos.ellipse_2d(
Vec2::ZERO,
time.elapsed_seconds() % TAU,
Isometry2d::new(Vec2::ZERO, Rot2::radians(time.elapsed_seconds() % TAU)),
Vec2::new(100., 200.),
YELLOW_GREEN,
);

View file

@ -83,41 +83,48 @@ fn draw_example_collection(
time: Res<Time>,
) {
gizmos.grid(
Vec3::ZERO,
Quat::from_rotation_x(PI / 2.),
Isometry3d::from_rotation(Quat::from_rotation_x(PI / 2.)),
UVec2::splat(20),
Vec2::new(2., 2.),
// Light gray
LinearRgba::gray(0.65),
);
gizmos.grid(
Vec3::ONE * 10.0,
Quat::from_rotation_x(PI / 3. * 2.),
Isometry3d::new(Vec3::ONE * 10.0, Quat::from_rotation_x(PI / 3. * 2.)),
UVec2::splat(20),
Vec2::new(2., 2.),
PURPLE,
);
gizmos.sphere(Vec3::ONE * 10.0, Quat::default(), 1.0, PURPLE);
gizmos.sphere(Isometry3d::from_translation(Vec3::ONE * 10.0), 1.0, PURPLE);
gizmos.cuboid(
Transform::from_translation(Vec3::Y * 0.5).with_scale(Vec3::splat(1.25)),
BLACK,
);
gizmos.rect(
Vec3::new(time.elapsed_seconds().cos() * 2.5, 1., 0.),
Quat::from_rotation_y(PI / 2.),
Isometry3d::new(
Vec3::new(time.elapsed_seconds().cos() * 2.5, 1., 0.),
Quat::from_rotation_y(PI / 2.),
),
Vec2::splat(2.),
LIME,
);
gizmos.cross(Vec3::new(-1., 1., 1.), Quat::IDENTITY, 0.5, FUCHSIA);
gizmos.cross(
Isometry3d::from_translation(Vec3::new(-1., 1., 1.)),
0.5,
FUCHSIA,
);
my_gizmos.sphere(Vec3::new(1., 0.5, 0.), Quat::IDENTITY, 0.5, RED);
my_gizmos.sphere(
Isometry3d::from_translation(Vec3::new(1., 0.5, 0.)),
0.5,
RED,
);
my_gizmos
.rounded_cuboid(
Vec3::new(-2.0, 0.75, -0.75),
Quat::IDENTITY,
Isometry3d::from_translation(Vec3::new(-2.0, 0.75, -0.75)),
Vec3::splat(0.9),
TURQUOISE,
)
@ -136,20 +143,30 @@ fn draw_example_collection(
.arc_3d(
180.0_f32.to_radians(),
0.2,
Vec3::ONE,
Quat::from_rotation_arc(Vec3::Y, Vec3::ONE.normalize()),
Isometry3d::new(
Vec3::ONE,
Quat::from_rotation_arc(Vec3::Y, Vec3::ONE.normalize()),
),
ORANGE,
)
.resolution(10);
// Circles have 32 line-segments by default.
my_gizmos.circle(Vec3::ZERO, Dir3::Y, 3., BLACK);
my_gizmos.circle(
Isometry3d::from_rotation(Quat::from_rotation_arc(Vec3::Z, Vec3::Y)),
3.,
BLACK,
);
// You may want to increase this for larger circles or spheres.
my_gizmos
.circle(Vec3::ZERO, Dir3::Y, 3.1, NAVY)
.circle(
Isometry3d::from_rotation(Quat::from_rotation_arc(Vec3::Z, Vec3::Y)),
3.1,
NAVY,
)
.resolution(64);
my_gizmos
.sphere(Vec3::ZERO, Quat::IDENTITY, 3.2, BLACK)
.sphere(Isometry3d::IDENTITY, 3.2, BLACK)
.resolution(64);
gizmos.arrow(Vec3::ZERO, Vec3::ONE * 1.5, YELLOW);

View file

@ -6,7 +6,7 @@ use bevy::{
ecs::system::Commands,
gizmos::gizmos::Gizmos,
input::{mouse::MouseButtonInput, ButtonState},
math::{cubic_splines::*, vec2},
math::{cubic_splines::*, vec2, Isometry2d},
prelude::*,
};
@ -197,7 +197,11 @@ fn draw_control_points(
mut gizmos: Gizmos,
) {
for &(point, tangent) in &control_points.points_and_tangents {
gizmos.circle_2d(point, 10.0, Color::srgb(0.0, 1.0, 0.0));
gizmos.circle_2d(
Isometry2d::from_translation(point),
10.0,
Color::srgb(0.0, 1.0, 0.0),
);
if matches!(*spline_mode, SplineMode::Hermite) {
gizmos.arrow_2d(point, point + tangent, Color::srgb(1.0, 0.0, 0.0));
@ -399,8 +403,16 @@ fn draw_edit_move(
return;
};
gizmos.circle_2d(start, 10.0, Color::srgb(0.0, 1.0, 0.7));
gizmos.circle_2d(start, 7.0, Color::srgb(0.0, 1.0, 0.7));
gizmos.circle_2d(
Isometry2d::from_translation(start),
10.0,
Color::srgb(0.0, 1.0, 0.7),
);
gizmos.circle_2d(
Isometry2d::from_translation(start),
7.0,
Color::srgb(0.0, 1.0, 0.7),
);
gizmos.arrow_2d(start, end, Color::srgb(1.0, 0.0, 0.7));
}

View file

@ -211,14 +211,22 @@ fn bounding_shapes_2d(
// Get the AABB of the primitive with the rotation and translation of the mesh.
let aabb = HEART.aabb_2d(isometry);
gizmos.rect_2d(aabb.center(), 0., aabb.half_size() * 2., WHITE);
gizmos.rect_2d(
Isometry2d::from_translation(aabb.center()),
aabb.half_size() * 2.,
WHITE,
);
}
BoundingShape::BoundingSphere => {
// Get the bounding sphere of the primitive with the rotation and translation of the mesh.
let bounding_circle = HEART.bounding_circle(isometry);
gizmos
.circle_2d(bounding_circle.center(), bounding_circle.radius(), WHITE)
.circle_2d(
Isometry2d::from_translation(bounding_circle.center()),
bounding_circle.radius(),
WHITE,
)
.resolution(64);
}
}
@ -249,8 +257,7 @@ fn bounding_shapes_3d(
gizmos.primitive_3d(
&Cuboid::from_size(Vec3::from(aabb.half_size()) * 2.),
aabb.center().into(),
Quat::IDENTITY,
Isometry3d::from_translation(aabb.center()),
WHITE,
);
}
@ -259,8 +266,7 @@ fn bounding_shapes_3d(
let bounding_sphere = EXTRUSION.bounding_sphere(transform.to_isometry());
gizmos.sphere(
bounding_sphere.center().into(),
Quat::IDENTITY,
Isometry3d::from_translation(bounding_sphere.center()),
bounding_sphere.radius(),
WHITE,
);

View file

@ -3,7 +3,8 @@
#![allow(clippy::match_same_arms)]
use bevy::{
input::common_conditions::input_just_pressed, prelude::*, sprite::MaterialMesh2dBundle,
input::common_conditions::input_just_pressed, math::Isometry2d, prelude::*,
sprite::MaterialMesh2dBundle,
};
const LEFT_RIGHT_OFFSET_2D: f32 = 200.0;
@ -445,39 +446,40 @@ fn in_mode(active: CameraActive) -> impl Fn(Res<State<CameraActive>>) -> bool {
fn draw_gizmos_2d(mut gizmos: Gizmos, state: Res<State<PrimitiveSelected>>, time: Res<Time>) {
const POSITION: Vec2 = Vec2::new(-LEFT_RIGHT_OFFSET_2D, 0.0);
let angle = time.elapsed_seconds();
let isometry = Isometry2d::new(POSITION, Rot2::radians(angle));
let color = Color::WHITE;
match state.get() {
PrimitiveSelected::RectangleAndCuboid => {
gizmos.primitive_2d(&RECTANGLE, POSITION, angle, color);
gizmos.primitive_2d(&RECTANGLE, isometry, color);
}
PrimitiveSelected::CircleAndSphere => {
gizmos.primitive_2d(&CIRCLE, POSITION, angle, color);
gizmos.primitive_2d(&CIRCLE, isometry, color);
}
PrimitiveSelected::Ellipse => drop(gizmos.primitive_2d(&ELLIPSE, POSITION, angle, color)),
PrimitiveSelected::Triangle => gizmos.primitive_2d(&TRIANGLE_2D, POSITION, angle, color),
PrimitiveSelected::Plane => gizmos.primitive_2d(&PLANE_2D, POSITION, angle, color),
PrimitiveSelected::Line => drop(gizmos.primitive_2d(&LINE2D, POSITION, angle, color)),
PrimitiveSelected::Ellipse => drop(gizmos.primitive_2d(&ELLIPSE, isometry, color)),
PrimitiveSelected::Triangle => gizmos.primitive_2d(&TRIANGLE_2D, isometry, color),
PrimitiveSelected::Plane => gizmos.primitive_2d(&PLANE_2D, isometry, color),
PrimitiveSelected::Line => drop(gizmos.primitive_2d(&LINE2D, isometry, color)),
PrimitiveSelected::Segment => {
drop(gizmos.primitive_2d(&SEGMENT_2D, POSITION, angle, color));
drop(gizmos.primitive_2d(&SEGMENT_2D, isometry, color));
}
PrimitiveSelected::Polyline => gizmos.primitive_2d(&POLYLINE_2D, POSITION, angle, color),
PrimitiveSelected::Polygon => gizmos.primitive_2d(&POLYGON_2D, POSITION, angle, color),
PrimitiveSelected::Polyline => gizmos.primitive_2d(&POLYLINE_2D, isometry, color),
PrimitiveSelected::Polygon => gizmos.primitive_2d(&POLYGON_2D, isometry, color),
PrimitiveSelected::RegularPolygon => {
gizmos.primitive_2d(&REGULAR_POLYGON, POSITION, angle, color);
gizmos.primitive_2d(&REGULAR_POLYGON, isometry, color);
}
PrimitiveSelected::Capsule => gizmos.primitive_2d(&CAPSULE_2D, POSITION, angle, color),
PrimitiveSelected::Capsule => gizmos.primitive_2d(&CAPSULE_2D, isometry, color),
PrimitiveSelected::Cylinder => {}
PrimitiveSelected::Cone => {}
PrimitiveSelected::ConicalFrustum => {}
PrimitiveSelected::Torus => drop(gizmos.primitive_2d(&ANNULUS, POSITION, angle, color)),
PrimitiveSelected::Torus => drop(gizmos.primitive_2d(&ANNULUS, isometry, color)),
PrimitiveSelected::Tetrahedron => {}
PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, POSITION, angle, color),
PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, isometry, color),
PrimitiveSelected::CircularSector => {
gizmos.primitive_2d(&CIRCULAR_SECTOR, POSITION, angle, color);
gizmos.primitive_2d(&CIRCULAR_SECTOR, isometry, color);
}
PrimitiveSelected::CircularSegment => {
gizmos.primitive_2d(&CIRCULAR_SEGMENT, POSITION, angle, color);
gizmos.primitive_2d(&CIRCULAR_SEGMENT, isometry, color);
}
}
}
@ -660,53 +662,54 @@ fn draw_gizmos_3d(mut gizmos: Gizmos, state: Res<State<PrimitiveSelected>>, time
.try_normalize()
.unwrap_or(Vec3::Z),
);
let isometry = Isometry3d::new(POSITION, rotation);
let color = Color::WHITE;
let resolution = 10;
match state.get() {
PrimitiveSelected::RectangleAndCuboid => {
gizmos.primitive_3d(&CUBOID, POSITION, rotation, color);
gizmos.primitive_3d(&CUBOID, isometry, color);
}
PrimitiveSelected::CircleAndSphere => drop(
gizmos
.primitive_3d(&SPHERE, POSITION, rotation, color)
.primitive_3d(&SPHERE, isometry, color)
.resolution(resolution),
),
PrimitiveSelected::Ellipse => {}
PrimitiveSelected::Triangle => gizmos.primitive_3d(&TRIANGLE_3D, POSITION, rotation, color),
PrimitiveSelected::Plane => drop(gizmos.primitive_3d(&PLANE_3D, POSITION, rotation, color)),
PrimitiveSelected::Line => gizmos.primitive_3d(&LINE3D, POSITION, rotation, color),
PrimitiveSelected::Segment => gizmos.primitive_3d(&SEGMENT_3D, POSITION, rotation, color),
PrimitiveSelected::Polyline => gizmos.primitive_3d(&POLYLINE_3D, POSITION, rotation, color),
PrimitiveSelected::Triangle => gizmos.primitive_3d(&TRIANGLE_3D, isometry, color),
PrimitiveSelected::Plane => drop(gizmos.primitive_3d(&PLANE_3D, isometry, color)),
PrimitiveSelected::Line => gizmos.primitive_3d(&LINE3D, isometry, color),
PrimitiveSelected::Segment => gizmos.primitive_3d(&SEGMENT_3D, isometry, color),
PrimitiveSelected::Polyline => gizmos.primitive_3d(&POLYLINE_3D, isometry, color),
PrimitiveSelected::Polygon => {}
PrimitiveSelected::RegularPolygon => {}
PrimitiveSelected::Capsule => drop(
gizmos
.primitive_3d(&CAPSULE_3D, POSITION, rotation, color)
.primitive_3d(&CAPSULE_3D, isometry, color)
.resolution(resolution),
),
PrimitiveSelected::Cylinder => drop(
gizmos
.primitive_3d(&CYLINDER, POSITION, rotation, color)
.primitive_3d(&CYLINDER, isometry, color)
.resolution(resolution),
),
PrimitiveSelected::Cone => drop(
gizmos
.primitive_3d(&CONE, POSITION, rotation, color)
.primitive_3d(&CONE, isometry, color)
.resolution(resolution),
),
PrimitiveSelected::ConicalFrustum => {
gizmos.primitive_3d(&CONICAL_FRUSTUM, POSITION, rotation, color);
gizmos.primitive_3d(&CONICAL_FRUSTUM, isometry, color);
}
PrimitiveSelected::Torus => drop(
gizmos
.primitive_3d(&TORUS, POSITION, rotation, color)
.primitive_3d(&TORUS, isometry, color)
.minor_resolution(resolution)
.major_resolution(resolution),
),
PrimitiveSelected::Tetrahedron => {
gizmos.primitive_3d(&TETRAHEDRON, POSITION, rotation, color);
gizmos.primitive_3d(&TETRAHEDRON, isometry, color);
}
PrimitiveSelected::Arc => {}