Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/circles.rs
9443 views
1
//! Additional [`GizmoBuffer`] Functions -- Circles
2
//!
3
//! Includes the implementation of [`GizmoBuffer::circle`] and [`GizmoBuffer::circle_2d`],
4
//! and assorted support items.
5
6
use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup};
7
use bevy_color::Color;
8
use bevy_math::{ops, Isometry2d, Isometry3d, Quat, Vec2, Vec3};
9
use core::f32::consts::TAU;
10
11
pub(crate) const DEFAULT_CIRCLE_RESOLUTION: u32 = 32;
12
13
fn ellipse_inner(half_size: Vec2, resolution: u32) -> impl Iterator<Item = Vec2> {
14
(0..resolution + 1).map(move |i| {
15
let angle = i as f32 * TAU / resolution as f32;
16
let (x, y) = ops::sin_cos(angle);
17
Vec2::new(x, y) * half_size
18
})
19
}
20
21
impl<Config, Clear> GizmoBuffer<Config, Clear>
22
where
23
Config: GizmoConfigGroup,
24
Clear: 'static + Send + Sync,
25
{
26
/// Draw an ellipse in 3D with the given `isometry` applied.
27
///
28
/// If `isometry == Isometry3d::IDENTITY` then
29
///
30
/// - the center is at `Vec3::ZERO`
31
/// - the `half_sizes` are aligned with the `Vec3::X` and `Vec3::Y` axes.
32
///
33
/// # Example
34
/// ```
35
/// # use bevy_gizmos::prelude::*;
36
/// # use bevy_math::prelude::*;
37
/// # use bevy_color::palettes::basic::{RED, GREEN};
38
/// fn system(mut gizmos: Gizmos) {
39
/// gizmos.ellipse(Isometry3d::IDENTITY, Vec2::new(1., 2.), GREEN);
40
///
41
/// // Ellipses have 32 line-segments by default.
42
/// // You may want to increase this for larger ellipses.
43
/// gizmos
44
/// .ellipse(Isometry3d::IDENTITY, Vec2::new(5., 1.), RED)
45
/// .resolution(64);
46
/// }
47
/// # bevy_ecs::system::assert_is_system(system);
48
/// ```
49
#[inline]
50
pub fn ellipse(
51
&mut self,
52
isometry: impl Into<Isometry3d>,
53
half_size: Vec2,
54
color: impl Into<Color>,
55
) -> EllipseBuilder<'_, Config, Clear> {
56
EllipseBuilder {
57
gizmos: self,
58
isometry: isometry.into(),
59
half_size,
60
color: color.into(),
61
resolution: DEFAULT_CIRCLE_RESOLUTION,
62
}
63
}
64
65
/// Draw an ellipse in 2D with the given `isometry` applied.
66
///
67
/// If `isometry == Isometry2d::IDENTITY` then
68
///
69
/// - the center is at `Vec2::ZERO`
70
/// - the `half_sizes` are aligned with the `Vec2::X` and `Vec2::Y` axes.
71
///
72
/// # Example
73
/// ```
74
/// # use bevy_gizmos::prelude::*;
75
/// # use bevy_math::prelude::*;
76
/// # use bevy_color::palettes::basic::{RED, GREEN};
77
/// fn system(mut gizmos: Gizmos) {
78
/// gizmos.ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(2., 1.), GREEN);
79
///
80
/// // Ellipses have 32 line-segments by default.
81
/// // You may want to increase this for larger ellipses.
82
/// gizmos
83
/// .ellipse_2d(Isometry2d::from_rotation(Rot2::degrees(180.0)), Vec2::new(5., 1.), RED)
84
/// .resolution(64);
85
/// }
86
/// # bevy_ecs::system::assert_is_system(system);
87
/// ```
88
#[inline]
89
pub fn ellipse_2d(
90
&mut self,
91
isometry: impl Into<Isometry2d>,
92
half_size: Vec2,
93
color: impl Into<Color>,
94
) -> Ellipse2dBuilder<'_, Config, Clear> {
95
Ellipse2dBuilder {
96
gizmos: self,
97
isometry: isometry.into(),
98
half_size,
99
color: color.into(),
100
resolution: DEFAULT_CIRCLE_RESOLUTION,
101
}
102
}
103
104
/// Draw a circle in 3D with the given `isometry` applied.
105
///
106
/// If `isometry == Isometry3d::IDENTITY` then
107
///
108
/// - the center is at `Vec3::ZERO`
109
/// - the radius is aligned with the `Vec3::X` and `Vec3::Y` axes.
110
///
111
/// # Example
112
/// ```
113
/// # use bevy_gizmos::prelude::*;
114
/// # use bevy_math::prelude::*;
115
/// # use bevy_color::palettes::basic::{RED, GREEN};
116
/// fn system(mut gizmos: Gizmos) {
117
/// gizmos.circle(Isometry3d::IDENTITY, 1., GREEN);
118
///
119
/// // Circles have 32 line-segments by default.
120
/// // You may want to increase this for larger circles.
121
/// gizmos
122
/// .circle(Isometry3d::IDENTITY, 5., RED)
123
/// .resolution(64);
124
/// }
125
/// # bevy_ecs::system::assert_is_system(system);
126
/// ```
127
#[inline]
128
pub fn circle(
129
&mut self,
130
isometry: impl Into<Isometry3d>,
131
radius: f32,
132
color: impl Into<Color>,
133
) -> EllipseBuilder<'_, Config, Clear> {
134
EllipseBuilder {
135
gizmos: self,
136
isometry: isometry.into(),
137
half_size: Vec2::splat(radius),
138
color: color.into(),
139
resolution: DEFAULT_CIRCLE_RESOLUTION,
140
}
141
}
142
143
/// Draw a circle in 2D with the given `isometry` applied.
144
///
145
/// If `isometry == Isometry2d::IDENTITY` then
146
///
147
/// - the center is at `Vec2::ZERO`
148
/// - the radius is aligned with the `Vec2::X` and `Vec2::Y` axes.
149
///
150
/// # Example
151
/// ```
152
/// # use bevy_gizmos::prelude::*;
153
/// # use bevy_math::prelude::*;
154
/// # use bevy_color::palettes::basic::{RED, GREEN};
155
/// fn system(mut gizmos: Gizmos) {
156
/// gizmos.circle_2d(Isometry2d::IDENTITY, 1., GREEN);
157
///
158
/// // Circles have 32 line-segments by default.
159
/// // You may want to increase this for larger circles.
160
/// gizmos
161
/// .circle_2d(Isometry2d::IDENTITY, 5., RED)
162
/// .resolution(64);
163
/// }
164
/// # bevy_ecs::system::assert_is_system(system);
165
/// ```
166
#[inline]
167
pub fn circle_2d(
168
&mut self,
169
isometry: impl Into<Isometry2d>,
170
radius: f32,
171
color: impl Into<Color>,
172
) -> Ellipse2dBuilder<'_, Config, Clear> {
173
Ellipse2dBuilder {
174
gizmos: self,
175
isometry: isometry.into(),
176
half_size: Vec2::splat(radius),
177
color: color.into(),
178
resolution: DEFAULT_CIRCLE_RESOLUTION,
179
}
180
}
181
182
/// Draw a wireframe sphere in 3D made out of 3 circles around the axes with the given
183
/// `isometry` applied.
184
///
185
/// If `isometry == Isometry3d::IDENTITY` then
186
///
187
/// - the center is at `Vec3::ZERO`
188
/// - the 3 circles are in the XY, YZ and XZ planes.
189
///
190
/// # Example
191
/// ```
192
/// # use bevy_gizmos::prelude::*;
193
/// # use bevy_math::prelude::*;
194
/// # use bevy_color::Color;
195
/// fn system(mut gizmos: Gizmos) {
196
/// gizmos.sphere(Isometry3d::IDENTITY, 1., Color::BLACK);
197
///
198
/// // Each circle has 32 line-segments by default.
199
/// // You may want to increase this for larger spheres.
200
/// gizmos
201
/// .sphere(Isometry3d::IDENTITY, 5., Color::BLACK)
202
/// .resolution(64);
203
/// }
204
/// # bevy_ecs::system::assert_is_system(system);
205
/// ```
206
#[inline]
207
pub fn sphere(
208
&mut self,
209
isometry: impl Into<Isometry3d>,
210
radius: f32,
211
color: impl Into<Color>,
212
) -> SphereBuilder<'_, Config, Clear> {
213
SphereBuilder {
214
gizmos: self,
215
radius,
216
isometry: isometry.into(),
217
color: color.into(),
218
resolution: DEFAULT_CIRCLE_RESOLUTION,
219
}
220
}
221
}
222
223
/// A builder returned by [`GizmoBuffer::ellipse`].
224
pub struct EllipseBuilder<'a, Config, Clear>
225
where
226
Config: GizmoConfigGroup,
227
Clear: 'static + Send + Sync,
228
{
229
gizmos: &'a mut GizmoBuffer<Config, Clear>,
230
isometry: Isometry3d,
231
half_size: Vec2,
232
color: Color,
233
resolution: u32,
234
}
235
236
impl<Config, Clear> EllipseBuilder<'_, Config, Clear>
237
where
238
Config: GizmoConfigGroup,
239
Clear: 'static + Send + Sync,
240
{
241
/// Set the number of lines used to approximate the geometry of this ellipse.
242
pub fn resolution(mut self, resolution: u32) -> Self {
243
self.resolution = resolution;
244
self
245
}
246
}
247
248
impl<Config, Clear> Drop for EllipseBuilder<'_, Config, Clear>
249
where
250
Config: GizmoConfigGroup,
251
Clear: 'static + Send + Sync,
252
{
253
fn drop(&mut self) {
254
if !self.gizmos.enabled {
255
return;
256
}
257
258
let positions = ellipse_inner(self.half_size, self.resolution)
259
.map(|vec2| self.isometry * vec2.extend(0.));
260
self.gizmos.linestrip(positions, self.color);
261
}
262
}
263
264
/// A builder returned by [`GizmoBuffer::ellipse_2d`].
265
pub struct Ellipse2dBuilder<'a, Config, Clear>
266
where
267
Config: GizmoConfigGroup,
268
Clear: 'static + Send + Sync,
269
{
270
gizmos: &'a mut GizmoBuffer<Config, Clear>,
271
isometry: Isometry2d,
272
half_size: Vec2,
273
color: Color,
274
resolution: u32,
275
}
276
277
impl<Config, Clear> Ellipse2dBuilder<'_, Config, Clear>
278
where
279
Config: GizmoConfigGroup,
280
Clear: 'static + Send + Sync,
281
{
282
/// Set the number of line-segments used to approximate the geometry of this ellipse.
283
pub fn resolution(mut self, resolution: u32) -> Self {
284
self.resolution = resolution;
285
self
286
}
287
}
288
289
impl<Config, Clear> Drop for Ellipse2dBuilder<'_, Config, Clear>
290
where
291
Config: GizmoConfigGroup,
292
Clear: 'static + Send + Sync,
293
{
294
/// Set the number of line-segments for this ellipse.
295
fn drop(&mut self) {
296
if !self.gizmos.enabled {
297
return;
298
};
299
300
let positions =
301
ellipse_inner(self.half_size, self.resolution).map(|vec2| self.isometry * vec2);
302
self.gizmos.linestrip_2d(positions, self.color);
303
}
304
}
305
306
/// A builder returned by [`GizmoBuffer::sphere`].
307
pub struct SphereBuilder<'a, Config, Clear>
308
where
309
Config: GizmoConfigGroup,
310
Clear: 'static + Send + Sync,
311
{
312
gizmos: &'a mut GizmoBuffer<Config, Clear>,
313
314
// Radius of the sphere
315
radius: f32,
316
317
isometry: Isometry3d,
318
// Color of the sphere
319
color: Color,
320
321
// Number of line-segments used to approximate the sphere geometry
322
resolution: u32,
323
}
324
325
impl<Config, Clear> SphereBuilder<'_, Config, Clear>
326
where
327
Config: GizmoConfigGroup,
328
Clear: 'static + Send + Sync,
329
{
330
/// Set the number of line-segments used to approximate the sphere geometry.
331
pub fn resolution(mut self, resolution: u32) -> Self {
332
self.resolution = resolution;
333
self
334
}
335
}
336
337
impl<Config, Clear> Drop for SphereBuilder<'_, Config, Clear>
338
where
339
Config: GizmoConfigGroup,
340
Clear: 'static + Send + Sync,
341
{
342
fn drop(&mut self) {
343
if !self.gizmos.enabled {
344
return;
345
}
346
347
// draws one great circle around each of the local axes
348
Vec3::AXES.into_iter().for_each(|axis| {
349
let axis_rotation = Isometry3d::from_rotation(Quat::from_rotation_arc(Vec3::Z, axis));
350
self.gizmos
351
.circle(self.isometry * axis_rotation, self.radius, self.color)
352
.resolution(self.resolution);
353
});
354
}
355
}
356
357