game/debug/
polygons.rs

1use geom::{Polygon, Pt2D, Triangle};
2use widgetry::tools::PopupMsg;
3use widgetry::{
4    EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
5    TextExt, VerticalAlignment, Widget,
6};
7
8use crate::app::{App, Transition};
9
10pub struct PolygonDebugger {
11    panel: Panel,
12    noun: String,
13    items: Vec<Item>,
14    idx: usize,
15    center: Option<Pt2D>,
16}
17
18pub enum Item {
19    Point(Pt2D),
20    Triangle(Triangle),
21    Polygon(Polygon),
22}
23
24impl PolygonDebugger {
25    pub fn new_state(
26        ctx: &mut EventCtx,
27        noun: &str,
28        items: Vec<Item>,
29        center: Option<Pt2D>,
30    ) -> Box<dyn State<App>> {
31        if items.is_empty() {
32            return PopupMsg::new_state(ctx, "Woops", vec![format!("No {}, never mind", noun)]);
33        }
34
35        Box::new(PolygonDebugger {
36            panel: Panel::new_builder(Widget::col(vec![
37                Widget::row(vec![
38                    Line("Geometry debugger").small_heading().into_widget(ctx),
39                    ctx.style().btn_close_widget(ctx),
40                ]),
41                Widget::row(vec![
42                    // TODO Make these inactive when on the first/last. More generally, refactor
43                    // some kind of pagination helper.
44                    ctx.style()
45                        .btn_prev()
46                        .hotkey(Key::LeftArrow)
47                        .build_widget(ctx, "previous"),
48                    "noun X/Y".text_widget(ctx).named("pointer").centered_vert(),
49                    ctx.style()
50                        .btn_next()
51                        .hotkey(Key::RightArrow)
52                        .build_widget(ctx, "next"),
53                ])
54                .evenly_spaced(),
55            ]))
56            .aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
57            .build(ctx),
58            noun: noun.to_string(),
59            items,
60            idx: 0,
61            center,
62        })
63    }
64}
65
66impl State<App> for PolygonDebugger {
67    fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
68        ctx.canvas_movement();
69
70        if let Outcome::Clicked(x) = self.panel.event(ctx) {
71            match x.as_ref() {
72                "close" => {
73                    return Transition::Pop;
74                }
75                "previous" => {
76                    if self.idx != 0 {
77                        self.idx -= 1;
78                    }
79                }
80                "next" => {
81                    if self.idx != self.items.len() - 1 {
82                        self.idx += 1;
83                    }
84                }
85                _ => unreachable!(),
86            }
87        }
88        self.panel.replace(
89            ctx,
90            "pointer",
91            format!("{} {}/{}", self.noun, self.idx + 1, self.items.len()).text_widget(ctx),
92        );
93
94        Transition::Keep
95    }
96
97    fn draw(&self, g: &mut GfxCtx, app: &App) {
98        // This is drawn in screen-space, so zooming doesn't affect the text size
99        let mut batch = GeomBatch::new();
100
101        match &self.items[self.idx] {
102            Item::Point(pt) => {
103                batch.append(
104                    Text::from(self.idx.to_string())
105                        .bg(app.cs.panel_bg)
106                        .render(g)
107                        .centered_on(g.canvas.map_to_screen(*pt).to_pt()),
108                );
109            }
110            Item::Triangle(ref tri) => {
111                for pt in [tri.pt1, tri.pt2, tri.pt3] {
112                    batch.append(
113                        Text::from(self.idx.to_string())
114                            .bg(app.cs.panel_bg)
115                            .render(g)
116                            .centered_on(g.canvas.map_to_screen(pt).to_pt()),
117                    );
118                }
119                g.draw_polygon(app.cs.selected, Polygon::from_triangle(tri));
120            }
121            Item::Polygon(ref poly) => {
122                g.draw_polygon(app.cs.selected, poly.clone());
123                batch.append(
124                    Text::from(self.idx.to_string())
125                        .bg(app.cs.panel_bg)
126                        .render(g)
127                        .centered_on(g.canvas.map_to_screen(poly.center()).to_pt()),
128                );
129            }
130        }
131        if let Some(pt) = self.center {
132            batch.append(
133                Text::from("c")
134                    .bg(app.cs.panel_bg)
135                    .render(g)
136                    .centered_on(g.canvas.map_to_screen(pt).to_pt()),
137            );
138        }
139
140        let draw = g.upload(batch);
141        g.fork_screenspace();
142        g.redraw(&draw);
143        g.unfork();
144
145        self.panel.draw(g);
146    }
147}