Fix arc_2d Gizmos (#14731)
				
					
				
			# Objective
`arc_2d` wasn't actually doing what the docs were saying. The arc wasn't
offset by what was previously `direction_angle` but by `direction_angle
- arc_angle / 2.0`. This meant that the arcs center was laying on the
`Vec2::Y` axis and then it was offset. This was probably done to fit the
behavior of the `Arc2D` primitive. I would argue that this isn't
desirable for the plain `arc_2d` gizmo method since
- a) the docs get longer to explain the weird centering
- b) the mental model the user has to know gets bigger with more
implicit assumptions
given the code
```rust
    my_gizmos.arc_2d(Vec2::ZERO, 0.0, FRAC_PI_2, 75.0, ORANGE_RED);
```
we get

where after the fix with
```rust
    my_gizmos.arc_2d(Isometry2d::IDENTITY, FRAC_PI_2, 75.0, ORANGE_RED);
```
we get

To get the same result with the previous implementation you would have
to randomly add `arc_angle / 2.0` to the `direction_angle`.
```rust
    my_gizmos.arc_2d(Vec2::ZERO, FRAC_PI_4, FRAC_PI_2, 75.0, ORANGE_RED);
```
This makes constructing similar helping functions as they already exist
in 3D like
- `long_arc_2d_between`
- `short_arc_2d_between`
 much harder.
## Solution
- Make the arc really start at `Vec2::Y * radius` in counter-clockwise
direction + offset by an angle as the docs state it
- Use `Isometry2d` instead of `position : Vec2` and `direction_angle :
f32` to reduce the chance of messing up rotation/translation
- Adjust the docs for the changes above
- Adjust the gizmo rendering of some primitives
## Testing
- check `2d_gizmos.rs` and `render_primitives.rs` examples
## Migration Guide
- users have to adjust their usages of `arc_2d`:
  - before: 
  ```rust
  arc_2d(
    pos,
    angle,
    arc_angle,
    radius,
    color
  )
  ```
  - after: 
  ```rust
  arc_2d(
// this `+ arc_angle * 0.5` quirk is only if you want to preserve the
previous behavior
    // with the new API.
// feel free to try to fix this though since your current calls to this
function most likely
// involve some computations to counter-act that quirk in the first
place
    Isometry2d::new(pos, Rot2::radians(angle + arc_angle * 0.5),
    arc_angle,
    radius,
    color
  )
  ```
			
			
This commit is contained in:
		
							parent
							
								
									2e36b2719c
								
							
						
					
					
						commit
						6819e998c0
					
				| @ -6,8 +6,8 @@ | ||||
| use crate::circles::DEFAULT_CIRCLE_RESOLUTION; | ||||
| use crate::prelude::{GizmoConfigGroup, Gizmos}; | ||||
| use bevy_color::Color; | ||||
| use bevy_math::{Quat, Vec2, Vec3}; | ||||
| use std::f32::consts::TAU; | ||||
| use bevy_math::{Isometry2d, Quat, Vec2, Vec3}; | ||||
| use std::f32::consts::{FRAC_PI_2, TAU}; | ||||
| 
 | ||||
| // === 2D ===
 | ||||
| 
 | ||||
| @ -21,9 +21,9 @@ where | ||||
|     /// This should be called for each frame the arc needs to be rendered.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     /// - `position` sets the center of this circle.
 | ||||
|     /// - `direction_angle` sets the counter-clockwise  angle in radians between `Vec2::Y` and
 | ||||
|     ///     the vector from `position` to the midpoint of the arc.
 | ||||
|     /// - `isometry` defines the translation and rotation of the arc.
 | ||||
|     ///   - the translation specifies the center of the arc
 | ||||
|     ///   - the rotation is counter-clockwise starting from `Vec2::Y`
 | ||||
|     /// - `arc_angle` sets the length of this arc, in radians.
 | ||||
|     /// - `radius` controls the distance from `position` to this arc, and thus its curvature.
 | ||||
|     /// - `color` sets the color to draw the arc.
 | ||||
| @ -32,15 +32,15 @@ where | ||||
|     /// ```
 | ||||
|     /// # use bevy_gizmos::prelude::*;
 | ||||
|     /// # use bevy_math::prelude::*;
 | ||||
|     /// # use std::f32::consts::PI;
 | ||||
|     /// # use std::f32::consts::FRAC_PI_4;
 | ||||
|     /// # use bevy_color::palettes::basic::{GREEN, RED};
 | ||||
|     /// fn system(mut gizmos: Gizmos) {
 | ||||
|     ///     gizmos.arc_2d(Vec2::ZERO, 0., PI / 4., 1., GREEN);
 | ||||
|     ///     gizmos.arc_2d(Isometry2d::IDENTITY, FRAC_PI_4, 1., GREEN);
 | ||||
|     ///
 | ||||
|     ///     // Arcs have 32 line-segments by default.
 | ||||
|     ///     // You may want to increase this for larger arcs.
 | ||||
|     ///     gizmos
 | ||||
|     ///         .arc_2d(Vec2::ZERO, 0., PI / 4., 5., RED)
 | ||||
|     ///         .arc_2d(Isometry2d::IDENTITY, FRAC_PI_4, 5., RED)
 | ||||
|     ///         .resolution(64);
 | ||||
|     /// }
 | ||||
|     /// # bevy_ecs::system::assert_is_system(system);
 | ||||
| @ -48,16 +48,14 @@ where | ||||
|     #[inline] | ||||
|     pub fn arc_2d( | ||||
|         &mut self, | ||||
|         position: Vec2, | ||||
|         direction_angle: f32, | ||||
|         isometry: Isometry2d, | ||||
|         arc_angle: f32, | ||||
|         radius: f32, | ||||
|         color: impl Into<Color>, | ||||
|     ) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> { | ||||
|         Arc2dBuilder { | ||||
|             gizmos: self, | ||||
|             position, | ||||
|             direction_angle, | ||||
|             isometry, | ||||
|             arc_angle, | ||||
|             radius, | ||||
|             color: color.into(), | ||||
| @ -73,8 +71,7 @@ where | ||||
|     Clear: 'static + Send + Sync, | ||||
| { | ||||
|     gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, | ||||
|     position: Vec2, | ||||
|     direction_angle: f32, | ||||
|     isometry: Isometry2d, | ||||
|     arc_angle: f32, | ||||
|     radius: f32, | ||||
|     color: Color, | ||||
| @ -107,31 +104,19 @@ where | ||||
|             .resolution | ||||
|             .unwrap_or_else(|| resolution_from_angle(self.arc_angle)); | ||||
| 
 | ||||
|         let positions = arc_2d_inner( | ||||
|             self.direction_angle, | ||||
|             self.arc_angle, | ||||
|             self.radius, | ||||
|             resolution, | ||||
|         ) | ||||
|         .map(|vec2| (vec2 + self.position)); | ||||
|         let positions = | ||||
|             arc_2d_inner(self.arc_angle, self.radius, resolution).map(|vec2| self.isometry * vec2); | ||||
|         self.gizmos.linestrip_2d(positions, self.color); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn arc_2d_inner( | ||||
|     direction_angle: f32, | ||||
|     arc_angle: f32, | ||||
|     radius: f32, | ||||
|     resolution: u32, | ||||
| ) -> impl Iterator<Item = Vec2> { | ||||
|     (0..resolution + 1).map(move |i| { | ||||
|         let start = direction_angle - arc_angle / 2.; | ||||
| 
 | ||||
|         let angle = | ||||
|             start + (i as f32 * (arc_angle / resolution as f32)) + std::f32::consts::FRAC_PI_2; | ||||
| 
 | ||||
|         Vec2::new(angle.cos(), angle.sin()) * radius | ||||
|     }) | ||||
| fn arc_2d_inner(arc_angle: f32, radius: f32, resolution: u32) -> impl Iterator<Item = Vec2> { | ||||
|     (0..=resolution) | ||||
|         .map(move |n| arc_angle * n as f32 / resolution as f32) | ||||
|         .map(|angle| angle + FRAC_PI_2) | ||||
|         .map(f32::sin_cos) | ||||
|         .map(|(sin, cos)| Vec2::new(cos, sin)) | ||||
|         .map(move |vec2| vec2 * radius) | ||||
| } | ||||
| 
 | ||||
| // === 3D ===
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| //! A module for rendering each of the 2D [`bevy_math::primitives`] with [`Gizmos`].
 | ||||
| 
 | ||||
| use std::f32::consts::PI; | ||||
| use std::f32::consts::{FRAC_PI_2, PI}; | ||||
| 
 | ||||
| use super::helpers::*; | ||||
| 
 | ||||
| @ -10,7 +10,7 @@ use bevy_math::primitives::{ | ||||
|     CircularSegment, Ellipse, Line2d, Plane2d, Polygon, Polyline2d, Primitive2d, Rectangle, | ||||
|     RegularPolygon, Rhombus, Segment2d, Triangle2d, | ||||
| }; | ||||
| use bevy_math::{Dir2, Mat2, Vec2}; | ||||
| use bevy_math::{Dir2, Isometry2d, Mat2, Rot2, Vec2}; | ||||
| 
 | ||||
| use crate::prelude::{GizmoConfigGroup, Gizmos}; | ||||
| 
 | ||||
| @ -86,8 +86,7 @@ where | ||||
|         } | ||||
| 
 | ||||
|         self.arc_2d( | ||||
|             position, | ||||
|             angle, | ||||
|             Isometry2d::new(position, Rot2::radians(angle - primitive.half_angle)), | ||||
|             primitive.half_angle * 2.0, | ||||
|             primitive.radius, | ||||
|             color, | ||||
| @ -139,8 +138,7 @@ where | ||||
| 
 | ||||
|         // we need to draw the arc part of the sector, and the two lines connecting the arc and the center
 | ||||
|         self.arc_2d( | ||||
|             position, | ||||
|             angle, | ||||
|             Isometry2d::new(position, Rot2::radians(angle - primitive.arc.half_angle)), | ||||
|             primitive.arc.half_angle * 2.0, | ||||
|             primitive.arc.radius, | ||||
|             color, | ||||
| @ -179,8 +177,7 @@ where | ||||
| 
 | ||||
|         // we need to draw the arc part of the segment, and the line connecting the two ends
 | ||||
|         self.arc_2d( | ||||
|             position, | ||||
|             angle, | ||||
|             Isometry2d::new(position, Rot2::radians(angle - primitive.arc.half_angle)), | ||||
|             primitive.arc.half_angle * 2.0, | ||||
|             primitive.arc.radius, | ||||
|             color, | ||||
| @ -386,20 +383,18 @@ where | ||||
|         self.line_2d(bottom_left, top_left, polymorphic_color); | ||||
|         self.line_2d(bottom_right, top_right, polymorphic_color); | ||||
| 
 | ||||
|         let start_angle_top = angle; | ||||
|         let start_angle_bottom = PI + angle; | ||||
|         let start_angle_top = angle - FRAC_PI_2; | ||||
|         let start_angle_bottom = angle + FRAC_PI_2; | ||||
| 
 | ||||
|         // draw arcs
 | ||||
|         self.arc_2d( | ||||
|             top_center, | ||||
|             start_angle_top, | ||||
|             Isometry2d::new(top_center, Rot2::radians(start_angle_top)), | ||||
|             PI, | ||||
|             primitive.radius, | ||||
|             polymorphic_color, | ||||
|         ); | ||||
|         self.arc_2d( | ||||
|             bottom_center, | ||||
|             start_angle_bottom, | ||||
|             Isometry2d::new(bottom_center, Rot2::radians(start_angle_bottom)), | ||||
|             PI, | ||||
|             primitive.radius, | ||||
|             polymorphic_color, | ||||
|  | ||||
| @ -57,9 +57,9 @@ pub mod prelude { | ||||
|         }, | ||||
|         direction::{Dir2, Dir3, Dir3A}, | ||||
|         primitives::*, | ||||
|         BVec2, BVec3, BVec4, EulerRot, FloatExt, IRect, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, | ||||
|         Quat, Ray2d, Ray3d, Rect, Rot2, StableInterpolate, URect, UVec2, UVec3, UVec4, Vec2, | ||||
|         Vec2Swizzles, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles, | ||||
|         BVec2, BVec3, BVec4, EulerRot, FloatExt, IRect, IVec2, IVec3, IVec4, Isometry2d, | ||||
|         Isometry3d, Mat2, Mat3, Mat4, Quat, Ray2d, Ray3d, Rect, Rot2, StableInterpolate, URect, | ||||
|         UVec2, UVec3, UVec4, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| //! This example demonstrates Bevy's immediate mode drawing API intended for visual debugging.
 | ||||
| 
 | ||||
| use std::f32::consts::{PI, TAU}; | ||||
| use std::f32::consts::{FRAC_PI_2, PI, TAU}; | ||||
| 
 | ||||
| use bevy::{color::palettes::css::*, prelude::*}; | ||||
| use bevy::{color::palettes::css::*, math::Isometry2d, prelude::*}; | ||||
| 
 | ||||
| fn main() { | ||||
|     App::new() | ||||
| @ -87,7 +87,13 @@ fn draw_example_collection( | ||||
| 
 | ||||
|     // Arcs default resolution is linearly interpolated between
 | ||||
|     // 1 and 32, using the arc length as scalar.
 | ||||
|     my_gizmos.arc_2d(Vec2::ZERO, sin / 10., PI / 2., 310., ORANGE_RED); | ||||
|     my_gizmos.arc_2d( | ||||
|         Isometry2d::from_rotation(Rot2::radians(sin / 10.)), | ||||
|         FRAC_PI_2, | ||||
|         310., | ||||
|         ORANGE_RED, | ||||
|     ); | ||||
|     my_gizmos.arc_2d(Isometry2d::IDENTITY, FRAC_PI_2, 75.0, ORANGE_RED); | ||||
| 
 | ||||
|     gizmos.arrow_2d( | ||||
|         Vec2::ZERO, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Robert Walter
						Robert Walter