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(
"edit map",
Box::new(move |ctx, ui| {
ui.primary.clear_sim();
Some(Transition::Replace(Box::new(EditMode::new(
ctx,
ui,
gameplay.clone(),
))))
}),

View File

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

View File

@ -15,6 +15,7 @@ use geom::{Duration, Time};
use map_model::{
ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroupID, TurnPriority, TurnType,
};
use sim::Sim;
use std::collections::BTreeSet;
use std::time::Instant;
@ -24,10 +25,17 @@ pub struct TrafficSignalEditor {
diagram: TrafficSignalDiagram,
groups: Vec<DrawTurnGroup>,
group_selected: Option<TurnGroupID>,
suspended_sim: Sim,
}
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;
let menu = ModalMenu::new(
format!("Traffic Signal Editor for {}", id),
@ -57,6 +65,7 @@ impl TrafficSignalEditor {
diagram: TrafficSignalDiagram::new(id, 0, ui, ctx),
groups: DrawTurnGroup::for_i(id, &ui.primary.map),
group_selected: None,
suspended_sim,
}
}
}
@ -219,15 +228,12 @@ impl State for TrafficSignalEditor {
}
if self.menu.action("preview changes") {
return Transition::PushWithMode(
Box::new(PreviewTrafficSignal::new(
self.diagram.i,
current_phase,
ui,
ctx,
)),
EventLoopMode::Animation,
);
// TODO These're expensive clones :(
return Transition::Push(make_previewer(
self.diagram.i,
current_phase,
self.suspended_sim.clone(),
));
}
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()]))
}
// 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 Auto quit after things are gone?
struct PreviewTrafficSignal {
@ -414,27 +461,7 @@ struct PreviewTrafficSignal {
}
impl PreviewTrafficSignal {
fn new(
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);
fn new(ctx: &EventCtx) -> PreviewTrafficSignal {
PreviewTrafficSignal {
menu: ModalMenu::new(
"Preview traffic signal",

View File

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

View File

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