1use 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 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 app.add_systems(PostStartup, setup_text);
30 app.add_systems(
31 Update,
32 (update_text.run_if(state_changed::<PrimitiveSelected>),),
33 );
34
35 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default, Reflect)]
56enum CameraActive {
57 #[default]
58 Dim2,
60 Dim3,
62}
63
64#[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
149const 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#[derive(Debug, Clone, Component, Default, Reflect)]
328pub struct HeaderText;
329
330#[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(®ULAR_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#[derive(Debug, Clone, Component, Default, Reflect)]
478pub struct PrimitiveData {
479 camera_mode: CameraActive,
480 primitive_state: PrimitiveSelected,
481}
482
483#[derive(Debug, Clone, Component, Default)]
485pub struct MeshDim2;
486
487#[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, None, None, None, None, Some(REGULAR_POLYGON.mesh().build()),
510 Some(CAPSULE_2D.mesh().build()),
511 None, None, None, Some(ANNULUS.mesh().build()),
515 None, ]
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, Some(TRIANGLE_3D.mesh().build()),
548 Some(PLANE_3D.mesh().build()),
549 None, None, None, None, None, Some(CAPSULE_3D.mesh().build()),
555 Some(CYLINDER.mesh().build()),
556 None, None, 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}