Skip to main content

polytope_playground/
gimbal.rs

1use 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}