mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +03:00
add option to preview traffic signal edits with the live sim results
This commit is contained in:
parent
ebd405e2ef
commit
fbbb59f6a5
@ -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(),
|
||||
))))
|
||||
}),
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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(),
|
||||
))))
|
||||
}),
|
||||
|
@ -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"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user