polytope_playground/
gimbal.rs1use glam::Vec3;
2use loam::math::{Rotor, Rotor4};
3use loam::render::gizmo::{Handle, HandleDrag, HandleId};
4use loam::render::hypergimbal::{Hypergimbal, RingStyle};
5use loam::runtime::{ImageRay, SegmentRecord};
6use loam::shape::LineMesh;
7
8const SCALE: f32 = 0.55;
9
10const PICK_TOLERANCE: f32 = 0.09;
11
12const HIGHLIGHT: [f32; 4] = [1.0, 0.94, 0.55, 1.0];
13
14const RING_WIDTH_PX: f32 = 1.8;
15
16#[derive(Clone, Copy, Default)]
17pub(crate) struct Gimbal {
18 pub(crate) enabled: bool,
19 drag: Option<HandleDrag>,
20 applied: Rotor4,
21 hover: Option<HandleId>,
22}
23
24#[derive(Default)]
25pub(crate) struct GimbalRenderer {
26 mesh: LineMesh<3>,
27 segments: Vec<SegmentRecord>,
28}
29
30pub(crate) fn widget(center: Vec3) -> Hypergimbal {
31 Hypergimbal {
32 center,
33 scale: SCALE,
34 }
35}
36
37impl Gimbal {
38 pub(crate) fn press(&mut self, ray: &ImageRay, center: Vec3) -> bool {
39 let (origin, direction) = (Vec3::from(ray.origin), Vec3::from(ray.direction));
40 self.drag = widget(center)
41 .pick(origin, direction, PICK_TOLERANCE)
42 .and_then(|ring| HandleDrag::press(Handle::Rotate(ring), origin, direction));
43 self.applied = Rotor4::IDENTITY;
44 self.hover = self.drag.map(|drag| drag.id());
45 self.drag.is_some()
46 }
47
48 pub(crate) fn turn(&mut self, ray: &ImageRay) -> Option<Rotor4> {
49 let drag = self.drag?;
50 let total = drag
51 .delta(Vec3::from(ray.origin), Vec3::from(ray.direction))?
52 .rotor();
53 let step = total * self.applied.inverse();
54 self.applied = total;
55 Some(step.normalize())
56 }
57
58 pub(crate) fn release(&mut self) {
59 self.drag = None;
60 self.applied = Rotor4::IDENTITY;
61 }
62
63 pub(crate) fn held(&self) -> bool {
64 self.drag.is_some()
65 }
66
67 pub(crate) fn aim(&mut self, ray: Option<&ImageRay>, center: Vec3) {
68 if self.drag.is_some() {
69 return;
70 }
71 self.hover = ray.and_then(|ray| {
72 widget(center)
73 .pick(
74 Vec3::from(ray.origin),
75 Vec3::from(ray.direction),
76 PICK_TOLERANCE,
77 )
78 .map(|ring| HandleId::Rotate(ring.plane))
79 });
80 }
81}
82
83impl GimbalRenderer {
84 pub(crate) fn rings(&mut self, gimbal: &Gimbal, center: Vec3) -> &[SegmentRecord] {
85 self.segments.clear();
86 if !gimbal.enabled {
87 return &self.segments;
88 }
89 let mut style = RingStyle::default();
90 if let Some(HandleId::Rotate(plane)) = gimbal.hover {
91 style.colors[plane as usize] = HIGHLIGHT;
92 }
93 style.width_px = RING_WIDTH_PX;
94 let mesh = &mut self.mesh;
95 mesh.segments.clear();
96 mesh.colors.clear();
97 mesh.widths.clear();
98 widget(center).append_line_mesh(&style, mesh);
99 for ((start, end), ((from, to), width)) in mesh
100 .segments
101 .iter()
102 .zip(mesh.colors.iter().zip(mesh.widths.iter()))
103 {
104 self.segments.push(SegmentRecord {
105 start: *start,
106 _pad0: 0.0,
107 end: *end,
108 _pad1: 0.0,
109 start_color: *from,
110 end_color: *to,
111 width_px: *width,
112 _pad2: [0.0; 3],
113 });
114 }
115 &self.segments
116 }
117}