add option to preview traffic signal edits with the live sim results

This commit is contained in:
Dustin Carlino 2019-12-24 14:34:50 -08:00
parent ebd405e2ef
commit fbbb59f6a5
6 changed files with 89 additions and 41 deletions

View File

@ -85,9 +85,9 @@ pub fn edit_map_panel(ctx: &EventCtx, ui: &UI, gameplay: GameplayMode) -> Compos
.cb( .cb(
"edit map", "edit map",
Box::new(move |ctx, ui| { Box::new(move |ctx, ui| {
ui.primary.clear_sim();
Some(Transition::Replace(Box::new(EditMode::new( Some(Transition::Replace(Box::new(EditMode::new(
ctx, ctx,
ui,
gameplay.clone(), gameplay.clone(),
)))) ))))
}), }),

View File

@ -18,19 +18,24 @@ use ezgui::{
WrappedWizard, WrappedWizard,
}; };
use map_model::{ControlStopSign, ControlTrafficSignal, EditCmd, LaneID, MapEdits}; use map_model::{ControlStopSign, ControlTrafficSignal, EditCmd, LaneID, MapEdits};
use sim::Sim;
use std::collections::BTreeSet; use std::collections::BTreeSet;
pub struct EditMode { pub struct EditMode {
common: CommonState, common: CommonState,
tool_panel: Composite, tool_panel: Composite,
menu: ModalMenu, menu: ModalMenu,
// Retained state from the SandboxMode that spawned us
mode: GameplayMode, mode: GameplayMode,
suspended_sim: Sim,
lane_editor: lanes::LaneEditor, lane_editor: lanes::LaneEditor,
} }
impl EditMode { impl EditMode {
pub fn new(ctx: &EventCtx, mode: GameplayMode) -> EditMode { pub fn new(ctx: &EventCtx, ui: &mut UI, mode: GameplayMode) -> EditMode {
let suspended_sim = ui.primary.clear_sim();
EditMode { EditMode {
common: CommonState::new(), common: CommonState::new(),
tool_panel: tool_panel(ctx, Vec::new()), tool_panel: tool_panel(ctx, Vec::new()),
@ -49,6 +54,7 @@ impl EditMode {
ctx, ctx,
), ),
mode, mode,
suspended_sim,
lane_editor: lanes::LaneEditor::setup(ctx), lane_editor: lanes::LaneEditor::setup(ctx),
} }
} }
@ -196,7 +202,10 @@ impl State for EditMode {
.action(ctx, Key::E, format!("edit traffic signal for {}", id)) .action(ctx, Key::E, format!("edit traffic signal for {}", id))
{ {
return Transition::Push(Box::new(traffic_signals::TrafficSignalEditor::new( return Transition::Push(Box::new(traffic_signals::TrafficSignalEditor::new(
id, ctx, ui, id,
ctx,
ui,
self.suspended_sim.clone(),
))); )));
} else if ui } else if ui
.primary .primary

View File

@ -15,6 +15,7 @@ use geom::{Duration, Time};
use map_model::{ use map_model::{
ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroupID, TurnPriority, TurnType, ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroupID, TurnPriority, TurnType,
}; };
use sim::Sim;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::time::Instant; use std::time::Instant;
@ -24,10 +25,17 @@ pub struct TrafficSignalEditor {
diagram: TrafficSignalDiagram, diagram: TrafficSignalDiagram,
groups: Vec<DrawTurnGroup>, groups: Vec<DrawTurnGroup>,
group_selected: Option<TurnGroupID>, group_selected: Option<TurnGroupID>,
suspended_sim: Sim,
} }
impl TrafficSignalEditor { impl TrafficSignalEditor {
pub fn new(id: IntersectionID, ctx: &mut EventCtx, ui: &mut UI) -> TrafficSignalEditor { pub fn new(
id: IntersectionID,
ctx: &mut EventCtx,
ui: &mut UI,
suspended_sim: Sim,
) -> TrafficSignalEditor {
ui.primary.current_selection = None; ui.primary.current_selection = None;
let menu = ModalMenu::new( let menu = ModalMenu::new(
format!("Traffic Signal Editor for {}", id), format!("Traffic Signal Editor for {}", id),
@ -57,6 +65,7 @@ impl TrafficSignalEditor {
diagram: TrafficSignalDiagram::new(id, 0, ui, ctx), diagram: TrafficSignalDiagram::new(id, 0, ui, ctx),
groups: DrawTurnGroup::for_i(id, &ui.primary.map), groups: DrawTurnGroup::for_i(id, &ui.primary.map),
group_selected: None, group_selected: None,
suspended_sim,
} }
} }
} }
@ -219,15 +228,12 @@ impl State for TrafficSignalEditor {
} }
if self.menu.action("preview changes") { if self.menu.action("preview changes") {
return Transition::PushWithMode( // TODO These're expensive clones :(
Box::new(PreviewTrafficSignal::new( return Transition::Push(make_previewer(
self.diagram.i, self.diagram.i,
current_phase, current_phase,
ui, self.suspended_sim.clone(),
ctx, ));
)),
EventLoopMode::Animation,
);
} }
Transition::Keep Transition::Keep
@ -406,6 +412,47 @@ fn check_for_missing_groups(
Transition::Push(msg("Error: missing turns", vec![format!("{} turns are missing from this traffic signal", num_missing), "They've all been added as a new last phase. Please update your changes to include them.".to_string()])) Transition::Push(msg("Error: missing turns", vec![format!("{} turns are missing from this traffic signal", num_missing), "They've all been added as a new last phase. Please update your changes to include them.".to_string()]))
} }
// TODO I guess it's valid to preview without all turns possible. Some agents are just sad.
fn make_previewer(i: IntersectionID, phase: usize, suspended_sim: Sim) -> Box<dyn State> {
WizardState::new(Box::new(move |wiz, ctx, ui| {
let random = "random agents around just this intersection".to_string();
let right_now = format!("change the traffic signal live at {}", suspended_sim.time());
match wiz
.wrap(ctx)
.choose_string(
"Preview the traffic signal with what kind of traffic?",
|| vec![random.clone(), right_now.clone()],
)?
.as_str()
{
x if x == random => {
// Start at the current phase
let signal = ui.primary.map.get_traffic_signal(i);
// TODO Use the offset correctly
let mut step = Duration::ZERO;
for idx in 0..phase {
step += signal.phases[idx].duration;
}
ui.primary.sim.step(&ui.primary.map, step);
// This should be a no-op
ui.primary
.map
.recalculate_pathfinding_after_edits(&mut Timer::throwaway());
spawn_agents_around(i, ui, ctx);
}
x if x == right_now => {
ui.primary.sim = suspended_sim.clone();
}
_ => unreachable!(),
};
Some(Transition::ReplaceWithMode(
Box::new(PreviewTrafficSignal::new(ctx)),
EventLoopMode::Animation,
))
}))
}
// TODO Show diagram, auto-sync the phase. // TODO Show diagram, auto-sync the phase.
// TODO Auto quit after things are gone? // TODO Auto quit after things are gone?
struct PreviewTrafficSignal { struct PreviewTrafficSignal {
@ -414,27 +461,7 @@ struct PreviewTrafficSignal {
} }
impl PreviewTrafficSignal { impl PreviewTrafficSignal {
fn new( fn new(ctx: &EventCtx) -> PreviewTrafficSignal {
i: IntersectionID,
phase_idx: usize,
ui: &mut UI,
ctx: &EventCtx,
) -> PreviewTrafficSignal {
// Start at the current phase
let signal = ui.primary.map.get_traffic_signal(i);
// TODO Use the offset correctly
let mut step = Duration::ZERO;
for idx in 0..phase_idx {
step += signal.phases[idx].duration;
}
ui.primary.sim.step(&ui.primary.map, step);
// This should be a no-op
ui.primary
.map
.recalculate_pathfinding_after_edits(&mut Timer::throwaway());
spawn_agents_around(i, ui, ctx);
PreviewTrafficSignal { PreviewTrafficSignal {
menu: ModalMenu::new( menu: ModalMenu::new(
"Preview traffic signal", "Preview traffic signal",

View File

@ -119,9 +119,9 @@ pub fn freeform_controller(
.cb( .cb(
"edit map", "edit map",
Box::new(move |ctx, ui| { Box::new(move |ctx, ui| {
ui.primary.clear_sim();
Some(Transition::Replace(Box::new(EditMode::new( Some(Transition::Replace(Box::new(EditMode::new(
ctx, ctx,
ui,
gameplay.clone(), gameplay.clone(),
)))) ))))
}), }),

View File

@ -467,11 +467,15 @@ impl PerMapUI {
} }
} }
pub fn clear_sim(&mut self) { // Returns whatever was there
self.sim = Sim::new( pub fn clear_sim(&mut self) -> Sim {
&self.map, std::mem::replace(
self.current_flags.sim_flags.opts.clone(), &mut self.sim,
&mut Timer::new("reset simulation"), Sim::new(
); &self.map,
self.current_flags.sim_flags.opts.clone(),
&mut Timer::new("reset simulation"),
),
)
} }
} }

View File

@ -800,6 +800,14 @@ impl Sim {
); );
self.scheduler.after_savestate(paths); self.scheduler.after_savestate(paths);
} }
// TODO Slight hack?
pub fn clone(&self) -> Sim {
// TODO Temp file
let path = self.save_path(self.time);
abstutil::write_binary(path.clone(), self);
abstutil::read_binary(path, &mut Timer::throwaway())
}
} }
// Queries of all sorts // Queries of all sorts