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