Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/grid.rs
9412 views
1
//! Additional [`GizmoBuffer`] Functions -- Grids
2
//!
3
//! Includes the implementation of [`GizmoBuffer::grid`] and [`GizmoBuffer::grid_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, UVec2, UVec3, Vec2, Vec3};
9
10
/// A builder returned by [`GizmoBuffer::grid_3d`]
11
pub struct GridBuilder3d<'a, Config, Clear>
12
where
13
Config: GizmoConfigGroup,
14
Clear: 'static + Send + Sync,
15
{
16
gizmos: &'a mut GizmoBuffer<Config, Clear>,
17
isometry: Isometry3d,
18
spacing: Vec3,
19
cell_count: UVec3,
20
skew: Vec3,
21
outer_edges: [bool; 3],
22
color: Color,
23
}
24
/// A builder returned by [`GizmoBuffer::grid`] and [`GizmoBuffer::grid_2d`]
25
pub struct GridBuilder2d<'a, Config, Clear>
26
where
27
Config: GizmoConfigGroup,
28
Clear: 'static + Send + Sync,
29
{
30
gizmos: &'a mut GizmoBuffer<Config, Clear>,
31
isometry: Isometry3d,
32
spacing: Vec2,
33
cell_count: UVec2,
34
skew: Vec2,
35
outer_edges: [bool; 2],
36
color: Color,
37
}
38
39
impl<Config, Clear> GridBuilder3d<'_, Config, Clear>
40
where
41
Config: GizmoConfigGroup,
42
Clear: 'static + Send + Sync,
43
{
44
/// Skews the grid by `tan(skew)` in the x direction.
45
/// `skew` is in radians
46
pub fn skew_x(mut self, skew: f32) -> Self {
47
self.skew.x = skew;
48
self
49
}
50
/// Skews the grid by `tan(skew)` in the y direction.
51
/// `skew` is in radians
52
pub fn skew_y(mut self, skew: f32) -> Self {
53
self.skew.y = skew;
54
self
55
}
56
/// Skews the grid by `tan(skew)` in the z direction.
57
/// `skew` is in radians
58
pub fn skew_z(mut self, skew: f32) -> Self {
59
self.skew.z = skew;
60
self
61
}
62
/// Skews the grid by `tan(skew)` in the x, y and z directions.
63
/// `skew` is in radians
64
pub fn skew(mut self, skew: Vec3) -> Self {
65
self.skew = skew;
66
self
67
}
68
69
/// Declare that the outer edges of the grid parallel to the x axis should be drawn.
70
/// By default, the outer edges will not be drawn.
71
pub fn outer_edges_x(mut self) -> Self {
72
self.outer_edges[0] = true;
73
self
74
}
75
/// Declare that the outer edges of the grid parallel to the y axis should be drawn.
76
/// By default, the outer edges will not be drawn.
77
pub fn outer_edges_y(mut self) -> Self {
78
self.outer_edges[1] = true;
79
self
80
}
81
/// Declare that the outer edges of the grid parallel to the z axis should be drawn.
82
/// By default, the outer edges will not be drawn.
83
pub fn outer_edges_z(mut self) -> Self {
84
self.outer_edges[2] = true;
85
self
86
}
87
/// Declare that all outer edges of the grid should be drawn.
88
/// By default, the outer edges will not be drawn.
89
pub fn outer_edges(mut self) -> Self {
90
self.outer_edges.fill(true);
91
self
92
}
93
}
94
95
impl<Config, Clear> GridBuilder2d<'_, Config, Clear>
96
where
97
Config: GizmoConfigGroup,
98
Clear: 'static + Send + Sync,
99
{
100
/// Skews the grid by `tan(skew)` in the x direction.
101
/// `skew` is in radians
102
pub fn skew_x(mut self, skew: f32) -> Self {
103
self.skew.x = skew;
104
self
105
}
106
/// Skews the grid by `tan(skew)` in the y direction.
107
/// `skew` is in radians
108
pub fn skew_y(mut self, skew: f32) -> Self {
109
self.skew.y = skew;
110
self
111
}
112
/// Skews the grid by `tan(skew)` in the x and y directions.
113
/// `skew` is in radians
114
pub fn skew(mut self, skew: Vec2) -> Self {
115
self.skew = skew;
116
self
117
}
118
119
/// Declare that the outer edges of the grid parallel to the x axis should be drawn.
120
/// By default, the outer edges will not be drawn.
121
pub fn outer_edges_x(mut self) -> Self {
122
self.outer_edges[0] = true;
123
self
124
}
125
/// Declare that the outer edges of the grid parallel to the y axis should be drawn.
126
/// By default, the outer edges will not be drawn.
127
pub fn outer_edges_y(mut self) -> Self {
128
self.outer_edges[1] = true;
129
self
130
}
131
/// Declare that all outer edges of the grid should be drawn.
132
/// By default, the outer edges will not be drawn.
133
pub fn outer_edges(mut self) -> Self {
134
self.outer_edges.fill(true);
135
self
136
}
137
}
138
139
impl<Config, Clear> Drop for GridBuilder3d<'_, Config, Clear>
140
where
141
Config: GizmoConfigGroup,
142
Clear: 'static + Send + Sync,
143
{
144
/// Draws a grid, by drawing lines with the stored [`GizmoBuffer`]
145
fn drop(&mut self) {
146
draw_grid(
147
self.gizmos,
148
self.isometry,
149
self.spacing,
150
self.cell_count,
151
self.skew,
152
self.outer_edges,
153
self.color,
154
);
155
}
156
}
157
158
impl<Config, Clear> Drop for GridBuilder2d<'_, Config, Clear>
159
where
160
Config: GizmoConfigGroup,
161
Clear: 'static + Send + Sync,
162
{
163
fn drop(&mut self) {
164
draw_grid(
165
self.gizmos,
166
self.isometry,
167
self.spacing.extend(0.),
168
self.cell_count.extend(0),
169
self.skew.extend(0.),
170
[self.outer_edges[0], self.outer_edges[1], true],
171
self.color,
172
);
173
}
174
}
175
176
impl<Config, Clear> GizmoBuffer<Config, Clear>
177
where
178
Config: GizmoConfigGroup,
179
Clear: 'static + Send + Sync,
180
{
181
/// Draw a 2D grid in 3D.
182
///
183
/// The grid's default orientation aligns with the XY-plane.
184
///
185
/// # Arguments
186
///
187
/// - `isometry` defines the translation and rotation of the grid.
188
/// - the translation specifies the center of the grid
189
/// - defines the orientation of the grid, by default we assume the grid is contained in a
190
/// plane parallel to the XY plane
191
/// - `cell_count`: defines the amount of cells in the x and y axes
192
/// - `spacing`: defines the distance between cells along the x and y axes
193
/// - `color`: color of the grid
194
///
195
/// # Builder methods
196
///
197
/// - The skew of the grid can be adjusted using the `.skew(...)`, `.skew_x(...)` or `.skew_y(...)` methods. They behave very similar to their CSS equivalents.
198
/// - All outer edges can be toggled on or off using `.outer_edges(...)`. Alternatively you can use `.outer_edges_x(...)` or `.outer_edges_y(...)` to toggle the outer edges along an axis.
199
///
200
/// # Example
201
/// ```
202
/// # use bevy_gizmos::prelude::*;
203
/// # use bevy_math::prelude::*;
204
/// # use bevy_color::palettes::basic::GREEN;
205
/// fn system(mut gizmos: Gizmos) {
206
/// gizmos.grid(
207
/// Isometry3d::IDENTITY,
208
/// UVec2::new(10, 10),
209
/// Vec2::splat(2.),
210
/// GREEN
211
/// )
212
/// .skew_x(0.25)
213
/// .outer_edges();
214
/// }
215
/// # bevy_ecs::system::assert_is_system(system);
216
/// ```
217
pub fn grid(
218
&mut self,
219
isometry: impl Into<Isometry3d>,
220
cell_count: UVec2,
221
spacing: Vec2,
222
color: impl Into<Color>,
223
) -> GridBuilder2d<'_, Config, Clear> {
224
GridBuilder2d {
225
gizmos: self,
226
isometry: isometry.into(),
227
spacing,
228
cell_count,
229
skew: Vec2::ZERO,
230
outer_edges: [false, false],
231
color: color.into(),
232
}
233
}
234
235
/// Draw a 3D grid of voxel-like cells.
236
///
237
/// # Arguments
238
///
239
/// - `isometry` defines the translation and rotation of the grid.
240
/// - the translation specifies the center of the grid
241
/// - defines the orientation of the grid, by default we assume the grid is aligned with all axes
242
/// - `cell_count`: defines the amount of cells in the x, y and z axes
243
/// - `spacing`: defines the distance between cells along the x, y and z axes
244
/// - `color`: color of the grid
245
///
246
/// # Builder methods
247
///
248
/// - The skew of the grid can be adjusted using the `.skew(...)`, `.skew_x(...)`, `.skew_y(...)` or `.skew_z(...)` methods. They behave very similar to their CSS equivalents.
249
/// - All outer edges can be toggled on or off using `.outer_edges(...)`. Alternatively you can use `.outer_edges_x(...)`, `.outer_edges_y(...)` or `.outer_edges_z(...)` to toggle the outer edges along an axis.
250
///
251
/// # Example
252
/// ```
253
/// # use bevy_gizmos::prelude::*;
254
/// # use bevy_math::prelude::*;
255
/// # use bevy_color::palettes::basic::GREEN;
256
/// fn system(mut gizmos: Gizmos) {
257
/// gizmos.grid_3d(
258
/// Isometry3d::IDENTITY,
259
/// UVec3::new(10, 2, 10),
260
/// Vec3::splat(2.),
261
/// GREEN
262
/// )
263
/// .skew_x(0.25)
264
/// .outer_edges();
265
/// }
266
/// # bevy_ecs::system::assert_is_system(system);
267
/// ```
268
pub fn grid_3d(
269
&mut self,
270
isometry: impl Into<Isometry3d>,
271
cell_count: UVec3,
272
spacing: Vec3,
273
color: impl Into<Color>,
274
) -> GridBuilder3d<'_, Config, Clear> {
275
GridBuilder3d {
276
gizmos: self,
277
isometry: isometry.into(),
278
spacing,
279
cell_count,
280
skew: Vec3::ZERO,
281
outer_edges: [false, false, false],
282
color: color.into(),
283
}
284
}
285
286
/// Draw a grid in 2D.
287
///
288
/// # Arguments
289
///
290
/// - `isometry` defines the translation and rotation of the grid.
291
/// - the translation specifies the center of the grid
292
/// - defines the orientation of the grid, by default we assume the grid is aligned with all axes
293
/// - `cell_count`: defines the amount of cells in the x and y axes
294
/// - `spacing`: defines the distance between cells along the x and y axes
295
/// - `color`: color of the grid
296
///
297
/// # Builder methods
298
///
299
/// - The skew of the grid can be adjusted using the `.skew(...)`, `.skew_x(...)` or `.skew_y(...)` methods. They behave very similar to their CSS equivalents.
300
/// - All outer edges can be toggled on or off using `.outer_edges(...)`. Alternatively you can use `.outer_edges_x(...)` or `.outer_edges_y(...)` to toggle the outer edges along an axis.
301
///
302
/// # Example
303
/// ```
304
/// # use bevy_gizmos::prelude::*;
305
/// # use bevy_math::prelude::*;
306
/// # use bevy_color::palettes::basic::GREEN;
307
/// fn system(mut gizmos: Gizmos) {
308
/// gizmos.grid_2d(
309
/// Isometry2d::IDENTITY,
310
/// UVec2::new(10, 10),
311
/// Vec2::splat(1.),
312
/// GREEN
313
/// )
314
/// .skew_x(0.25)
315
/// .outer_edges();
316
/// }
317
/// # bevy_ecs::system::assert_is_system(system);
318
/// ```
319
pub fn grid_2d(
320
&mut self,
321
isometry: impl Into<Isometry2d>,
322
cell_count: UVec2,
323
spacing: Vec2,
324
color: impl Into<Color>,
325
) -> GridBuilder2d<'_, Config, Clear> {
326
let isometry = isometry.into();
327
GridBuilder2d {
328
gizmos: self,
329
isometry: Isometry3d::new(
330
isometry.translation.extend(0.0),
331
Quat::from_rotation_z(isometry.rotation.as_radians()),
332
),
333
spacing,
334
cell_count,
335
skew: Vec2::ZERO,
336
outer_edges: [false, false],
337
color: color.into(),
338
}
339
}
340
}
341
342
fn draw_grid<Config, Clear>(
343
gizmos: &mut GizmoBuffer<Config, Clear>,
344
isometry: Isometry3d,
345
spacing: Vec3,
346
cell_count: UVec3,
347
skew: Vec3,
348
outer_edges: [bool; 3],
349
color: Color,
350
) where
351
Config: GizmoConfigGroup,
352
Clear: 'static + Send + Sync,
353
{
354
if !gizmos.enabled {
355
return;
356
}
357
358
#[inline]
359
fn or_zero(cond: bool, val: Vec3) -> Vec3 {
360
if cond {
361
val
362
} else {
363
Vec3::ZERO
364
}
365
}
366
367
// Offset between two adjacent grid cells along the x/y-axis and accounting for skew.
368
let skew_tan = Vec3::from(skew.to_array().map(ops::tan));
369
let dx = or_zero(
370
cell_count.x != 0,
371
spacing.x * Vec3::new(1., skew_tan.y, skew_tan.z),
372
);
373
let dy = or_zero(
374
cell_count.y != 0,
375
spacing.y * Vec3::new(skew_tan.x, 1., skew_tan.z),
376
);
377
let dz = or_zero(
378
cell_count.z != 0,
379
spacing.z * Vec3::new(skew_tan.x, skew_tan.y, 1.),
380
);
381
382
// Bottom-left-front corner of the grid
383
let cell_count_half = cell_count.as_vec3() * 0.5;
384
let grid_start = -cell_count_half.x * dx - cell_count_half.y * dy - cell_count_half.z * dz;
385
386
#[inline]
387
fn cell_count_to_line_count(include_outer: bool, cell_count: u32) -> u32 {
388
if include_outer {
389
cell_count.saturating_add(1)
390
} else {
391
cell_count.saturating_sub(1).max(1)
392
}
393
}
394
395
let x_line_count = UVec2::new(
396
cell_count_to_line_count(outer_edges[0], cell_count.y),
397
cell_count_to_line_count(outer_edges[0], cell_count.z),
398
);
399
let y_line_count = UVec2::new(
400
cell_count_to_line_count(outer_edges[1], cell_count.z),
401
cell_count_to_line_count(outer_edges[1], cell_count.x),
402
);
403
let z_line_count = UVec2::new(
404
cell_count_to_line_count(outer_edges[2], cell_count.x),
405
cell_count_to_line_count(outer_edges[2], cell_count.y),
406
);
407
408
let x_start = grid_start + or_zero(!outer_edges[0], dy + dz);
409
let y_start = grid_start + or_zero(!outer_edges[1], dx + dz);
410
let z_start = grid_start + or_zero(!outer_edges[2], dx + dy);
411
412
fn iter_lines(
413
delta_a: Vec3,
414
delta_b: Vec3,
415
delta_c: Vec3,
416
line_count: UVec2,
417
cell_count: u32,
418
start: Vec3,
419
) -> impl Iterator<Item = [Vec3; 2]> {
420
let dline = delta_a * cell_count as f32;
421
(0..line_count.x).map(|v| v as f32).flat_map(move |b| {
422
(0..line_count.y).map(|v| v as f32).map(move |c| {
423
let line_start = start + b * delta_b + c * delta_c;
424
let line_end = line_start + dline;
425
[line_start, line_end]
426
})
427
})
428
}
429
430
// Lines along the x direction
431
let x_lines = iter_lines(dx, dy, dz, x_line_count, cell_count.x, x_start);
432
// Lines along the y direction
433
let y_lines = iter_lines(dy, dz, dx, y_line_count, cell_count.y, y_start);
434
// Lines along the z direction
435
let z_lines = iter_lines(dz, dx, dy, z_line_count, cell_count.z, z_start);
436
437
x_lines
438
.chain(y_lines)
439
.chain(z_lines)
440
.map(|vec3s| vec3s.map(|vec3| isometry * vec3))
441
.for_each(|[start, end]| {
442
gizmos.line(start, end, color);
443
});
444
}
445
446