Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/primitives/dim3.rs
6596 views
1
//! A module for rendering each of the 3D [`bevy_math::primitives`] with [`GizmoBuffer`].
2
3
use super::helpers::*;
4
5
use bevy_color::Color;
6
use bevy_math::{
7
primitives::{
8
Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, Polyline3d,
9
Primitive3d, Segment3d, Sphere, Tetrahedron, Torus, Triangle3d,
10
},
11
Dir3, Isometry3d, Quat, UVec2, Vec2, Vec3,
12
};
13
14
use crate::{circles::SphereBuilder, gizmos::GizmoBuffer, prelude::GizmoConfigGroup};
15
16
const DEFAULT_RESOLUTION: u32 = 5;
17
// length used to simulate infinite lines
18
const INFINITE_LEN: f32 = 10_000.0;
19
20
/// A trait for rendering 3D geometric primitives (`P`) with [`GizmoBuffer`].
21
pub trait GizmoPrimitive3d<P: Primitive3d> {
22
/// The output of `primitive_3d`. This is a builder to set non-default values.
23
type Output<'a>
24
where
25
Self: 'a;
26
27
/// Renders a 3D primitive with its associated details.
28
fn primitive_3d(
29
&mut self,
30
primitive: &P,
31
isometry: impl Into<Isometry3d>,
32
color: impl Into<Color>,
33
) -> Self::Output<'_>;
34
}
35
36
// direction 3d
37
38
impl<Config, Clear> GizmoPrimitive3d<Dir3> for GizmoBuffer<Config, Clear>
39
where
40
Config: GizmoConfigGroup,
41
Clear: 'static + Send + Sync,
42
{
43
type Output<'a>
44
= ()
45
where
46
Self: 'a;
47
48
fn primitive_3d(
49
&mut self,
50
primitive: &Dir3,
51
isometry: impl Into<Isometry3d>,
52
color: impl Into<Color>,
53
) -> Self::Output<'_> {
54
let isometry = isometry.into();
55
let start = Vec3::ZERO;
56
let end = primitive.as_vec3();
57
self.arrow(isometry * start, isometry * end, color);
58
}
59
}
60
61
// sphere
62
63
impl<Config, Clear> GizmoPrimitive3d<Sphere> for GizmoBuffer<Config, Clear>
64
where
65
Config: GizmoConfigGroup,
66
Clear: 'static + Send + Sync,
67
{
68
type Output<'a>
69
= SphereBuilder<'a, Config, Clear>
70
where
71
Self: 'a;
72
73
fn primitive_3d(
74
&mut self,
75
primitive: &Sphere,
76
isometry: impl Into<Isometry3d>,
77
color: impl Into<Color>,
78
) -> Self::Output<'_> {
79
self.sphere(isometry, primitive.radius, color)
80
}
81
}
82
83
// plane 3d
84
85
/// Builder for configuring the drawing options of [`Plane3d`].
86
pub struct Plane3dBuilder<'a, Config, Clear>
87
where
88
Config: GizmoConfigGroup,
89
Clear: 'static + Send + Sync,
90
{
91
gizmos: &'a mut GizmoBuffer<Config, Clear>,
92
93
// Direction of the normal orthogonal to the plane
94
normal: Dir3,
95
96
isometry: Isometry3d,
97
// Color of the plane
98
color: Color,
99
100
// Defines the amount of cells in the x and y axes
101
cell_count: UVec2,
102
// Defines the distance between cells along the x and y axes
103
spacing: Vec2,
104
}
105
106
impl<Config, Clear> Plane3dBuilder<'_, Config, Clear>
107
where
108
Config: GizmoConfigGroup,
109
Clear: 'static + Send + Sync,
110
{
111
/// Set the number of cells in the x and y axes direction.
112
pub fn cell_count(mut self, cell_count: UVec2) -> Self {
113
self.cell_count = cell_count;
114
self
115
}
116
117
/// Set the distance between cells along the x and y axes.
118
pub fn spacing(mut self, spacing: Vec2) -> Self {
119
self.spacing = spacing;
120
self
121
}
122
}
123
124
impl<Config, Clear> GizmoPrimitive3d<Plane3d> for GizmoBuffer<Config, Clear>
125
where
126
Config: GizmoConfigGroup,
127
Clear: 'static + Send + Sync,
128
{
129
type Output<'a>
130
= Plane3dBuilder<'a, Config, Clear>
131
where
132
Self: 'a;
133
134
fn primitive_3d(
135
&mut self,
136
primitive: &Plane3d,
137
isometry: impl Into<Isometry3d>,
138
color: impl Into<Color>,
139
) -> Self::Output<'_> {
140
Plane3dBuilder {
141
gizmos: self,
142
normal: primitive.normal,
143
isometry: isometry.into(),
144
color: color.into(),
145
cell_count: UVec2::splat(3),
146
spacing: Vec2::splat(1.0),
147
}
148
}
149
}
150
151
impl<Config, Clear> Drop for Plane3dBuilder<'_, Config, Clear>
152
where
153
Config: GizmoConfigGroup,
154
Clear: 'static + Send + Sync,
155
{
156
fn drop(&mut self) {
157
if !self.gizmos.enabled {
158
return;
159
}
160
161
self.gizmos
162
.primitive_3d(&self.normal, self.isometry, self.color);
163
// the default orientation of the grid is Z-up
164
let rot = Quat::from_rotation_arc(Vec3::Z, self.normal.as_vec3());
165
self.gizmos.grid(
166
Isometry3d::new(self.isometry.translation, self.isometry.rotation * rot),
167
self.cell_count,
168
self.spacing,
169
self.color,
170
);
171
}
172
}
173
174
// line 3d
175
176
impl<Config, Clear> GizmoPrimitive3d<Line3d> for GizmoBuffer<Config, Clear>
177
where
178
Config: GizmoConfigGroup,
179
Clear: 'static + Send + Sync,
180
{
181
type Output<'a>
182
= ()
183
where
184
Self: 'a;
185
186
fn primitive_3d(
187
&mut self,
188
primitive: &Line3d,
189
isometry: impl Into<Isometry3d>,
190
color: impl Into<Color>,
191
) -> Self::Output<'_> {
192
if !self.enabled {
193
return;
194
}
195
196
let isometry = isometry.into();
197
let color = color.into();
198
let direction = primitive.direction.as_vec3();
199
self.arrow(isometry * Vec3::ZERO, isometry * direction, color);
200
201
let [start, end] = [1.0, -1.0]
202
.map(|sign| sign * INFINITE_LEN)
203
.map(|length| primitive.direction * length)
204
.map(|offset| isometry * offset);
205
self.line(start, end, color);
206
}
207
}
208
209
// segment 3d
210
211
impl<Config, Clear> GizmoPrimitive3d<Segment3d> for GizmoBuffer<Config, Clear>
212
where
213
Config: GizmoConfigGroup,
214
Clear: 'static + Send + Sync,
215
{
216
type Output<'a>
217
= ()
218
where
219
Self: 'a;
220
221
fn primitive_3d(
222
&mut self,
223
primitive: &Segment3d,
224
isometry: impl Into<Isometry3d>,
225
color: impl Into<Color>,
226
) -> Self::Output<'_> {
227
if !self.enabled {
228
return;
229
}
230
231
let transformed = primitive.transformed(isometry);
232
self.line(transformed.point1(), transformed.point2(), color);
233
}
234
}
235
236
// polyline 3d
237
238
impl<Config, Clear> GizmoPrimitive3d<Polyline3d> for GizmoBuffer<Config, Clear>
239
where
240
Config: GizmoConfigGroup,
241
Clear: 'static + Send + Sync,
242
{
243
type Output<'a>
244
= ()
245
where
246
Self: 'a;
247
248
fn primitive_3d(
249
&mut self,
250
primitive: &Polyline3d,
251
isometry: impl Into<Isometry3d>,
252
color: impl Into<Color>,
253
) -> Self::Output<'_> {
254
if !self.enabled {
255
return;
256
}
257
258
let isometry = isometry.into();
259
self.linestrip(
260
primitive.vertices.iter().map(|vec3| isometry * *vec3),
261
color,
262
);
263
}
264
}
265
266
// triangle 3d
267
268
impl<Config, Clear> GizmoPrimitive3d<Triangle3d> for GizmoBuffer<Config, Clear>
269
where
270
Config: GizmoConfigGroup,
271
Clear: 'static + Send + Sync,
272
{
273
type Output<'a>
274
= ()
275
where
276
Self: 'a;
277
278
fn primitive_3d(
279
&mut self,
280
primitive: &Triangle3d,
281
isometry: impl Into<Isometry3d>,
282
color: impl Into<Color>,
283
) -> Self::Output<'_> {
284
if !self.enabled {
285
return;
286
}
287
288
let isometry = isometry.into();
289
let [a, b, c] = primitive.vertices;
290
self.linestrip([a, b, c, a].map(|vec3| isometry * vec3), color);
291
}
292
}
293
294
// cuboid
295
296
impl<Config, Clear> GizmoPrimitive3d<Cuboid> for GizmoBuffer<Config, Clear>
297
where
298
Config: GizmoConfigGroup,
299
Clear: 'static + Send + Sync,
300
{
301
type Output<'a>
302
= ()
303
where
304
Self: 'a;
305
306
fn primitive_3d(
307
&mut self,
308
primitive: &Cuboid,
309
isometry: impl Into<Isometry3d>,
310
color: impl Into<Color>,
311
) -> Self::Output<'_> {
312
if !self.enabled {
313
return;
314
}
315
316
let isometry = isometry.into();
317
318
// transform the points from the reference unit cube to the cuboid coords
319
let vertices @ [a, b, c, d, e, f, g, h] = [
320
[1.0, 1.0, 1.0],
321
[-1.0, 1.0, 1.0],
322
[-1.0, -1.0, 1.0],
323
[1.0, -1.0, 1.0],
324
[1.0, 1.0, -1.0],
325
[-1.0, 1.0, -1.0],
326
[-1.0, -1.0, -1.0],
327
[1.0, -1.0, -1.0],
328
]
329
.map(Vec3::from)
330
.map(|vec3| vec3 * primitive.half_size)
331
.map(|vec3| isometry * vec3);
332
333
// lines for the upper rectangle of the cuboid
334
let upper = [a, b, c, d]
335
.into_iter()
336
.zip([a, b, c, d].into_iter().cycle().skip(1));
337
338
// lines for the lower rectangle of the cuboid
339
let lower = [e, f, g, h]
340
.into_iter()
341
.zip([e, f, g, h].into_iter().cycle().skip(1));
342
343
// lines connecting upper and lower rectangles of the cuboid
344
let connections = vertices.into_iter().zip(vertices.into_iter().skip(4));
345
346
let color = color.into();
347
upper
348
.chain(lower)
349
.chain(connections)
350
.for_each(|(start, end)| {
351
self.line(start, end, color);
352
});
353
}
354
}
355
356
// cylinder 3d
357
358
/// Builder for configuring the drawing options of [`Cylinder`].
359
pub struct Cylinder3dBuilder<'a, Config, Clear>
360
where
361
Config: GizmoConfigGroup,
362
Clear: 'static + Send + Sync,
363
{
364
gizmos: &'a mut GizmoBuffer<Config, Clear>,
365
366
// Radius of the cylinder
367
radius: f32,
368
// Half height of the cylinder
369
half_height: f32,
370
371
isometry: Isometry3d,
372
// Color of the cylinder
373
color: Color,
374
375
// Number of lines used to approximate the cylinder geometry
376
resolution: u32,
377
}
378
379
impl<Config, Clear> Cylinder3dBuilder<'_, Config, Clear>
380
where
381
Config: GizmoConfigGroup,
382
Clear: 'static + Send + Sync,
383
{
384
/// Set the number of lines used to approximate the top and bottom of the cylinder geometry.
385
pub fn resolution(mut self, resolution: u32) -> Self {
386
self.resolution = resolution;
387
self
388
}
389
}
390
391
impl<Config, Clear> GizmoPrimitive3d<Cylinder> for GizmoBuffer<Config, Clear>
392
where
393
Config: GizmoConfigGroup,
394
Clear: 'static + Send + Sync,
395
{
396
type Output<'a>
397
= Cylinder3dBuilder<'a, Config, Clear>
398
where
399
Self: 'a;
400
401
fn primitive_3d(
402
&mut self,
403
primitive: &Cylinder,
404
isometry: impl Into<Isometry3d>,
405
color: impl Into<Color>,
406
) -> Self::Output<'_> {
407
Cylinder3dBuilder {
408
gizmos: self,
409
radius: primitive.radius,
410
half_height: primitive.half_height,
411
isometry: isometry.into(),
412
color: color.into(),
413
resolution: DEFAULT_RESOLUTION,
414
}
415
}
416
}
417
418
impl<Config, Clear> Drop for Cylinder3dBuilder<'_, Config, Clear>
419
where
420
Config: GizmoConfigGroup,
421
Clear: 'static + Send + Sync,
422
{
423
fn drop(&mut self) {
424
if !self.gizmos.enabled {
425
return;
426
}
427
428
self.gizmos
429
.primitive_3d(
430
&ConicalFrustum {
431
radius_top: self.radius,
432
radius_bottom: self.radius,
433
height: self.half_height * 2.0,
434
},
435
self.isometry,
436
self.color,
437
)
438
.resolution(self.resolution);
439
}
440
}
441
442
// capsule 3d
443
444
/// Builder for configuring the drawing options of [`Capsule3d`].
445
pub struct Capsule3dBuilder<'a, Config, Clear>
446
where
447
Config: GizmoConfigGroup,
448
Clear: 'static + Send + Sync,
449
{
450
gizmos: &'a mut GizmoBuffer<Config, Clear>,
451
452
// Radius of the capsule
453
radius: f32,
454
// Half length of the capsule
455
half_length: f32,
456
457
isometry: Isometry3d,
458
// Color of the capsule
459
color: Color,
460
461
// Number of lines used to approximate the capsule geometry
462
resolution: u32,
463
}
464
465
impl<Config, Clear> Capsule3dBuilder<'_, Config, Clear>
466
where
467
Config: GizmoConfigGroup,
468
Clear: 'static + Send + Sync,
469
{
470
/// Set the number of lines used to approximate the capsule geometry.
471
pub fn resolution(mut self, resolution: u32) -> Self {
472
self.resolution = resolution;
473
self
474
}
475
}
476
477
impl<Config, Clear> GizmoPrimitive3d<Capsule3d> for GizmoBuffer<Config, Clear>
478
where
479
Config: GizmoConfigGroup,
480
Clear: 'static + Send + Sync,
481
{
482
type Output<'a>
483
= Capsule3dBuilder<'a, Config, Clear>
484
where
485
Self: 'a;
486
487
fn primitive_3d(
488
&mut self,
489
primitive: &Capsule3d,
490
isometry: impl Into<Isometry3d>,
491
color: impl Into<Color>,
492
) -> Self::Output<'_> {
493
Capsule3dBuilder {
494
gizmos: self,
495
radius: primitive.radius,
496
half_length: primitive.half_length,
497
isometry: isometry.into(),
498
color: color.into(),
499
resolution: DEFAULT_RESOLUTION,
500
}
501
}
502
}
503
504
impl<Config, Clear> Drop for Capsule3dBuilder<'_, Config, Clear>
505
where
506
Config: GizmoConfigGroup,
507
Clear: 'static + Send + Sync,
508
{
509
fn drop(&mut self) {
510
if !self.gizmos.enabled {
511
return;
512
}
513
514
let [upper_apex, lower_apex] = [-1.0, 1.0]
515
.map(|sign| Vec3::Y * sign * (self.half_length + self.radius))
516
.map(|vec3| self.isometry * vec3);
517
let [upper_center, lower_center] = [-1.0, 1.0]
518
.map(|sign| Vec3::Y * sign * self.half_length)
519
.map(|vec3| self.isometry * vec3);
520
let [upper_points, lower_points] = [-1.0, 1.0]
521
.map(|sign| Vec3::Y * sign * self.half_length)
522
.map(|vec3| {
523
circle_coordinates_closed(self.radius, self.resolution)
524
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + vec3)
525
.map(|vec3| self.isometry * vec3)
526
.collect::<Vec<_>>()
527
});
528
529
upper_points.iter().skip(1).copied().for_each(|start| {
530
self.gizmos
531
.short_arc_3d_between(upper_center, start, upper_apex, self.color);
532
});
533
lower_points.iter().skip(1).copied().for_each(|start| {
534
self.gizmos
535
.short_arc_3d_between(lower_center, start, lower_apex, self.color);
536
});
537
538
let circle_rotation = self
539
.isometry
540
.rotation
541
.mul_quat(Quat::from_rotation_x(core::f32::consts::FRAC_PI_2));
542
self.gizmos.circle(
543
Isometry3d::new(upper_center, circle_rotation),
544
self.radius,
545
self.color,
546
);
547
self.gizmos.circle(
548
Isometry3d::new(lower_center, circle_rotation),
549
self.radius,
550
self.color,
551
);
552
553
let connection_lines = upper_points.into_iter().zip(lower_points).skip(1);
554
connection_lines.for_each(|(start, end)| {
555
self.gizmos.line(start, end, self.color);
556
});
557
}
558
}
559
560
// cone 3d
561
562
/// Builder for configuring the drawing options of [`Cone`].
563
pub struct Cone3dBuilder<'a, Config, Clear>
564
where
565
Config: GizmoConfigGroup,
566
Clear: 'static + Send + Sync,
567
{
568
gizmos: &'a mut GizmoBuffer<Config, Clear>,
569
570
// Radius of the cone
571
radius: f32,
572
// Height of the cone
573
height: f32,
574
575
isometry: Isometry3d,
576
// Color of the cone
577
color: Color,
578
579
// Number of lines used to approximate the cone base geometry
580
base_resolution: u32,
581
582
// Number of lines used to approximate the cone height geometry
583
height_resolution: u32,
584
}
585
586
impl<Config, Clear> Cone3dBuilder<'_, Config, Clear>
587
where
588
Config: GizmoConfigGroup,
589
Clear: 'static + Send + Sync,
590
{
591
/// Set the number of lines used to approximate the cone geometry for its base and height.
592
pub fn resolution(mut self, resolution: u32) -> Self {
593
self.base_resolution = resolution;
594
self.height_resolution = resolution;
595
self
596
}
597
598
/// Set the number of lines used to approximate the height of the cone geometry.
599
///
600
/// `resolution` should be a multiple of the value passed to [`Self::height_resolution`]
601
/// for the height to connect properly with the base.
602
pub fn base_resolution(mut self, resolution: u32) -> Self {
603
self.base_resolution = resolution;
604
self
605
}
606
607
/// Set the number of lines used to approximate the height of the cone geometry.
608
///
609
/// `resolution` should be a divisor of the value passed to [`Self::base_resolution`]
610
/// for the height to connect properly with the base.
611
pub fn height_resolution(mut self, resolution: u32) -> Self {
612
self.height_resolution = resolution;
613
self
614
}
615
}
616
617
impl<Config, Clear> GizmoPrimitive3d<Cone> for GizmoBuffer<Config, Clear>
618
where
619
Config: GizmoConfigGroup,
620
Clear: 'static + Send + Sync,
621
{
622
type Output<'a>
623
= Cone3dBuilder<'a, Config, Clear>
624
where
625
Self: 'a;
626
627
fn primitive_3d(
628
&mut self,
629
primitive: &Cone,
630
isometry: impl Into<Isometry3d>,
631
color: impl Into<Color>,
632
) -> Self::Output<'_> {
633
Cone3dBuilder {
634
gizmos: self,
635
radius: primitive.radius,
636
height: primitive.height,
637
isometry: isometry.into(),
638
color: color.into(),
639
base_resolution: DEFAULT_RESOLUTION,
640
height_resolution: DEFAULT_RESOLUTION,
641
}
642
}
643
}
644
645
impl<Config, Clear> Drop for Cone3dBuilder<'_, Config, Clear>
646
where
647
Config: GizmoConfigGroup,
648
Clear: 'static + Send + Sync,
649
{
650
fn drop(&mut self) {
651
if !self.gizmos.enabled {
652
return;
653
}
654
655
let half_height = self.height * 0.5;
656
let apex = self.isometry * (Vec3::Y * half_height);
657
let circle_center = half_height * Vec3::NEG_Y;
658
let circle_coords = circle_coordinates_closed(self.radius, self.height_resolution)
659
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + circle_center)
660
.map(|vec3| self.isometry * vec3)
661
.collect::<Vec<_>>();
662
663
// connections to apex
664
circle_coords
665
.iter()
666
.skip(1)
667
.map(|vec3| (*vec3, apex))
668
.for_each(|(start, end)| {
669
self.gizmos.line(start, end, self.color);
670
});
671
672
// base circle
673
circle_coords
674
.windows(2)
675
.map(|win| (win[0], win[1]))
676
.for_each(|(start, end)| {
677
self.gizmos.line(start, end, self.color);
678
});
679
}
680
}
681
682
// conical frustum 3d
683
684
/// Builder for configuring the drawing options of [`ConicalFrustum`].
685
pub struct ConicalFrustum3dBuilder<'a, Config, Clear>
686
where
687
Config: GizmoConfigGroup,
688
Clear: 'static + Send + Sync,
689
{
690
gizmos: &'a mut GizmoBuffer<Config, Clear>,
691
692
// Radius of the top circle
693
radius_top: f32,
694
// Radius of the bottom circle
695
radius_bottom: f32,
696
// Height of the conical frustum
697
height: f32,
698
699
isometry: Isometry3d,
700
// Color of the conical frustum
701
color: Color,
702
703
// Number of lines used to approximate the curved surfaces
704
resolution: u32,
705
}
706
707
impl<Config, Clear> ConicalFrustum3dBuilder<'_, Config, Clear>
708
where
709
Config: GizmoConfigGroup,
710
Clear: 'static + Send + Sync,
711
{
712
/// Set the number of lines used to approximate the curved surfaces.
713
pub fn resolution(mut self, resolution: u32) -> Self {
714
self.resolution = resolution;
715
self
716
}
717
}
718
719
impl<Config, Clear> GizmoPrimitive3d<ConicalFrustum> for GizmoBuffer<Config, Clear>
720
where
721
Config: GizmoConfigGroup,
722
Clear: 'static + Send + Sync,
723
{
724
type Output<'a>
725
= ConicalFrustum3dBuilder<'a, Config, Clear>
726
where
727
Self: 'a;
728
729
fn primitive_3d(
730
&mut self,
731
primitive: &ConicalFrustum,
732
isometry: impl Into<Isometry3d>,
733
color: impl Into<Color>,
734
) -> Self::Output<'_> {
735
ConicalFrustum3dBuilder {
736
gizmos: self,
737
radius_top: primitive.radius_top,
738
radius_bottom: primitive.radius_bottom,
739
height: primitive.height,
740
isometry: isometry.into(),
741
color: color.into(),
742
resolution: DEFAULT_RESOLUTION,
743
}
744
}
745
}
746
747
impl<Config, Clear> Drop for ConicalFrustum3dBuilder<'_, Config, Clear>
748
where
749
Config: GizmoConfigGroup,
750
Clear: 'static + Send + Sync,
751
{
752
fn drop(&mut self) {
753
if !self.gizmos.enabled {
754
return;
755
}
756
757
let half_height = self.height * 0.5;
758
let [upper_points, lower_points] = [(-1.0, self.radius_bottom), (1.0, self.radius_top)]
759
.map(|(sign, radius)| {
760
let translation = Vec3::Y * sign * half_height;
761
circle_coordinates_closed(radius, self.resolution)
762
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation)
763
.map(|vec3| self.isometry * vec3)
764
.collect::<Vec<_>>()
765
});
766
767
let upper_lines = upper_points.windows(2).map(|win| (win[0], win[1]));
768
let lower_lines = lower_points.windows(2).map(|win| (win[0], win[1]));
769
upper_lines.chain(lower_lines).for_each(|(start, end)| {
770
self.gizmos.line(start, end, self.color);
771
});
772
773
let connection_lines = upper_points.into_iter().zip(lower_points).skip(1);
774
connection_lines.for_each(|(start, end)| {
775
self.gizmos.line(start, end, self.color);
776
});
777
}
778
}
779
780
// torus 3d
781
782
/// Builder for configuring the drawing options of [`Torus`].
783
pub struct Torus3dBuilder<'a, Config, Clear>
784
where
785
Config: GizmoConfigGroup,
786
Clear: 'static + Send + Sync,
787
{
788
gizmos: &'a mut GizmoBuffer<Config, Clear>,
789
790
// Radius of the minor circle (tube)
791
minor_radius: f32,
792
// Radius of the major circle (ring)
793
major_radius: f32,
794
795
isometry: Isometry3d,
796
// Color of the torus
797
color: Color,
798
799
// Number of lines in the minor (tube) direction
800
minor_resolution: u32,
801
// Number of lines in the major (ring) direction
802
major_resolution: u32,
803
}
804
805
impl<Config, Clear> Torus3dBuilder<'_, Config, Clear>
806
where
807
Config: GizmoConfigGroup,
808
Clear: 'static + Send + Sync,
809
{
810
/// Set the number of lines in the minor (tube) direction.
811
pub fn minor_resolution(mut self, minor_resolution: u32) -> Self {
812
self.minor_resolution = minor_resolution;
813
self
814
}
815
816
/// Set the number of lines in the major (ring) direction.
817
pub fn major_resolution(mut self, major_resolution: u32) -> Self {
818
self.major_resolution = major_resolution;
819
self
820
}
821
}
822
823
impl<Config, Clear> GizmoPrimitive3d<Torus> for GizmoBuffer<Config, Clear>
824
where
825
Config: GizmoConfigGroup,
826
Clear: 'static + Send + Sync,
827
{
828
type Output<'a>
829
= Torus3dBuilder<'a, Config, Clear>
830
where
831
Self: 'a;
832
833
fn primitive_3d(
834
&mut self,
835
primitive: &Torus,
836
isometry: impl Into<Isometry3d>,
837
color: impl Into<Color>,
838
) -> Self::Output<'_> {
839
Torus3dBuilder {
840
gizmos: self,
841
minor_radius: primitive.minor_radius,
842
major_radius: primitive.major_radius,
843
isometry: isometry.into(),
844
color: color.into(),
845
minor_resolution: DEFAULT_RESOLUTION,
846
major_resolution: DEFAULT_RESOLUTION,
847
}
848
}
849
}
850
851
impl<Config, Clear> Drop for Torus3dBuilder<'_, Config, Clear>
852
where
853
Config: GizmoConfigGroup,
854
Clear: 'static + Send + Sync,
855
{
856
fn drop(&mut self) {
857
if !self.gizmos.enabled {
858
return;
859
}
860
861
// draw 4 circles with major_radius
862
let [inner, outer, top, bottom] = [
863
(self.major_radius - self.minor_radius, 0.0),
864
(self.major_radius + self.minor_radius, 0.0),
865
(self.major_radius, self.minor_radius),
866
(self.major_radius, -self.minor_radius),
867
]
868
.map(|(radius, height)| {
869
let translation = height * Vec3::Y;
870
circle_coordinates_closed(radius, self.major_resolution)
871
.map(|vec2| Vec3::new(vec2.x, 0.0, vec2.y) + translation)
872
.map(|vec3| self.isometry * vec3)
873
.collect::<Vec<_>>()
874
});
875
876
[&inner, &outer, &top, &bottom]
877
.iter()
878
.flat_map(|points| points.windows(2).map(|win| (win[0], win[1])))
879
.for_each(|(start, end)| {
880
self.gizmos.line(start, end, self.color);
881
});
882
883
inner
884
.into_iter()
885
.zip(top)
886
.zip(outer)
887
.zip(bottom)
888
.flat_map(|(((inner, top), outer), bottom)| {
889
let center = (inner + top + outer + bottom) * 0.25;
890
[(inner, top), (top, outer), (outer, bottom), (bottom, inner)]
891
.map(|(start, end)| (start, end, center))
892
})
893
.for_each(|(from, to, center)| {
894
self.gizmos
895
.short_arc_3d_between(center, from, to, self.color)
896
.resolution(self.minor_resolution);
897
});
898
}
899
}
900
901
// tetrahedron
902
903
impl<Config, Clear> GizmoPrimitive3d<Tetrahedron> for GizmoBuffer<Config, Clear>
904
where
905
Config: GizmoConfigGroup,
906
Clear: 'static + Send + Sync,
907
{
908
type Output<'a>
909
= ()
910
where
911
Self: 'a;
912
913
fn primitive_3d(
914
&mut self,
915
primitive: &Tetrahedron,
916
isometry: impl Into<Isometry3d>,
917
color: impl Into<Color>,
918
) -> Self::Output<'_> {
919
if !self.enabled {
920
return;
921
}
922
923
let isometry = isometry.into();
924
925
let [a, b, c, d] = primitive.vertices.map(|vec3| isometry * vec3);
926
927
let lines = [(a, b), (a, c), (a, d), (b, c), (b, d), (c, d)];
928
929
let color = color.into();
930
lines.into_iter().for_each(|(start, end)| {
931
self.line(start, end, color);
932
});
933
}
934
}
935
936