render_primitives/
render_primitives.rs

1//! This example demonstrates how each of Bevy's math primitives look like in 2D and 3D with meshes
2//! and with gizmos
3
4use bevy::{input::common_conditions::input_just_pressed, math::Isometry2d, prelude::*};
5
6const LEFT_RIGHT_OFFSET_2D: f32 = 200.0;
7const LEFT_RIGHT_OFFSET_3D: f32 = 2.0;
8
9fn main() {
10    let mut app = App::new();
11
12    app.add_plugins(DefaultPlugins)
13        .init_state::<PrimitiveSelected>()
14        .init_state::<CameraActive>();
15
16    // cameras
17    app.add_systems(Startup, (setup_cameras, setup_lights, setup_ambient_light))
18        .add_systems(
19            Update,
20            (
21                update_active_cameras.run_if(state_changed::<CameraActive>),
22                switch_cameras.run_if(input_just_pressed(KeyCode::KeyC)),
23            ),
24        );
25
26    // text
27
28    // PostStartup since we need the cameras to exist
29    app.add_systems(PostStartup, setup_text);
30    app.add_systems(
31        Update,
32        (update_text.run_if(state_changed::<PrimitiveSelected>),),
33    );
34
35    // primitives
36    app.add_systems(Startup, (spawn_primitive_2d, spawn_primitive_3d))
37        .add_systems(
38            Update,
39            (
40                switch_to_next_primitive.run_if(input_just_pressed(KeyCode::ArrowUp)),
41                switch_to_previous_primitive.run_if(input_just_pressed(KeyCode::ArrowDown)),
42                draw_gizmos_2d.run_if(in_mode(CameraActive::Dim2)),
43                draw_gizmos_3d.run_if(in_mode(CameraActive::Dim3)),
44                update_primitive_meshes
45                    .run_if(state_changed::<PrimitiveSelected>.or(state_changed::<CameraActive>)),
46                rotate_primitive_2d_meshes,
47                rotate_primitive_3d_meshes,
48            ),
49        );
50
51    app.run();
52}
53
54/// State for tracking which of the two cameras (2D & 3D) is currently active
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default, Reflect)]
56enum CameraActive {
57    #[default]
58    /// 2D Camera is active
59    Dim2,
60    /// 3D Camera is active
61    Dim3,
62}
63
64/// State for tracking which primitives are currently displayed
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default, Reflect)]
66enum PrimitiveSelected {
67    #[default]
68    RectangleAndCuboid,
69    CircleAndSphere,
70    Ellipse,
71    Triangle,
72    Plane,
73    Line,
74    Segment,
75    Polyline,
76    Polygon,
77    RegularPolygon,
78    Capsule,
79    Cylinder,
80    Cone,
81    ConicalFrustum,
82    Torus,
83    Tetrahedron,
84    Arc,
85    CircularSector,
86    CircularSegment,
87}
88
89impl std::fmt::Display for PrimitiveSelected {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        let name = match self {
92            PrimitiveSelected::RectangleAndCuboid => String::from("Rectangle/Cuboid"),
93            PrimitiveSelected::CircleAndSphere => String::from("Circle/Sphere"),
94            other => format!("{other:?}"),
95        };
96        write!(f, "{name}")
97    }
98}
99
100impl PrimitiveSelected {
101    const ALL: [Self; 19] = [
102        Self::RectangleAndCuboid,
103        Self::CircleAndSphere,
104        Self::Ellipse,
105        Self::Triangle,
106        Self::Plane,
107        Self::Line,
108        Self::Segment,
109        Self::Polyline,
110        Self::Polygon,
111        Self::RegularPolygon,
112        Self::Capsule,
113        Self::Cylinder,
114        Self::Cone,
115        Self::ConicalFrustum,
116        Self::Torus,
117        Self::Tetrahedron,
118        Self::Arc,
119        Self::CircularSector,
120        Self::CircularSegment,
121    ];
122
123    fn next(self) -> Self {
124        Self::ALL
125            .into_iter()
126            .cycle()
127            .skip_while(|&x| x != self)
128            .nth(1)
129            .unwrap()
130    }
131
132    fn previous(self) -> Self {
133        Self::ALL
134            .into_iter()
135            .rev()
136            .cycle()
137            .skip_while(|&x| x != self)
138            .nth(1)
139            .unwrap()
140    }
141}
142
143const SMALL_2D: f32 = 50.0;
144const BIG_2D: f32 = 100.0;
145
146const SMALL_3D: f32 = 0.5;
147const BIG_3D: f32 = 1.0;
148
149// primitives
150const RECTANGLE: Rectangle = Rectangle {
151    half_size: Vec2::new(SMALL_2D, BIG_2D),
152};
153const CUBOID: Cuboid = Cuboid {
154    half_size: Vec3::new(BIG_3D, SMALL_3D, BIG_3D),
155};
156
157const CIRCLE: Circle = Circle { radius: BIG_2D };
158const SPHERE: Sphere = Sphere { radius: BIG_3D };
159
160const ELLIPSE: Ellipse = Ellipse {
161    half_size: Vec2::new(BIG_2D, SMALL_2D),
162};
163
164const TRIANGLE_2D: Triangle2d = Triangle2d {
165    vertices: [
166        Vec2::new(BIG_2D, 0.0),
167        Vec2::new(0.0, BIG_2D),
168        Vec2::new(-BIG_2D, 0.0),
169    ],
170};
171
172const TRIANGLE_3D: Triangle3d = Triangle3d {
173    vertices: [
174        Vec3::new(BIG_3D, 0.0, 0.0),
175        Vec3::new(0.0, BIG_3D, 0.0),
176        Vec3::new(-BIG_3D, 0.0, 0.0),
177    ],
178};
179
180const PLANE_2D: Plane2d = Plane2d { normal: Dir2::Y };
181const PLANE_3D: Plane3d = Plane3d {
182    normal: Dir3::Y,
183    half_size: Vec2::new(BIG_3D, BIG_3D),
184};
185
186const LINE2D: Line2d = Line2d { direction: Dir2::X };
187const LINE3D: Line3d = Line3d { direction: Dir3::X };
188
189const SEGMENT_2D: Segment2d = Segment2d {
190    vertices: [Vec2::new(-BIG_2D / 2., 0.), Vec2::new(BIG_2D / 2., 0.)],
191};
192
193const SEGMENT_3D: Segment3d = Segment3d {
194    vertices: [
195        Vec3::new(-BIG_3D / 2., 0., 0.),
196        Vec3::new(BIG_3D / 2., 0., 0.),
197    ],
198};
199
200const POLYLINE_2D: Polyline2d<4> = Polyline2d {
201    vertices: [
202        Vec2::new(-BIG_2D, -SMALL_2D),
203        Vec2::new(-SMALL_2D, SMALL_2D),
204        Vec2::new(SMALL_2D, -SMALL_2D),
205        Vec2::new(BIG_2D, SMALL_2D),
206    ],
207};
208const POLYLINE_3D: Polyline3d<4> = Polyline3d {
209    vertices: [
210        Vec3::new(-BIG_3D, -SMALL_3D, -SMALL_3D),
211        Vec3::new(SMALL_3D, SMALL_3D, 0.0),
212        Vec3::new(-SMALL_3D, -SMALL_3D, 0.0),
213        Vec3::new(BIG_3D, SMALL_3D, SMALL_3D),
214    ],
215};
216
217const POLYGON_2D: Polygon<5> = Polygon {
218    vertices: [
219        Vec2::new(-BIG_2D, -SMALL_2D),
220        Vec2::new(BIG_2D, -SMALL_2D),
221        Vec2::new(BIG_2D, SMALL_2D),
222        Vec2::new(0.0, 0.0),
223        Vec2::new(-BIG_2D, SMALL_2D),
224    ],
225};
226
227const REGULAR_POLYGON: RegularPolygon = RegularPolygon {
228    circumcircle: Circle { radius: BIG_2D },
229    sides: 5,
230};
231
232const CAPSULE_2D: Capsule2d = Capsule2d {
233    radius: SMALL_2D,
234    half_length: SMALL_2D,
235};
236const CAPSULE_3D: Capsule3d = Capsule3d {
237    radius: SMALL_3D,
238    half_length: SMALL_3D,
239};
240
241const CYLINDER: Cylinder = Cylinder {
242    radius: SMALL_3D,
243    half_height: SMALL_3D,
244};
245
246const CONE: Cone = Cone {
247    radius: BIG_3D,
248    height: BIG_3D,
249};
250
251const CONICAL_FRUSTUM: ConicalFrustum = ConicalFrustum {
252    radius_top: BIG_3D,
253    radius_bottom: SMALL_3D,
254    height: BIG_3D,
255};
256
257const ANNULUS: Annulus = Annulus {
258    inner_circle: Circle { radius: SMALL_2D },
259    outer_circle: Circle { radius: BIG_2D },
260};
261
262const TORUS: Torus = Torus {
263    minor_radius: SMALL_3D / 2.0,
264    major_radius: SMALL_3D * 1.5,
265};
266
267const TETRAHEDRON: Tetrahedron = Tetrahedron {
268    vertices: [
269        Vec3::new(-BIG_3D, 0.0, 0.0),
270        Vec3::new(BIG_3D, 0.0, 0.0),
271        Vec3::new(0.0, 0.0, -BIG_3D * 1.67),
272        Vec3::new(0.0, BIG_3D * 1.67, -BIG_3D * 0.5),
273    ],
274};
275
276const ARC: Arc2d = Arc2d {
277    radius: BIG_2D,
278    half_angle: std::f32::consts::FRAC_PI_4,
279};
280
281const CIRCULAR_SECTOR: CircularSector = CircularSector {
282    arc: Arc2d {
283        radius: BIG_2D,
284        half_angle: std::f32::consts::FRAC_PI_4,
285    },
286};
287
288const CIRCULAR_SEGMENT: CircularSegment = CircularSegment {
289    arc: Arc2d {
290        radius: BIG_2D,
291        half_angle: std::f32::consts::FRAC_PI_4,
292    },
293};
294
295fn setup_cameras(mut commands: Commands) {
296    let start_in_2d = true;
297    let make_camera = |is_active| Camera {
298        is_active,
299        ..Default::default()
300    };
301
302    commands.spawn((Camera2d, make_camera(start_in_2d)));
303
304    commands.spawn((
305        Camera3d::default(),
306        make_camera(!start_in_2d),
307        Transform::from_xyz(0.0, 10.0, 0.0).looking_at(Vec3::ZERO, Vec3::Z),
308    ));
309}
310
311fn setup_ambient_light(mut ambient_light: ResMut<AmbientLight>) {
312    ambient_light.brightness = 50.0;
313}
314
315fn setup_lights(mut commands: Commands) {
316    commands.spawn((
317        PointLight {
318            intensity: 5000.0,
319            ..default()
320        },
321        Transform::from_translation(Vec3::new(-LEFT_RIGHT_OFFSET_3D, 2.0, 0.0))
322            .looking_at(Vec3::new(-LEFT_RIGHT_OFFSET_3D, 0.0, 0.0), Vec3::Y),
323    ));
324}
325
326/// Marker component for header text
327#[derive(Debug, Clone, Component, Default, Reflect)]
328pub struct HeaderText;
329
330/// Marker component for header node
331#[derive(Debug, Clone, Component, Default, Reflect)]
332pub struct HeaderNode;
333
334fn update_active_cameras(
335    state: Res<State<CameraActive>>,
336    camera_2d: Single<(Entity, &mut Camera), With<Camera2d>>,
337    camera_3d: Single<(Entity, &mut Camera), (With<Camera3d>, Without<Camera2d>)>,
338    mut text: Query<&mut UiTargetCamera, With<HeaderNode>>,
339) {
340    let (entity_2d, mut cam_2d) = camera_2d.into_inner();
341    let (entity_3d, mut cam_3d) = camera_3d.into_inner();
342    let is_camera_2d_active = matches!(*state.get(), CameraActive::Dim2);
343
344    cam_2d.is_active = is_camera_2d_active;
345    cam_3d.is_active = !is_camera_2d_active;
346
347    let active_camera = if is_camera_2d_active {
348        entity_2d
349    } else {
350        entity_3d
351    };
352
353    text.iter_mut().for_each(|mut target_camera| {
354        *target_camera = UiTargetCamera(active_camera);
355    });
356}
357
358fn switch_cameras(current: Res<State<CameraActive>>, mut next: ResMut<NextState<CameraActive>>) {
359    let next_state = match current.get() {
360        CameraActive::Dim2 => CameraActive::Dim3,
361        CameraActive::Dim3 => CameraActive::Dim2,
362    };
363    next.set(next_state);
364}
365
366fn setup_text(mut commands: Commands, cameras: Query<(Entity, &Camera)>) {
367    let active_camera = cameras
368        .iter()
369        .find_map(|(entity, camera)| camera.is_active.then_some(entity))
370        .expect("run condition ensures existence");
371    commands.spawn((
372        HeaderNode,
373        Node {
374            justify_self: JustifySelf::Center,
375            top: Val::Px(5.0),
376            ..Default::default()
377        },
378        UiTargetCamera(active_camera),
379        children![(
380            Text::default(),
381            HeaderText,
382            TextLayout::new_with_justify(JustifyText::Center),
383            children![
384                TextSpan::new("Primitive: "),
385                TextSpan(format!("{text}", text = PrimitiveSelected::default())),
386                TextSpan::new("\n\n"),
387                TextSpan::new(
388                    "Press 'C' to switch between 2D and 3D mode\n\
389                    Press 'Up' or 'Down' to switch to the next/previous primitive",
390                ),
391                TextSpan::new("\n\n"),
392                TextSpan::new("(If nothing is displayed, there's no rendering support yet)",),
393            ]
394        )],
395    ));
396}
397
398fn update_text(
399    primitive_state: Res<State<PrimitiveSelected>>,
400    header: Query<Entity, With<HeaderText>>,
401    mut writer: TextUiWriter,
402) {
403    let new_text = format!("{text}", text = primitive_state.get());
404    header.iter().for_each(|header_text| {
405        if let Some(mut text) = writer.get_text(header_text, 2) {
406            (*text).clone_from(&new_text);
407        };
408    });
409}
410
411fn switch_to_next_primitive(
412    current: Res<State<PrimitiveSelected>>,
413    mut next: ResMut<NextState<PrimitiveSelected>>,
414) {
415    let next_state = current.get().next();
416    next.set(next_state);
417}
418
419fn switch_to_previous_primitive(
420    current: Res<State<PrimitiveSelected>>,
421    mut next: ResMut<NextState<PrimitiveSelected>>,
422) {
423    let next_state = current.get().previous();
424    next.set(next_state);
425}
426
427fn in_mode(active: CameraActive) -> impl Fn(Res<State<CameraActive>>) -> bool {
428    move |state| *state.get() == active
429}
430
431fn draw_gizmos_2d(mut gizmos: Gizmos, state: Res<State<PrimitiveSelected>>, time: Res<Time>) {
432    const POSITION: Vec2 = Vec2::new(-LEFT_RIGHT_OFFSET_2D, 0.0);
433    let angle = time.elapsed_secs();
434    let isometry = Isometry2d::new(POSITION, Rot2::radians(angle));
435    let color = Color::WHITE;
436
437    #[expect(
438        clippy::match_same_arms,
439        reason = "Certain primitives don't have any 2D rendering support yet."
440    )]
441    match state.get() {
442        PrimitiveSelected::RectangleAndCuboid => {
443            gizmos.primitive_2d(&RECTANGLE, isometry, color);
444        }
445        PrimitiveSelected::CircleAndSphere => {
446            gizmos.primitive_2d(&CIRCLE, isometry, color);
447        }
448        PrimitiveSelected::Ellipse => drop(gizmos.primitive_2d(&ELLIPSE, isometry, color)),
449        PrimitiveSelected::Triangle => gizmos.primitive_2d(&TRIANGLE_2D, isometry, color),
450        PrimitiveSelected::Plane => gizmos.primitive_2d(&PLANE_2D, isometry, color),
451        PrimitiveSelected::Line => drop(gizmos.primitive_2d(&LINE2D, isometry, color)),
452        PrimitiveSelected::Segment => {
453            drop(gizmos.primitive_2d(&SEGMENT_2D, isometry, color));
454        }
455        PrimitiveSelected::Polyline => gizmos.primitive_2d(&POLYLINE_2D, isometry, color),
456        PrimitiveSelected::Polygon => gizmos.primitive_2d(&POLYGON_2D, isometry, color),
457        PrimitiveSelected::RegularPolygon => {
458            gizmos.primitive_2d(&REGULAR_POLYGON, isometry, color);
459        }
460        PrimitiveSelected::Capsule => gizmos.primitive_2d(&CAPSULE_2D, isometry, color),
461        PrimitiveSelected::Cylinder => {}
462        PrimitiveSelected::Cone => {}
463        PrimitiveSelected::ConicalFrustum => {}
464        PrimitiveSelected::Torus => drop(gizmos.primitive_2d(&ANNULUS, isometry, color)),
465        PrimitiveSelected::Tetrahedron => {}
466        PrimitiveSelected::Arc => gizmos.primitive_2d(&ARC, isometry, color),
467        PrimitiveSelected::CircularSector => {
468            gizmos.primitive_2d(&CIRCULAR_SECTOR, isometry, color);
469        }
470        PrimitiveSelected::CircularSegment => {
471            gizmos.primitive_2d(&CIRCULAR_SEGMENT, isometry, color);
472        }
473    }
474}
475
476/// Marker for primitive meshes to record in which state they should be visible in
477#[derive(Debug, Clone, Component, Default, Reflect)]
478pub struct PrimitiveData {
479    camera_mode: CameraActive,
480    primitive_state: PrimitiveSelected,
481}
482
483/// Marker for meshes of 2D primitives
484#[derive(Debug, Clone, Component, Default)]
485pub struct MeshDim2;
486
487/// Marker for meshes of 3D primitives
488#[derive(Debug, Clone, Component, Default)]
489pub struct MeshDim3;
490
491fn spawn_primitive_2d(
492    mut commands: Commands,
493    mut materials: ResMut<Assets<ColorMaterial>>,
494    mut meshes: ResMut<Assets<Mesh>>,
495) {
496    const POSITION: Vec3 = Vec3::new(LEFT_RIGHT_OFFSET_2D, 0.0, 0.0);
497    let material: Handle<ColorMaterial> = materials.add(Color::WHITE);
498    let camera_mode = CameraActive::Dim2;
499    [
500        Some(RECTANGLE.mesh().build()),
501        Some(CIRCLE.mesh().build()),
502        Some(ELLIPSE.mesh().build()),
503        Some(TRIANGLE_2D.mesh().build()),
504        None, // plane
505        None, // line
506        None, // segment
507        None, // polyline
508        None, // polygon
509        Some(REGULAR_POLYGON.mesh().build()),
510        Some(CAPSULE_2D.mesh().build()),
511        None, // cylinder
512        None, // cone
513        None, // conical frustum
514        Some(ANNULUS.mesh().build()),
515        None, // tetrahedron
516    ]
517    .into_iter()
518    .zip(PrimitiveSelected::ALL)
519    .for_each(|(maybe_mesh, state)| {
520        if let Some(mesh) = maybe_mesh {
521            commands.spawn((
522                MeshDim2,
523                PrimitiveData {
524                    camera_mode,
525                    primitive_state: state,
526                },
527                Mesh2d(meshes.add(mesh)),
528                MeshMaterial2d(material.clone()),
529                Transform::from_translation(POSITION),
530            ));
531        }
532    });
533}
534
535fn spawn_primitive_3d(
536    mut commands: Commands,
537    mut materials: ResMut<Assets<StandardMaterial>>,
538    mut meshes: ResMut<Assets<Mesh>>,
539) {
540    const POSITION: Vec3 = Vec3::new(-LEFT_RIGHT_OFFSET_3D, 0.0, 0.0);
541    let material: Handle<StandardMaterial> = materials.add(Color::WHITE);
542    let camera_mode = CameraActive::Dim3;
543    [
544        Some(CUBOID.mesh().build()),
545        Some(SPHERE.mesh().build()),
546        None, // ellipse
547        Some(TRIANGLE_3D.mesh().build()),
548        Some(PLANE_3D.mesh().build()),
549        None, // line
550        None, // segment
551        None, // polyline
552        None, // polygon
553        None, // regular polygon
554        Some(CAPSULE_3D.mesh().build()),
555        Some(CYLINDER.mesh().build()),
556        None, // cone
557        None, // conical frustum
558        Some(TORUS.mesh().build()),
559        Some(TETRAHEDRON.mesh().build()),
560    ]
561    .into_iter()
562    .zip(PrimitiveSelected::ALL)
563    .for_each(|(maybe_mesh, state)| {
564        if let Some(mesh) = maybe_mesh {
565            commands.spawn((
566                MeshDim3,
567                PrimitiveData {
568                    camera_mode,
569                    primitive_state: state,
570                },
571                Mesh3d(meshes.add(mesh)),
572                MeshMaterial3d(material.clone()),
573                Transform::from_translation(POSITION),
574            ));
575        }
576    });
577}
578
579fn update_primitive_meshes(
580    camera_state: Res<State<CameraActive>>,
581    primitive_state: Res<State<PrimitiveSelected>>,
582    mut primitives: Query<(&mut Visibility, &PrimitiveData)>,
583) {
584    primitives.iter_mut().for_each(|(mut vis, primitive)| {
585        let visible = primitive.camera_mode == *camera_state.get()
586            && primitive.primitive_state == *primitive_state.get();
587        *vis = if visible {
588            Visibility::Inherited
589        } else {
590            Visibility::Hidden
591        };
592    });
593}
594
595fn rotate_primitive_2d_meshes(
596    mut primitives_2d: Query<
597        (&mut Transform, &ViewVisibility),
598        (With<PrimitiveData>, With<MeshDim2>),
599    >,
600    time: Res<Time>,
601) {
602    let rotation_2d = Quat::from_mat3(&Mat3::from_angle(time.elapsed_secs()));
603    primitives_2d
604        .iter_mut()
605        .filter(|(_, vis)| vis.get())
606        .for_each(|(mut transform, _)| {
607            transform.rotation = rotation_2d;
608        });
609}
610
611fn rotate_primitive_3d_meshes(
612    mut primitives_3d: Query<
613        (&mut Transform, &ViewVisibility),
614        (With<PrimitiveData>, With<MeshDim3>),
615    >,
616    time: Res<Time>,
617) {
618    let rotation_3d = Quat::from_rotation_arc(
619        Vec3::Z,
620        Vec3::new(
621            ops::sin(time.elapsed_secs()),
622            ops::cos(time.elapsed_secs()),
623            ops::sin(time.elapsed_secs()) * 0.5,
624        )
625        .try_normalize()
626        .unwrap_or(Vec3::Z),
627    );
628    primitives_3d
629        .iter_mut()
630        .filter(|(_, vis)| vis.get())
631        .for_each(|(mut transform, _)| {
632            transform.rotation = rotation_3d;
633        });
634}
635
636fn draw_gizmos_3d(mut gizmos: Gizmos, state: Res<State<PrimitiveSelected>>, time: Res<Time>) {
637    const POSITION: Vec3 = Vec3::new(LEFT_RIGHT_OFFSET_3D, 0.0, 0.0);
638    let rotation = Quat::from_rotation_arc(
639        Vec3::Z,
640        Vec3::new(
641            ops::sin(time.elapsed_secs()),
642            ops::cos(time.elapsed_secs()),
643            ops::sin(time.elapsed_secs()) * 0.5,
644        )
645        .try_normalize()
646        .unwrap_or(Vec3::Z),
647    );
648    let isometry = Isometry3d::new(POSITION, rotation);
649    let color = Color::WHITE;
650    let resolution = 10;
651
652    #[expect(
653        clippy::match_same_arms,
654        reason = "Certain primitives don't have any 3D rendering support yet."
655    )]
656    match state.get() {
657        PrimitiveSelected::RectangleAndCuboid => {
658            gizmos.primitive_3d(&CUBOID, isometry, color);
659        }
660        PrimitiveSelected::CircleAndSphere => drop(
661            gizmos
662                .primitive_3d(&SPHERE, isometry, color)
663                .resolution(resolution),
664        ),
665        PrimitiveSelected::Ellipse => {}
666        PrimitiveSelected::Triangle => gizmos.primitive_3d(&TRIANGLE_3D, isometry, color),
667        PrimitiveSelected::Plane => drop(gizmos.primitive_3d(&PLANE_3D, isometry, color)),
668        PrimitiveSelected::Line => gizmos.primitive_3d(&LINE3D, isometry, color),
669        PrimitiveSelected::Segment => gizmos.primitive_3d(&SEGMENT_3D, isometry, color),
670        PrimitiveSelected::Polyline => gizmos.primitive_3d(&POLYLINE_3D, isometry, color),
671        PrimitiveSelected::Polygon => {}
672        PrimitiveSelected::RegularPolygon => {}
673        PrimitiveSelected::Capsule => drop(
674            gizmos
675                .primitive_3d(&CAPSULE_3D, isometry, color)
676                .resolution(resolution),
677        ),
678        PrimitiveSelected::Cylinder => drop(
679            gizmos
680                .primitive_3d(&CYLINDER, isometry, color)
681                .resolution(resolution),
682        ),
683        PrimitiveSelected::Cone => drop(
684            gizmos
685                .primitive_3d(&CONE, isometry, color)
686                .resolution(resolution),
687        ),
688        PrimitiveSelected::ConicalFrustum => {
689            gizmos.primitive_3d(&CONICAL_FRUSTUM, isometry, color);
690        }
691
692        PrimitiveSelected::Torus => drop(
693            gizmos
694                .primitive_3d(&TORUS, isometry, color)
695                .minor_resolution(resolution)
696                .major_resolution(resolution),
697        ),
698        PrimitiveSelected::Tetrahedron => {
699            gizmos.primitive_3d(&TETRAHEDRON, isometry, color);
700        }
701
702        PrimitiveSelected::Arc => {}
703        PrimitiveSelected::CircularSector => {}
704        PrimitiveSelected::CircularSegment => {}
705    }
706}