game/edit/traffic_signals/
preview.rs

1use std::collections::BTreeSet;
2
3use abstutil::Timer;
4use geom::Duration;
5use map_model::IntersectionID;
6use widgetry::tools::ChooseSomething;
7use widgetry::{
8    Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, TextExt, UpdateType,
9    VerticalAlignment, Widget,
10};
11
12use crate::app::{App, Transition};
13use crate::sandbox::{spawn_agents_around, TimePanel};
14
15// TODO Show diagram, auto-sync the stage.
16// TODO Auto quit after things are gone?
17struct PreviewTrafficSignal {
18    panel: Panel,
19    time_panel: TimePanel,
20}
21
22impl PreviewTrafficSignal {
23    fn new_state(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
24        Box::new(PreviewTrafficSignal {
25            panel: Panel::new_builder(Widget::col(vec![
26                "Previewing traffic signal".text_widget(ctx),
27                ctx.style()
28                    .btn_outline
29                    .text("back to editing")
30                    .hotkey(Key::Escape)
31                    .build_def(ctx),
32            ]))
33            .aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
34            .build(ctx),
35            time_panel: TimePanel::new(ctx, app),
36        })
37    }
38}
39
40impl State<App> for PreviewTrafficSignal {
41    fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
42        ctx.canvas_movement();
43
44        if let Outcome::Clicked(x) = self.panel.event(ctx) {
45            match x.as_ref() {
46                "back to editing" => {
47                    app.primary.clear_sim();
48                    return Transition::Pop;
49                }
50                _ => unreachable!(),
51            }
52        }
53
54        // TODO Ideally here reset to midnight would jump back to when the preview started?
55        if let Some(t) = self.time_panel.event(ctx, app, None) {
56            return t;
57        }
58        if self.time_panel.is_paused() {
59            Transition::Keep
60        } else {
61            ctx.request_update(UpdateType::Game);
62            Transition::Keep
63        }
64    }
65
66    fn draw(&self, g: &mut GfxCtx, _: &App) {
67        self.panel.draw(g);
68        self.time_panel.draw(g);
69    }
70}
71
72// TODO I guess it's valid to preview without all turns possible. Some agents are just sad.
73pub fn make_previewer(
74    ctx: &mut EventCtx,
75    app: &App,
76    members: BTreeSet<IntersectionID>,
77    stage: usize,
78) -> Box<dyn State<App>> {
79    let random = "random agents around these intersections".to_string();
80    let right_now = format!(
81        "change the traffic signal live at {}",
82        app.primary.suspended_sim.as_ref().unwrap().time()
83    );
84
85    ChooseSomething::new_state(
86        ctx,
87        "Preview the traffic signal with what kind of traffic?",
88        Choice::strings(vec![random, right_now]),
89        Box::new(move |x, ctx, app| {
90            if x == "random agents around these intersections" {
91                for (idx, i) in members.into_iter().enumerate() {
92                    if idx == 0 {
93                        // Start at the current stage
94                        let signal = app.primary.map.get_traffic_signal(i);
95                        // TODO Use the offset correctly
96                        // TODO If there are variable stages, this could land anywhere
97                        let mut step = Duration::ZERO;
98                        for idx in 0..stage {
99                            step += signal.stages[idx].stage_type.simple_duration();
100                        }
101                        app.primary.sim.timed_step(
102                            &app.primary.map,
103                            step,
104                            &mut app.primary.sim_cb,
105                            &mut Timer::throwaway(),
106                        );
107                    }
108
109                    spawn_agents_around(i, app);
110                }
111            } else {
112                app.primary.sim = app.primary.suspended_sim.as_ref().unwrap().clone();
113                app.primary
114                    .sim
115                    .handle_live_edited_traffic_signals(&app.primary.map);
116            }
117            Transition::Replace(PreviewTrafficSignal::new_state(ctx, app))
118        }),
119    )
120}