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