Skip to main content

polytope_playground/
guides.rs

1use glam::{Vec2, Vec3};
2use loam::math::{EuclideanR4, Rotor};
3use loam::runtime::{DomainHandle, PointRecord, SegmentRecord, Session};
4
5use crate::consts::FLOOR_Y;
6use crate::mode::Mode;
7use crate::toy::{ARENA_HALF, BODY_SIZE};
8use crate::Playground;
9
10const HANDLE_COLOR: [f32; 4] = [1.0, 0.85, 0.25, 1.0];
11
12#[derive(Default)]
13pub(crate) struct Guides {
14    pub(crate) lines: Vec<SegmentRecord>,
15    pub(crate) points: Vec<PointRecord>,
16}
17
18impl Guides {
19    pub(crate) fn update(
20        &mut self,
21        session: &Session<Playground>,
22        domain: DomainHandle<EuclideanR4>,
23        alpha: f32,
24    ) {
25        self.lines.clear();
26        self.points.clear();
27        if *session.app.mode.get() != Mode::Toybox
28            || session.app.strip.get().on
29            || !*session.app.guides.get()
30        {
31            return;
32        }
33        self.rectangle(
34            Vec2::splat(-ARENA_HALF),
35            Vec2::splat(ARENA_HALF),
36            [0.55, 0.60, 0.68, 1.0],
37        );
38        let Some(drag) = session.dragging() else {
39            return;
40        };
41        let Some(polytope) = session
42            .app
43            .slots
44            .get(drag.entity)
45            .and_then(|slot| slot.entry.shape.polytope4())
46        else {
47            return;
48        };
49        let Ok(r4) = session.domains().read(domain) else {
50            return;
51        };
52        if *session.app.toybox_debug.get() {
53            let placement = session
54                .views()
55                .to_root(drag.image)
56                .and_then(|to| to.rigid());
57            let anchor = r4
58                .physics()
59                .and_then(|physics| physics.held_anchor(drag.entity));
60            if let (Some(anchor), Some(tick), Some(drawn), Some(placement), Some(view), Some(eye)) = (
61                anchor,
62                r4.poses().get(drag.entity),
63                r4.drawn_pose(drag.entity, alpha),
64                placement,
65                r4.view(drag.view),
66                r4.view_eye(drag.view)
67                    .and_then(|eye| r4.drawn_pose(eye, alpha)),
68            ) {
69                let anchor = drawn.point
70                    + drawn
71                        .frame
72                        .apply(tick.frame.inverse().apply(anchor - tick.point));
73                if let Some(point) = view.mapping().image_point(&eye, anchor) {
74                    self.points.push(PointRecord {
75                        position: placement.apply(point),
76                        radius_px: 6.0,
77                        color: HANDLE_COLOR,
78                    });
79                }
80            }
81        }
82        let Some(pose) = r4.drawn_pose(drag.entity, alpha) else {
83            return;
84        };
85        let mut min = Vec2::splat(f32::INFINITY);
86        let mut max = Vec2::splat(f32::NEG_INFINITY);
87        for vertex in polytope.topology().vertices {
88            let point = pose.point + pose.frame.apply(*vertex * BODY_SIZE);
89            let xz = Vec2::new(point.x, point.z);
90            min = min.min(xz);
91            max = max.max(xz);
92        }
93        self.rectangle(min, max, HANDLE_COLOR);
94    }
95
96    fn rectangle(&mut self, min: Vec2, max: Vec2, color: [f32; 4]) {
97        let y = FLOOR_Y + 0.002;
98        let corners = [
99            Vec3::new(min.x, y, min.y),
100            Vec3::new(max.x, y, min.y),
101            Vec3::new(max.x, y, max.y),
102            Vec3::new(min.x, y, max.y),
103        ];
104        for index in 0..4 {
105            self.lines.push(SegmentRecord {
106                start: corners[index].to_array(),
107                end: corners[(index + 1) % 4].to_array(),
108                start_color: color,
109                end_color: color,
110                width_px: 1.5,
111                ..SegmentRecord::default()
112            });
113        }
114    }
115}