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

View file

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

View file

@ -5,13 +5,18 @@
use crate::prelude::{GizmoConfigGroup, Gizmos}; use crate::prelude::{GizmoConfigGroup, Gizmos};
use bevy_color::Color; use bevy_color::Color;
use bevy_math::{Mat2, Mat3, Quat, Vec2, Vec3}; use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3};
impl<Config> Gizmos<'_, '_, Config> impl<Config> Gizmos<'_, '_, Config>
where where
Config: GizmoConfigGroup, 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. /// This should be called for each frame the cross needs to be rendered.
/// ///
@ -21,29 +26,26 @@ where
/// # use bevy_math::prelude::*; /// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::WHITE; /// # use bevy_color::palettes::basic::WHITE;
/// fn system(mut gizmos: Gizmos) { /// 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); /// # bevy_ecs::system::assert_is_system(system);
/// ``` /// ```
pub fn cross( pub fn cross(&mut self, isometry: Isometry3d, half_size: f32, color: impl Into<Color>) {
&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);
let color: Color = color.into(); let color: Color = color.into();
self.line(position + local_x, position - local_x, color); [Vec3::X, Vec3::Y, Vec3::Z]
self.line(position + local_y, position - local_y, color); .map(|axis| axis * half_size)
self.line(position + local_z, position - local_z, color); .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. /// This should be called for each frame the cross needs to be rendered.
/// ///
@ -53,23 +55,17 @@ where
/// # use bevy_math::prelude::*; /// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::WHITE; /// # use bevy_color::palettes::basic::WHITE;
/// fn system(mut gizmos: Gizmos) { /// 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); /// # bevy_ecs::system::assert_is_system(system);
/// ``` /// ```
pub fn cross_2d( pub fn cross_2d(&mut self, isometry: Isometry2d, half_size: f32, color: impl Into<Color>) {
&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);
let color: Color = color.into(); let color: Color = color.into();
self.line_2d(position + local_x, position - local_x, color); [Vec2::X, Vec2::Y]
self.line_2d(position + local_y, position - local_y, color); .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}, system::{Deferred, ReadOnlySystemParam, Res, Resource, SystemBuffer, SystemMeta, SystemParam},
world::{unsafe_world_cell::UnsafeWorldCell, World}, 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_transform::TransformPoint;
use bevy_utils::default; use bevy_utils::default;
@ -452,7 +452,12 @@ where
strip_colors.push(LinearRgba::NAN); 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. /// This should be called for each frame the rectangle needs to be rendered.
/// ///
@ -462,16 +467,16 @@ where
/// # use bevy_math::prelude::*; /// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::GREEN; /// # use bevy_color::palettes::basic::GREEN;
/// fn system(mut gizmos: Gizmos) { /// 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); /// # bevy_ecs::system::assert_is_system(system);
/// ``` /// ```
#[inline] #[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 { if !self.enabled {
return; 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); self.linestrip([tl, tr, br, bl, tl], color);
} }
@ -674,7 +679,12 @@ where
self.line_gradient_2d(start, start + vector, start_color, end_color); 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. /// This should be called for each frame the rectangle needs to be rendered.
/// ///
@ -684,23 +694,16 @@ where
/// # use bevy_math::prelude::*; /// # use bevy_math::prelude::*;
/// # use bevy_color::palettes::basic::GREEN; /// # use bevy_color::palettes::basic::GREEN;
/// fn system(mut gizmos: Gizmos) { /// 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); /// # bevy_ecs::system::assert_is_system(system);
/// ``` /// ```
#[inline] #[inline]
pub fn rect_2d( pub fn rect_2d(&mut self, isometry: Isometry2d, size: Vec2, color: impl Into<Color>) {
&mut self,
position: Vec2,
rotation: impl Into<Rot2>,
size: Vec2,
color: impl Into<Color>,
) {
if !self.enabled { if !self.enabled {
return; return;
} }
let rotation: Rot2 = rotation.into(); let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);
let [tl, tr, br, bl] = rect_inner(size).map(|vec2| position + rotation * vec2);
self.linestrip_2d([tl, tr, br, bl, tl], color); self.linestrip_2d([tl, tr, br, bl, tl], color);
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -1,28 +1,6 @@
use std::f32::consts::TAU; use std::f32::consts::TAU;
use bevy_color::Color; use bevy_math::Vec2;
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
}
/// Calculates the `nth` coordinate of a circle. /// 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. /// 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 /// 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` /// circle with the given radius, divided into linear segments. The iterator produces `resolution`
/// number of points. /// number of points.
@ -46,27 +26,18 @@ pub(crate) fn circle_coordinates(radius: f32, resolution: u32) -> impl Iterator<
.take(resolution as usize) .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. /// This function creates an iterator that yields the positions of points approximating a
pub(crate) fn draw_circle_3d<Config, Clear>( /// circle with the given radius, divided into linear segments. The iterator produces `resolution`
gizmos: &mut Gizmos<'_, '_, Config, Clear>, /// number of points.
pub(crate) fn circle_coordinates_closed(
radius: f32, radius: f32,
resolution: u32, resolution: u32,
rotation: Quat, ) -> impl Iterator<Item = Vec2> {
translation: Vec3, circle_coordinates(radius, resolution).chain(std::iter::once(single_circle_coordinate(
color: Color, radius, resolution, resolution,
) 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);
} }

View file

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

View file

@ -1,6 +1,6 @@
//! This example demonstrates how to use the `Camera::viewport_to_world_2d` method. //! 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() { fn main() {
App::new() App::new()
@ -30,7 +30,7 @@ fn draw_cursor(
return; return;
}; };
gizmos.circle_2d(point, 10., WHITE); gizmos.circle_2d(Isometry2d::from_translation(point), 10., WHITE);
} }
fn setup(mut commands: Commands) { 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() { for (shape, transform) in query.iter() {
let translation = transform.translation.xy(); let translation = transform.translation.xy();
let rotation = transform.rotation.to_euler(EulerRot::YXZ).2; let rotation = transform.rotation.to_euler(EulerRot::YXZ).2;
let isometry = Isometry2d::new(translation, Rot2::radians(rotation));
match shape { match shape {
Shape::Rectangle(r) => { Shape::Rectangle(r) => {
gizmos.primitive_2d(r, translation, rotation, color); gizmos.primitive_2d(r, isometry, color);
} }
Shape::Circle(c) => { Shape::Circle(c) => {
gizmos.primitive_2d(c, translation, rotation, color); gizmos.primitive_2d(c, isometry, color);
} }
Shape::Triangle(t) => { Shape::Triangle(t) => {
gizmos.primitive_2d(t, translation, rotation, color); gizmos.primitive_2d(t, isometry, color);
} }
Shape::Line(l) => { Shape::Line(l) => {
gizmos.primitive_2d(l, translation, rotation, color); gizmos.primitive_2d(l, isometry, color);
} }
Shape::Capsule(c) => { Shape::Capsule(c) => {
gizmos.primitive_2d(c, translation, rotation, color); gizmos.primitive_2d(c, isometry, color);
} }
Shape::Polygon(p) => { 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 }; let color = if **intersects { AQUA } else { ORANGE_RED };
match volume { match volume {
CurrentVolume::Aabb(a) => { 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) => { 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) { fn draw_filled_circle(gizmos: &mut Gizmos, position: Vec2, color: Srgba) {
for r in [1., 2., 3.] { 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(); **intersects = toi.is_some();
if let Some(toi) = toi { if let Some(toi) = toi {
gizmos.rect_2d( gizmos.rect_2d(
aabb_cast.ray.ray.origin + *aabb_cast.ray.ray.direction * toi, Isometry2d::from_translation(
0., aabb_cast.ray.ray.origin + *aabb_cast.ray.ray.direction * toi,
),
aabb_cast.aabb.half_size() * 2., aabb_cast.aabb.half_size() * 2.,
LIME, LIME,
); );
@ -382,7 +388,9 @@ fn bounding_circle_cast_system(
**intersects = toi.is_some(); **intersects = toi.is_some();
if let Some(toi) = toi { if let Some(toi) = toi {
gizmos.circle_2d( 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(), circle_cast.circle.radius(),
LIME, LIME,
); );
@ -403,7 +411,11 @@ fn aabb_intersection_system(
) { ) {
let center = get_intersection_position(&time); let center = get_intersection_position(&time);
let aabb = Aabb2d::new(center, Vec2::splat(50.)); 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() { for (volume, mut intersects) in volumes.iter_mut() {
let hit = match volume { let hit = match volume {
@ -422,7 +434,11 @@ fn circle_intersection_system(
) { ) {
let center = get_intersection_position(&time); let center = get_intersection_position(&time);
let circle = BoundingCircle::new(center, 50.); 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() { for (volume, mut intersects) in volumes.iter_mut() {
let hit = match volume { 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 isometry = Isometry2d::new(translation, Rot2::radians(rotation));
let aabb = shape.0.aabb_2d(isometry); 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); 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); let point = ray.get_point(distance);
// Draw a circle just above the ground plane at that position. // 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)] #[derive(Component)]

View file

@ -1,6 +1,7 @@
//! Demonstrates how to observe life-cycle triggers as well as define custom ones. //! Demonstrates how to observe life-cycle triggers as well as define custom ones.
use bevy::{ use bevy::{
math::Isometry2d,
prelude::*, prelude::*,
utils::{HashMap, HashSet}, 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>) { fn draw_shapes(mut gizmos: Gizmos, mines: Query<&Mine>) {
for mine in &mines { for mine in &mines {
gizmos.circle_2d( gizmos.circle_2d(
mine.pos, Isometry2d::from_translation(mine.pos),
mine.size, mine.size,
Color::hsl((mine.size - 4.0) / 16.0 * 360.0, 1.0, 0.8), 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 gizmos
.grid_2d( .grid_2d(
Vec2::ZERO, Isometry2d::IDENTITY,
0.0,
UVec2::new(16, 9), UVec2::new(16, 9),
Vec2::new(80., 80.), Vec2::new(80., 80.),
// Dark gray // Dark gray
@ -66,21 +65,26 @@ fn draw_example_collection(
(Vec2::Y * 300., BLUE), (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 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.); .corner_radius((time.elapsed_seconds() / 3.).cos() * 100.);
// Circles have 32 line-segments by default. // Circles have 32 line-segments by default.
// You may want to increase this for larger circles. // 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( my_gizmos.ellipse_2d(
Vec2::ZERO, Isometry2d::new(Vec2::ZERO, Rot2::radians(time.elapsed_seconds() % TAU)),
time.elapsed_seconds() % TAU,
Vec2::new(100., 200.), Vec2::new(100., 200.),
YELLOW_GREEN, YELLOW_GREEN,
); );

View file

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

View file

@ -6,7 +6,7 @@ use bevy::{
ecs::system::Commands, ecs::system::Commands,
gizmos::gizmos::Gizmos, gizmos::gizmos::Gizmos,
input::{mouse::MouseButtonInput, ButtonState}, input::{mouse::MouseButtonInput, ButtonState},
math::{cubic_splines::*, vec2}, math::{cubic_splines::*, vec2, Isometry2d},
prelude::*, prelude::*,
}; };
@ -197,7 +197,11 @@ fn draw_control_points(
mut gizmos: Gizmos, mut gizmos: Gizmos,
) { ) {
for &(point, tangent) in &control_points.points_and_tangents { 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) { if matches!(*spline_mode, SplineMode::Hermite) {
gizmos.arrow_2d(point, point + tangent, Color::srgb(1.0, 0.0, 0.0)); gizmos.arrow_2d(point, point + tangent, Color::srgb(1.0, 0.0, 0.0));
@ -399,8 +403,16 @@ fn draw_edit_move(
return; return;
}; };
gizmos.circle_2d(start, 10.0, Color::srgb(0.0, 1.0, 0.7)); gizmos.circle_2d(
gizmos.circle_2d(start, 7.0, Color::srgb(0.0, 1.0, 0.7)); 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)); 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. // Get the AABB of the primitive with the rotation and translation of the mesh.
let aabb = HEART.aabb_2d(isometry); 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 => { BoundingShape::BoundingSphere => {
// Get the bounding sphere of the primitive with the rotation and translation of the mesh. // Get the bounding sphere of the primitive with the rotation and translation of the mesh.
let bounding_circle = HEART.bounding_circle(isometry); let bounding_circle = HEART.bounding_circle(isometry);
gizmos gizmos
.circle_2d(bounding_circle.center(), bounding_circle.radius(), WHITE) .circle_2d(
Isometry2d::from_translation(bounding_circle.center()),
bounding_circle.radius(),
WHITE,
)
.resolution(64); .resolution(64);
} }
} }
@ -249,8 +257,7 @@ fn bounding_shapes_3d(
gizmos.primitive_3d( gizmos.primitive_3d(
&Cuboid::from_size(Vec3::from(aabb.half_size()) * 2.), &Cuboid::from_size(Vec3::from(aabb.half_size()) * 2.),
aabb.center().into(), Isometry3d::from_translation(aabb.center()),
Quat::IDENTITY,
WHITE, WHITE,
); );
} }
@ -259,8 +266,7 @@ fn bounding_shapes_3d(
let bounding_sphere = EXTRUSION.bounding_sphere(transform.to_isometry()); let bounding_sphere = EXTRUSION.bounding_sphere(transform.to_isometry());
gizmos.sphere( gizmos.sphere(
bounding_sphere.center().into(), Isometry3d::from_translation(bounding_sphere.center()),
Quat::IDENTITY,
bounding_sphere.radius(), bounding_sphere.radius(),
WHITE, WHITE,
); );

View file

@ -3,7 +3,8 @@
#![allow(clippy::match_same_arms)] #![allow(clippy::match_same_arms)]
use bevy::{ 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; 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>) { 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); const POSITION: Vec2 = Vec2::new(-LEFT_RIGHT_OFFSET_2D, 0.0);
let angle = time.elapsed_seconds(); let angle = time.elapsed_seconds();
let isometry = Isometry2d::new(POSITION, Rot2::radians(angle));
let color = Color::WHITE; let color = Color::WHITE;
match state.get() { match state.get() {
PrimitiveSelected::RectangleAndCuboid => { PrimitiveSelected::RectangleAndCuboid => {
gizmos.primitive_2d(&RECTANGLE, POSITION, angle, color); gizmos.primitive_2d(&RECTANGLE, isometry, color);
} }
PrimitiveSelected::CircleAndSphere => { 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::Ellipse => drop(gizmos.primitive_2d(&ELLIPSE, isometry, color)),
PrimitiveSelected::Triangle => gizmos.primitive_2d(&TRIANGLE_2D, POSITION, angle, color), PrimitiveSelected::Triangle => gizmos.primitive_2d(&TRIANGLE_2D, isometry, color),
PrimitiveSelected::Plane => gizmos.primitive_2d(&PLANE_2D, POSITION, angle, color), PrimitiveSelected::Plane => gizmos.primitive_2d(&PLANE_2D, isometry, color),
PrimitiveSelected::Line => drop(gizmos.primitive_2d(&LINE2D, POSITION, angle, color)), PrimitiveSelected::Line => drop(gizmos.primitive_2d(&LINE2D, isometry, color)),
PrimitiveSelected::Segment => { 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::Polyline => gizmos.primitive_2d(&POLYLINE_2D, isometry, color),
PrimitiveSelected::Polygon => gizmos.primitive_2d(&POLYGON_2D, POSITION, angle, color), PrimitiveSelected::Polygon => gizmos.primitive_2d(&POLYGON_2D, isometry, color),
PrimitiveSelected::RegularPolygon => { 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::Cylinder => {}
PrimitiveSelected::Cone => {} PrimitiveSelected::Cone => {}
PrimitiveSelected::ConicalFrustum => {} PrimitiveSelected::ConicalFrustum => {}
PrimitiveSelected::Torus => drop(gizmos.primitive_2d(&ANNULUS, POSITION, angle, color)), PrimitiveSelected::Torus => drop(gizmos.primitive_2d(&ANNULUS, isometry, color)),
PrimitiveSelected::Tetrahedron => {} PrimitiveSelected::Tetrahedron => {}
PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, POSITION, angle, color), PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, isometry, color),
PrimitiveSelected::CircularSector => { PrimitiveSelected::CircularSector => {
gizmos.primitive_2d(&CIRCULAR_SECTOR, POSITION, angle, color); gizmos.primitive_2d(&CIRCULAR_SECTOR, isometry, color);
} }
PrimitiveSelected::CircularSegment => { 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() .try_normalize()
.unwrap_or(Vec3::Z), .unwrap_or(Vec3::Z),
); );
let isometry = Isometry3d::new(POSITION, rotation);
let color = Color::WHITE; let color = Color::WHITE;
let resolution = 10; let resolution = 10;
match state.get() { match state.get() {
PrimitiveSelected::RectangleAndCuboid => { PrimitiveSelected::RectangleAndCuboid => {
gizmos.primitive_3d(&CUBOID, POSITION, rotation, color); gizmos.primitive_3d(&CUBOID, isometry, color);
} }
PrimitiveSelected::CircleAndSphere => drop( PrimitiveSelected::CircleAndSphere => drop(
gizmos gizmos
.primitive_3d(&SPHERE, POSITION, rotation, color) .primitive_3d(&SPHERE, isometry, color)
.resolution(resolution), .resolution(resolution),
), ),
PrimitiveSelected::Ellipse => {} PrimitiveSelected::Ellipse => {}
PrimitiveSelected::Triangle => gizmos.primitive_3d(&TRIANGLE_3D, POSITION, rotation, color), PrimitiveSelected::Triangle => gizmos.primitive_3d(&TRIANGLE_3D, isometry, color),
PrimitiveSelected::Plane => drop(gizmos.primitive_3d(&PLANE_3D, POSITION, rotation, color)), PrimitiveSelected::Plane => drop(gizmos.primitive_3d(&PLANE_3D, isometry, color)),
PrimitiveSelected::Line => gizmos.primitive_3d(&LINE3D, POSITION, rotation, color), PrimitiveSelected::Line => gizmos.primitive_3d(&LINE3D, isometry, color),
PrimitiveSelected::Segment => gizmos.primitive_3d(&SEGMENT_3D, POSITION, rotation, color), PrimitiveSelected::Segment => gizmos.primitive_3d(&SEGMENT_3D, isometry, color),
PrimitiveSelected::Polyline => gizmos.primitive_3d(&POLYLINE_3D, POSITION, rotation, color), PrimitiveSelected::Polyline => gizmos.primitive_3d(&POLYLINE_3D, isometry, color),
PrimitiveSelected::Polygon => {} PrimitiveSelected::Polygon => {}
PrimitiveSelected::RegularPolygon => {} PrimitiveSelected::RegularPolygon => {}
PrimitiveSelected::Capsule => drop( PrimitiveSelected::Capsule => drop(
gizmos gizmos
.primitive_3d(&CAPSULE_3D, POSITION, rotation, color) .primitive_3d(&CAPSULE_3D, isometry, color)
.resolution(resolution), .resolution(resolution),
), ),
PrimitiveSelected::Cylinder => drop( PrimitiveSelected::Cylinder => drop(
gizmos gizmos
.primitive_3d(&CYLINDER, POSITION, rotation, color) .primitive_3d(&CYLINDER, isometry, color)
.resolution(resolution), .resolution(resolution),
), ),
PrimitiveSelected::Cone => drop( PrimitiveSelected::Cone => drop(
gizmos gizmos
.primitive_3d(&CONE, POSITION, rotation, color) .primitive_3d(&CONE, isometry, color)
.resolution(resolution), .resolution(resolution),
), ),
PrimitiveSelected::ConicalFrustum => { PrimitiveSelected::ConicalFrustum => {
gizmos.primitive_3d(&CONICAL_FRUSTUM, POSITION, rotation, color); gizmos.primitive_3d(&CONICAL_FRUSTUM, isometry, color);
} }
PrimitiveSelected::Torus => drop( PrimitiveSelected::Torus => drop(
gizmos gizmos
.primitive_3d(&TORUS, POSITION, rotation, color) .primitive_3d(&TORUS, isometry, color)
.minor_resolution(resolution) .minor_resolution(resolution)
.major_resolution(resolution), .major_resolution(resolution),
), ),
PrimitiveSelected::Tetrahedron => { PrimitiveSelected::Tetrahedron => {
gizmos.primitive_3d(&TETRAHEDRON, POSITION, rotation, color); gizmos.primitive_3d(&TETRAHEDRON, isometry, color);
} }
PrimitiveSelected::Arc => {} PrimitiveSelected::Arc => {}