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(
|
.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(),
|
||||||
))))
|
))))
|
||||||
}),
|
}),
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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(),
|
||||||
))))
|
))))
|
||||||
}),
|
}),
|
||||||
|
@ -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"),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user