game/debug/
uber_turns.rs

1use std::collections::BTreeSet;
2
3use crate::ID;
4use geom::ArrowCap;
5use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
6use map_model::{IntersectionCluster, IntersectionID};
7use widgetry::tools::PopupMsg;
8use widgetry::{
9    Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
10    Panel, SimpleState, State, Text, TextExt, Toggle, VerticalAlignment, Widget,
11};
12
13use crate::app::{App, ShowEverything, Transition};
14use crate::common::CommonState;
15
16pub struct UberTurnPicker {
17    members: BTreeSet<IntersectionID>,
18}
19
20impl UberTurnPicker {
21    pub fn new_state(ctx: &mut EventCtx, app: &App, i: IntersectionID) -> Box<dyn State<App>> {
22        let mut members = BTreeSet::new();
23        if let Some(list) = IntersectionCluster::autodetect(i, &app.primary.map) {
24            members.extend(list);
25        } else {
26            members.insert(i);
27        }
28
29        let panel = Panel::new_builder(Widget::col(vec![
30            Widget::row(vec![
31                Line("Select multiple intersections")
32                    .small_heading()
33                    .into_widget(ctx),
34                ctx.style().btn_close_widget(ctx),
35            ]),
36            ctx.style()
37                .btn_outline
38                .text("View uber-turns")
39                .hotkey(Key::Enter)
40                .build_def(ctx),
41            ctx.style()
42                .btn_outline
43                .text("Detect all clusters")
44                .hotkey(Key::D)
45                .build_def(ctx),
46        ]))
47        .aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
48        .build(ctx);
49        <dyn SimpleState<_>>::new_state(panel, Box::new(UberTurnPicker { members }))
50    }
51}
52
53impl SimpleState<App> for UberTurnPicker {
54    fn on_click(
55        &mut self,
56        ctx: &mut EventCtx,
57        app: &mut App,
58        x: &str,
59        _: &mut Panel,
60    ) -> Transition {
61        match x {
62            "close" => Transition::Pop,
63            "View uber-turns" => {
64                if self.members.len() < 2 {
65                    return Transition::Push(PopupMsg::new_state(
66                        ctx,
67                        "Error",
68                        vec!["Select at least two intersections"],
69                    ));
70                }
71                Transition::Replace(UberTurnViewer::new_state(
72                    ctx,
73                    app,
74                    self.members.clone(),
75                    0,
76                    true,
77                ))
78            }
79            "Detect all clusters" => {
80                self.members.clear();
81                for ic in IntersectionCluster::find_all(&app.primary.map) {
82                    self.members.extend(ic.members);
83                }
84                Transition::Keep
85            }
86            _ => unreachable!(),
87        }
88    }
89
90    fn on_mouseover(&mut self, ctx: &mut EventCtx, app: &mut App) {
91        app.primary.current_selection = app.mouseover_unzoomed_intersections(ctx);
92    }
93    fn other_event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
94        ctx.canvas_movement();
95        if let Some(ID::Intersection(i)) = app.primary.current_selection {
96            if !self.members.contains(&i) && app.per_obj.left_click(ctx, "add this intersection") {
97                self.members.insert(i);
98            } else if self.members.contains(&i)
99                && app.per_obj.left_click(ctx, "remove this intersection")
100            {
101                self.members.remove(&i);
102            }
103        }
104        Transition::Keep
105    }
106
107    fn draw(&self, g: &mut GfxCtx, app: &App) {
108        CommonState::draw_osd(g, app);
109
110        let mut batch = GeomBatch::new();
111        for i in &self.members {
112            batch.push(
113                Color::RED.alpha(0.8),
114                app.primary.map.get_i(*i).polygon.clone(),
115            );
116        }
117        let draw = g.upload(batch);
118        g.redraw(&draw);
119    }
120}
121
122struct UberTurnViewer {
123    draw: Drawable,
124    ic: IntersectionCluster,
125    idx: usize,
126    legal_turns: bool,
127}
128
129impl UberTurnViewer {
130    pub fn new_state(
131        ctx: &mut EventCtx,
132        app: &mut App,
133        members: BTreeSet<IntersectionID>,
134        idx: usize,
135        legal_turns: bool,
136    ) -> Box<dyn State<App>> {
137        app.primary.current_selection = None;
138        let map = &app.primary.map;
139
140        let (ic1, ic2) = IntersectionCluster::new(members, map);
141        let ic = if legal_turns { ic1 } else { ic2 };
142
143        let mut batch = GeomBatch::new();
144        for i in &ic.members {
145            batch.push(Color::BLUE.alpha(0.5), map.get_i(*i).polygon.clone());
146        }
147        if !ic.uber_turns.is_empty() {
148            let ut = &ic.uber_turns[idx];
149            batch.push(
150                Color::RED,
151                ut.geom(map)
152                    .make_arrow(BIG_ARROW_THICKNESS, ArrowCap::Triangle),
153            );
154        }
155
156        let panel = Panel::new_builder(Widget::col(vec![
157            Widget::row(vec![
158                Line("Uber-turn viewer").small_heading().into_widget(ctx),
159                Widget::vert_separator(ctx, 50.0),
160                ctx.style()
161                    .btn_prev()
162                    .disabled(idx == 0)
163                    .hotkey(Key::LeftArrow)
164                    .build_widget(ctx, "previous uber-turn"),
165                Text::from(Line(format!("{}/{}", idx + 1, ic.uber_turns.len())).secondary())
166                    .into_widget(ctx)
167                    .centered_vert(),
168                ctx.style()
169                    .btn_next()
170                    .disabled(ic.uber_turns.is_empty() || idx == ic.uber_turns.len() - 1)
171                    .hotkey(Key::RightArrow)
172                    .build_widget(ctx, "next uber-turn"),
173                ctx.style().btn_close_widget(ctx),
174            ]),
175            Widget::row(vec![
176                Toggle::choice(
177                    ctx,
178                    "legal / illegal movements",
179                    "legal",
180                    "illegal",
181                    None,
182                    legal_turns,
183                ),
184                "movements".text_widget(ctx),
185            ]),
186        ]))
187        .aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
188        .build(ctx);
189        <dyn SimpleState<_>>::new_state(
190            panel,
191            Box::new(UberTurnViewer {
192                draw: ctx.upload(batch),
193                ic,
194                idx,
195                legal_turns,
196            }),
197        )
198    }
199}
200
201impl SimpleState<App> for UberTurnViewer {
202    fn on_click(
203        &mut self,
204        ctx: &mut EventCtx,
205        app: &mut App,
206        x: &str,
207        _: &mut Panel,
208    ) -> Transition {
209        match x {
210            "close" => Transition::Pop,
211            "previous uber-turn" => Transition::Replace(UberTurnViewer::new_state(
212                ctx,
213                app,
214                self.ic.members.clone(),
215                self.idx - 1,
216                self.legal_turns,
217            )),
218            "next uber-turn" => Transition::Replace(UberTurnViewer::new_state(
219                ctx,
220                app,
221                self.ic.members.clone(),
222                self.idx + 1,
223                self.legal_turns,
224            )),
225            _ => unreachable!(),
226        }
227    }
228    fn panel_changed(
229        &mut self,
230        ctx: &mut EventCtx,
231        app: &mut App,
232        panel: &mut Panel,
233    ) -> Option<Transition> {
234        Some(Transition::Replace(UberTurnViewer::new_state(
235            ctx,
236            app,
237            self.ic.members.clone(),
238            0,
239            panel.is_checked("legal / illegal movements"),
240        )))
241    }
242
243    fn other_event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
244        ctx.canvas_movement();
245        Transition::Keep
246    }
247
248    fn draw_baselayer(&self) -> DrawBaselayer {
249        DrawBaselayer::Custom
250    }
251
252    fn draw(&self, g: &mut GfxCtx, app: &App) {
253        let mut opts = DrawOptions::new();
254        opts.suppress_traffic_signal_details
255            .extend(self.ic.members.clone());
256        app.draw(g, opts, &ShowEverything::new());
257
258        g.redraw(&self.draw);
259    }
260}