|
4 | 4 | //! and assorted support items.
|
5 | 5 |
|
6 | 6 | use crate::prelude::{GizmoConfigGroup, Gizmos};
|
| 7 | +use bevy_math::Mat2; |
7 | 8 | use bevy_math::{primitives::Direction3d, Quat, Vec2, Vec3};
|
8 | 9 | use bevy_render::color::Color;
|
9 | 10 | use std::f32::consts::TAU;
|
10 | 11 |
|
11 | 12 | pub(crate) const DEFAULT_CIRCLE_SEGMENTS: usize = 32;
|
12 | 13 |
|
13 |
| -fn circle_inner(radius: f32, segments: usize) -> impl Iterator<Item = Vec2> { |
| 14 | +fn ellipse_inner(half_size: Vec2, segments: usize) -> impl Iterator<Item = Vec2> { |
14 | 15 | (0..segments + 1).map(move |i| {
|
15 | 16 | let angle = i as f32 * TAU / segments as f32;
|
16 |
| - Vec2::from(angle.sin_cos()) * radius |
| 17 | + let (x, y) = angle.sin_cos(); |
| 18 | + Vec2::new(x, y) * half_size |
17 | 19 | })
|
18 | 20 | }
|
19 | 21 |
|
20 | 22 | impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
|
| 23 | + /// Draw an ellipse in 3D at `position` with the flat side facing `normal`. |
| 24 | + /// |
| 25 | + /// This should be called for each frame the ellipse needs to be rendered. |
| 26 | + /// |
| 27 | + /// # Example |
| 28 | + /// ``` |
| 29 | + /// # use bevy_gizmos::prelude::*; |
| 30 | + /// # use bevy_render::prelude::*; |
| 31 | + /// # use bevy_math::prelude::*; |
| 32 | + /// fn system(mut gizmos: Gizmos) { |
| 33 | + /// gizmos.ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(1., 2.), Color::GREEN); |
| 34 | + /// |
| 35 | + /// // Ellipses have 32 line-segments by default. |
| 36 | + /// // You may want to increase this for larger ellipses. |
| 37 | + /// gizmos |
| 38 | + /// .ellipse(Vec3::ZERO, Quat::IDENTITY, Vec2::new(5., 1.), Color::RED) |
| 39 | + /// .segments(64); |
| 40 | + /// } |
| 41 | + /// # bevy_ecs::system::assert_is_system(system); |
| 42 | + /// ``` |
| 43 | + #[inline] |
| 44 | + pub fn ellipse( |
| 45 | + &mut self, |
| 46 | + position: Vec3, |
| 47 | + rotation: Quat, |
| 48 | + half_size: Vec2, |
| 49 | + color: Color, |
| 50 | + ) -> EllipseBuilder<'_, 'w, 's, T> { |
| 51 | + EllipseBuilder { |
| 52 | + gizmos: self, |
| 53 | + position, |
| 54 | + rotation, |
| 55 | + half_size, |
| 56 | + color, |
| 57 | + segments: DEFAULT_CIRCLE_SEGMENTS, |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + /// Draw an ellipse in 2D. |
| 62 | + /// |
| 63 | + /// This should be called for each frame the ellipse needs to be rendered. |
| 64 | + /// |
| 65 | + /// # Example |
| 66 | + /// ``` |
| 67 | + /// # use bevy_gizmos::prelude::*; |
| 68 | + /// # use bevy_render::prelude::*; |
| 69 | + /// # use bevy_math::prelude::*; |
| 70 | + /// fn system(mut gizmos: Gizmos) { |
| 71 | + /// gizmos.ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(2., 1.), Color::GREEN); |
| 72 | + /// |
| 73 | + /// // Ellipses have 32 line-segments by default. |
| 74 | + /// // You may want to increase this for larger ellipses. |
| 75 | + /// gizmos |
| 76 | + /// .ellipse_2d(Vec2::ZERO, 180.0_f32.to_radians(), Vec2::new(5., 1.), Color::RED) |
| 77 | + /// .segments(64); |
| 78 | + /// } |
| 79 | + /// # bevy_ecs::system::assert_is_system(system); |
| 80 | + /// ``` |
| 81 | + #[inline] |
| 82 | + pub fn ellipse_2d( |
| 83 | + &mut self, |
| 84 | + position: Vec2, |
| 85 | + angle: f32, |
| 86 | + half_size: Vec2, |
| 87 | + color: Color, |
| 88 | + ) -> Ellipse2dBuilder<'_, 'w, 's, T> { |
| 89 | + Ellipse2dBuilder { |
| 90 | + gizmos: self, |
| 91 | + position, |
| 92 | + rotation: Mat2::from_angle(angle), |
| 93 | + half_size, |
| 94 | + color, |
| 95 | + segments: DEFAULT_CIRCLE_SEGMENTS, |
| 96 | + } |
| 97 | + } |
| 98 | + |
21 | 99 | /// Draw a circle in 3D at `position` with the flat side facing `normal`.
|
22 | 100 | ///
|
23 | 101 | /// This should be called for each frame the circle needs to be rendered.
|
@@ -45,12 +123,12 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
|
45 | 123 | normal: Direction3d,
|
46 | 124 | radius: f32,
|
47 | 125 | color: Color,
|
48 |
| - ) -> CircleBuilder<'_, 'w, 's, T> { |
49 |
| - CircleBuilder { |
| 126 | + ) -> EllipseBuilder<'_, 'w, 's, T> { |
| 127 | + EllipseBuilder { |
50 | 128 | gizmos: self,
|
51 | 129 | position,
|
52 |
| - normal, |
53 |
| - radius, |
| 130 | + rotation: Quat::from_rotation_arc(Vec3::Z, *normal), |
| 131 | + half_size: Vec2::splat(radius), |
54 | 132 | color,
|
55 | 133 | segments: DEFAULT_CIRCLE_SEGMENTS,
|
56 | 134 | }
|
@@ -82,70 +160,76 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
|
82 | 160 | position: Vec2,
|
83 | 161 | radius: f32,
|
84 | 162 | color: Color,
|
85 |
| - ) -> Circle2dBuilder<'_, 'w, 's, T> { |
86 |
| - Circle2dBuilder { |
| 163 | + ) -> Ellipse2dBuilder<'_, 'w, 's, T> { |
| 164 | + Ellipse2dBuilder { |
87 | 165 | gizmos: self,
|
88 | 166 | position,
|
89 |
| - radius, |
| 167 | + rotation: Mat2::IDENTITY, |
| 168 | + half_size: Vec2::splat(radius), |
90 | 169 | color,
|
91 | 170 | segments: DEFAULT_CIRCLE_SEGMENTS,
|
92 | 171 | }
|
93 | 172 | }
|
94 | 173 | }
|
95 | 174 |
|
96 |
| -/// A builder returned by [`Gizmos::circle`]. |
97 |
| -pub struct CircleBuilder<'a, 'w, 's, T: GizmoConfigGroup> { |
| 175 | +/// A builder returned by [`Gizmos::ellipse`]. |
| 176 | +pub struct EllipseBuilder<'a, 'w, 's, T: GizmoConfigGroup> { |
98 | 177 | gizmos: &'a mut Gizmos<'w, 's, T>,
|
99 | 178 | position: Vec3,
|
100 |
| - normal: Direction3d, |
101 |
| - radius: f32, |
| 179 | + rotation: Quat, |
| 180 | + half_size: Vec2, |
102 | 181 | color: Color,
|
103 | 182 | segments: usize,
|
104 | 183 | }
|
105 | 184 |
|
106 |
| -impl<T: GizmoConfigGroup> CircleBuilder<'_, '_, '_, T> { |
107 |
| - /// Set the number of line-segments for this circle. |
| 185 | +impl<T: GizmoConfigGroup> EllipseBuilder<'_, '_, '_, T> { |
| 186 | + /// Set the number of line-segments for this ellipse. |
108 | 187 | pub fn segments(mut self, segments: usize) -> Self {
|
109 | 188 | self.segments = segments;
|
110 | 189 | self
|
111 | 190 | }
|
112 | 191 | }
|
113 | 192 |
|
114 |
| -impl<T: GizmoConfigGroup> Drop for CircleBuilder<'_, '_, '_, T> { |
| 193 | +impl<T: GizmoConfigGroup> Drop for EllipseBuilder<'_, '_, '_, T> { |
115 | 194 | fn drop(&mut self) {
|
116 | 195 | if !self.gizmos.enabled {
|
117 | 196 | return;
|
118 | 197 | }
|
119 |
| - let rotation = Quat::from_rotation_arc(Vec3::Z, *self.normal); |
120 |
| - let positions = circle_inner(self.radius, self.segments) |
121 |
| - .map(|vec2| self.position + rotation * vec2.extend(0.)); |
| 198 | + |
| 199 | + let positions = ellipse_inner(self.half_size, self.segments) |
| 200 | + .map(|vec2| self.rotation * vec2.extend(0.)) |
| 201 | + .map(|vec3| vec3 + self.position); |
122 | 202 | self.gizmos.linestrip(positions, self.color);
|
123 | 203 | }
|
124 | 204 | }
|
125 | 205 |
|
126 |
| -/// A builder returned by [`Gizmos::circle_2d`]. |
127 |
| -pub struct Circle2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> { |
| 206 | +/// A builder returned by [`Gizmos::ellipse_2d`]. |
| 207 | +pub struct Ellipse2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> { |
128 | 208 | gizmos: &'a mut Gizmos<'w, 's, T>,
|
129 | 209 | position: Vec2,
|
130 |
| - radius: f32, |
| 210 | + rotation: Mat2, |
| 211 | + half_size: Vec2, |
131 | 212 | color: Color,
|
132 | 213 | segments: usize,
|
133 | 214 | }
|
134 | 215 |
|
135 |
| -impl<T: GizmoConfigGroup> Circle2dBuilder<'_, '_, '_, T> { |
136 |
| - /// Set the number of line-segments for this circle. |
| 216 | +impl<T: GizmoConfigGroup> Ellipse2dBuilder<'_, '_, '_, T> { |
| 217 | + /// Set the number of line-segments for this ellipse. |
137 | 218 | pub fn segments(mut self, segments: usize) -> Self {
|
138 | 219 | self.segments = segments;
|
139 | 220 | self
|
140 | 221 | }
|
141 | 222 | }
|
142 | 223 |
|
143 |
| -impl<T: GizmoConfigGroup> Drop for Circle2dBuilder<'_, '_, '_, T> { |
| 224 | +impl<T: GizmoConfigGroup> Drop for Ellipse2dBuilder<'_, '_, '_, T> { |
144 | 225 | fn drop(&mut self) {
|
145 | 226 | if !self.gizmos.enabled {
|
146 | 227 | return;
|
147 |
| - } |
148 |
| - let positions = circle_inner(self.radius, self.segments).map(|vec2| vec2 + self.position); |
| 228 | + }; |
| 229 | + |
| 230 | + let positions = ellipse_inner(self.half_size, self.segments) |
| 231 | + .map(|vec2| self.rotation * vec2) |
| 232 | + .map(|vec2| vec2 + self.position); |
149 | 233 | self.gizmos.linestrip_2d(positions, self.color);
|
150 | 234 | }
|
151 | 235 | }
|
0 commit comments