mirror of
https://github.com/bevyengine/bevy
synced 2024-12-26 21:13:09 +00:00
f1f07bec09
# Objective When trying to test a gizmos change I ran `cargo test -p bevy_gizmos` and the output had a lot of noise from warnings and failed doc errors. This was because I didn't have all of the features enabled. ## Solution I admit this might be pedantic, and am happy if the concensus is to reject it. Although it does reduce the lines of code, testing noise, and the amount of code compiled. I don't think it affects the complexity of public code, and it doesn't change much to the complexity of internal code. I've removed un-needed `bevy_render` imports in all of the gizmos docs examples, there's probably other unnecessary ones there too, but I haven't looked exhaustively. It isn't needed for those docs, and isn't available except in a subset of `cfg` combinations. I've also made several of the `use` statements slightly more specific. I shouldn't have changed the public interfaces, except that `GizmoMeshConfig` requires either `bevy_sprite` or `bevy_pbr`, as it does nothing without them. I've also avoided adding some systems and plugins in situations where they can't work. An example of this is where the `light` module depends on `all(feature = "bevy_pbr", feature = "bevy_render")`, but it has `use` statements that only require `bevy_render`. ## Testing During development I ran: ``` cargo check -p bevy_gizmos && cargo check -p bevy_gizmos --features=bevy_pbr && cargo check -p bevy_gizmos --features=bevy_sprite && cargo check -p bevy_gizmos --features=bevy_render ``` Afterwards I ran this just to be sure: ``` cargo check && cargo check --features=bevy_pbr && cargo check --features=bevy_sprite && cargo check --features=bevy_render ``` Finally I ran: ``` cargo test -p bevy_gizmos && cargo test -p bevy_gizmos --features=bevy_pbr && test check -p bevy_gizmos --features=bevy_sprite && cargo test -p bevy_gizmos --features=bevy_render ``` ## Migration Guide There shouldn't be any reason to migrate, although if for some reason you use `GizmoMeshConfig` and `bevy_render` but not `bevy_pbr` or `bevy_sprite` (such that it does nothing), then you will get an error that it no longer exists.
365 lines
10 KiB
Rust
365 lines
10 KiB
Rust
//! Additional [`Gizmos`] Functions -- Circles
|
|
//!
|
|
//! Includes the implementation of [`Gizmos::circle`] and [`Gizmos::circle_2d`],
|
|
//! and assorted support items.
|
|
|
|
use crate::prelude::{GizmoConfigGroup, Gizmos};
|
|
use bevy_color::Color;
|
|
use bevy_math::Mat2;
|
|
use bevy_math::{Dir3, Quat, Vec2, Vec3};
|
|
use std::f32::consts::TAU;
|
|
|
|
pub(crate) const DEFAULT_CIRCLE_RESOLUTION: u32 = 32;
|
|
|
|
fn ellipse_inner(half_size: Vec2, resolution: u32) -> impl Iterator<Item = Vec2> {
|
|
(0..resolution + 1).map(move |i| {
|
|
let angle = i as f32 * TAU / resolution as f32;
|
|
let (x, y) = angle.sin_cos();
|
|
Vec2::new(x, y) * half_size
|
|
})
|
|
}
|
|
|
|
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
/// Draw an ellipse in 3D at `position` with the flat side facing `normal`.
|
|
///
|
|
/// This should be called for each frame the ellipse needs to be rendered.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use bevy_gizmos::prelude::*;
|
|
/// # use bevy_math::prelude::*;
|
|
/// # use bevy_color::palettes::basic::{RED, GREEN};
|
|
/// fn system(mut gizmos: Gizmos) {
|
|
/// gizmos.ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(1., 2.), GREEN);
|
|
///
|
|
/// // Ellipses have 32 line-segments by default.
|
|
/// // You may want to increase this for larger ellipses.
|
|
/// gizmos
|
|
/// .ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(5., 1.), RED)
|
|
/// .resolution(64);
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(system);
|
|
/// ```
|
|
#[inline]
|
|
pub fn ellipse(
|
|
&mut self,
|
|
position: Vec3,
|
|
rotation: Quat,
|
|
half_size: Vec2,
|
|
color: impl Into<Color>,
|
|
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
|
|
EllipseBuilder {
|
|
gizmos: self,
|
|
position,
|
|
rotation,
|
|
half_size,
|
|
color: color.into(),
|
|
resolution: DEFAULT_CIRCLE_RESOLUTION,
|
|
}
|
|
}
|
|
|
|
/// Draw an ellipse in 2D.
|
|
///
|
|
/// This should be called for each frame the ellipse needs to be rendered.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use bevy_gizmos::prelude::*;
|
|
/// # use bevy_math::prelude::*;
|
|
/// # use bevy_color::palettes::basic::{RED, GREEN};
|
|
/// fn system(mut gizmos: Gizmos) {
|
|
/// gizmos.ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(2., 1.), GREEN);
|
|
///
|
|
/// // Ellipses have 32 line-segments by default.
|
|
/// // You may want to increase this for larger ellipses.
|
|
/// gizmos
|
|
/// .ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(5., 1.), RED)
|
|
/// .resolution(64);
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(system);
|
|
/// ```
|
|
#[inline]
|
|
pub fn ellipse_2d(
|
|
&mut self,
|
|
position: Vec2,
|
|
angle: f32,
|
|
half_size: Vec2,
|
|
color: impl Into<Color>,
|
|
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
|
|
Ellipse2dBuilder {
|
|
gizmos: self,
|
|
position,
|
|
rotation: Mat2::from_angle(angle),
|
|
half_size,
|
|
color: color.into(),
|
|
resolution: DEFAULT_CIRCLE_RESOLUTION,
|
|
}
|
|
}
|
|
|
|
/// Draw a circle in 3D at `position` with the flat side facing `normal`.
|
|
///
|
|
/// This should be called for each frame the circle needs to be rendered.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use bevy_gizmos::prelude::*;
|
|
/// # use bevy_math::prelude::*;
|
|
/// # use bevy_color::palettes::basic::{RED, GREEN};
|
|
/// fn system(mut gizmos: Gizmos) {
|
|
/// gizmos.circle(Vec3::ZERO, Dir3::Z, 1., GREEN);
|
|
///
|
|
/// // Circles have 32 line-segments by default.
|
|
/// // You may want to increase this for larger circles.
|
|
/// gizmos
|
|
/// .circle(Vec3::ZERO, Dir3::Z, 5., RED)
|
|
/// .resolution(64);
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(system);
|
|
/// ```
|
|
#[inline]
|
|
pub fn circle(
|
|
&mut self,
|
|
position: Vec3,
|
|
normal: Dir3,
|
|
radius: f32,
|
|
color: impl Into<Color>,
|
|
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
|
|
EllipseBuilder {
|
|
gizmos: self,
|
|
position,
|
|
rotation: Quat::from_rotation_arc(Vec3::Z, *normal),
|
|
half_size: Vec2::splat(radius),
|
|
color: color.into(),
|
|
resolution: DEFAULT_CIRCLE_RESOLUTION,
|
|
}
|
|
}
|
|
|
|
/// Draw a circle in 2D.
|
|
///
|
|
/// This should be called for each frame the circle needs to be rendered.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use bevy_gizmos::prelude::*;
|
|
/// # use bevy_math::prelude::*;
|
|
/// # use bevy_color::palettes::basic::{RED, GREEN};
|
|
/// fn system(mut gizmos: Gizmos) {
|
|
/// gizmos.circle_2d(Vec2::ZERO, 1., GREEN);
|
|
///
|
|
/// // Circles have 32 line-segments by default.
|
|
/// // You may want to increase this for larger circles.
|
|
/// gizmos
|
|
/// .circle_2d(Vec2::ZERO, 5., RED)
|
|
/// .resolution(64);
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(system);
|
|
/// ```
|
|
#[inline]
|
|
pub fn circle_2d(
|
|
&mut self,
|
|
position: Vec2,
|
|
radius: f32,
|
|
color: impl Into<Color>,
|
|
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
|
|
Ellipse2dBuilder {
|
|
gizmos: self,
|
|
position,
|
|
rotation: Mat2::IDENTITY,
|
|
half_size: Vec2::splat(radius),
|
|
color: color.into(),
|
|
resolution: DEFAULT_CIRCLE_RESOLUTION,
|
|
}
|
|
}
|
|
|
|
/// Draw a wireframe sphere in 3D made out of 3 circles around the axes.
|
|
///
|
|
/// This should be called for each frame the sphere needs to be rendered.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use bevy_gizmos::prelude::*;
|
|
/// # use bevy_math::prelude::*;
|
|
/// # use bevy_color::Color;
|
|
/// fn system(mut gizmos: Gizmos) {
|
|
/// gizmos.sphere(Vec3::ZERO, Quat::IDENTITY, 1., Color::BLACK);
|
|
///
|
|
/// // Each circle has 32 line-segments by default.
|
|
/// // You may want to increase this for larger spheres.
|
|
/// gizmos
|
|
/// .sphere(Vec3::ZERO, Quat::IDENTITY, 5., Color::BLACK)
|
|
/// .resolution(64);
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(system);
|
|
/// ```
|
|
#[inline]
|
|
pub fn sphere(
|
|
&mut self,
|
|
position: Vec3,
|
|
rotation: Quat,
|
|
radius: f32,
|
|
color: impl Into<Color>,
|
|
) -> SphereBuilder<'_, 'w, 's, Config, Clear> {
|
|
SphereBuilder {
|
|
gizmos: self,
|
|
radius,
|
|
position,
|
|
rotation: rotation.normalize(),
|
|
color: color.into(),
|
|
resolution: DEFAULT_CIRCLE_RESOLUTION,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A builder returned by [`Gizmos::ellipse`].
|
|
pub struct EllipseBuilder<'a, 'w, 's, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
|
|
position: Vec3,
|
|
rotation: Quat,
|
|
half_size: Vec2,
|
|
color: Color,
|
|
resolution: u32,
|
|
}
|
|
|
|
impl<Config, Clear> EllipseBuilder<'_, '_, '_, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
/// Set the number of lines used to approximate the geometry of this ellipse.
|
|
pub fn resolution(mut self, resolution: u32) -> Self {
|
|
self.resolution = resolution;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<Config, Clear> Drop for EllipseBuilder<'_, '_, '_, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
fn drop(&mut self) {
|
|
if !self.gizmos.enabled {
|
|
return;
|
|
}
|
|
|
|
let positions = ellipse_inner(self.half_size, self.resolution)
|
|
.map(|vec2| self.rotation * vec2.extend(0.))
|
|
.map(|vec3| vec3 + self.position);
|
|
self.gizmos.linestrip(positions, self.color);
|
|
}
|
|
}
|
|
|
|
/// A builder returned by [`Gizmos::ellipse_2d`].
|
|
pub struct Ellipse2dBuilder<'a, 'w, 's, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
|
|
position: Vec2,
|
|
rotation: Mat2,
|
|
half_size: Vec2,
|
|
color: Color,
|
|
resolution: u32,
|
|
}
|
|
|
|
impl<Config, Clear> Ellipse2dBuilder<'_, '_, '_, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
/// Set the number of line-segments used to approximate the geometry of this ellipse.
|
|
pub fn resolution(mut self, resolution: u32) -> Self {
|
|
self.resolution = resolution;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<Config, Clear> Drop for Ellipse2dBuilder<'_, '_, '_, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
/// Set the number of line-segments for this ellipse.
|
|
fn drop(&mut self) {
|
|
if !self.gizmos.enabled {
|
|
return;
|
|
};
|
|
|
|
let positions = ellipse_inner(self.half_size, self.resolution)
|
|
.map(|vec2| self.rotation * vec2)
|
|
.map(|vec2| vec2 + self.position);
|
|
self.gizmos.linestrip_2d(positions, self.color);
|
|
}
|
|
}
|
|
|
|
/// A builder returned by [`Gizmos::sphere`].
|
|
pub struct SphereBuilder<'a, 'w, 's, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
|
|
|
|
// Radius of the sphere
|
|
radius: f32,
|
|
|
|
// Rotation of the sphere around the origin in 3D space
|
|
rotation: Quat,
|
|
// Center position of the sphere in 3D space
|
|
position: Vec3,
|
|
// Color of the sphere
|
|
color: Color,
|
|
|
|
// Number of line-segments used to approximate the sphere geometry
|
|
resolution: u32,
|
|
}
|
|
|
|
impl<Config, Clear> SphereBuilder<'_, '_, '_, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
/// Set the number of line-segments used to approximate the sphere geometry.
|
|
pub fn resolution(mut self, resolution: u32) -> Self {
|
|
self.resolution = resolution;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<Config, Clear> Drop for SphereBuilder<'_, '_, '_, Config, Clear>
|
|
where
|
|
Config: GizmoConfigGroup,
|
|
Clear: 'static + Send + Sync,
|
|
{
|
|
fn drop(&mut self) {
|
|
if !self.gizmos.enabled {
|
|
return;
|
|
}
|
|
|
|
let SphereBuilder {
|
|
radius,
|
|
position: center,
|
|
rotation,
|
|
color,
|
|
resolution,
|
|
..
|
|
} = self;
|
|
|
|
// draws one great circle around each of the local axes
|
|
Vec3::AXES.into_iter().for_each(|axis| {
|
|
let normal = *rotation * axis;
|
|
self.gizmos
|
|
.circle(*center, Dir3::new_unchecked(normal), *radius, *color)
|
|
.resolution(*resolution);
|
|
});
|
|
}
|
|
}
|