moving route viewer to sandbox

This commit is contained in:
Dustin Carlino 2019-04-25 14:39:18 -07:00
parent 79b87d6040
commit e763171bc9
6 changed files with 188 additions and 163 deletions

View File

@ -3,6 +3,5 @@ pub mod neighborhood_summary;
pub mod search;
pub mod show_activity;
pub mod show_associated;
pub mod show_route;
pub mod turn_cycler;
pub mod warp;

View File

@ -1,155 +0,0 @@
use crate::objects::{DrawCtx, ID};
use crate::plugins::{AmbientPlugin, PluginCtx};
use ezgui::{Color, GfxCtx, Key};
use geom::{Duration, PolyLine};
use map_model::LANE_THICKNESS;
use sim::{AgentID, TripID};
pub enum ShowRouteState {
Inactive,
Hovering(Duration, AgentID, PolyLine),
Active(Duration, TripID, Option<PolyLine>),
DebugAllRoutes(Duration, Vec<PolyLine>),
}
impl ShowRouteState {
pub fn new() -> ShowRouteState {
ShowRouteState::Inactive
}
}
impl AmbientPlugin for ShowRouteState {
fn ambient_event(&mut self, ctx: &mut PluginCtx) {
match self {
ShowRouteState::Inactive => {
if let Some(agent) = ctx.primary.current_selection.and_then(|id| id.agent_id()) {
if let Some(trace) = ctx.primary.sim.trace_route(agent, &ctx.primary.map, None)
{
*self = ShowRouteState::Hovering(ctx.primary.sim.time(), agent, trace);
}
};
}
ShowRouteState::Hovering(time, agent, _) => {
// Argh, borrow checker.
let agent = *agent;
if *time != ctx.primary.sim.time()
|| ctx.primary.current_selection != Some(ID::from_agent(agent))
{
*self = ShowRouteState::Inactive;
if let Some(new_agent) =
ctx.primary.current_selection.and_then(|id| id.agent_id())
{
if let Some(trace) =
ctx.primary
.sim
.trace_route(new_agent, &ctx.primary.map, None)
{
*self =
ShowRouteState::Hovering(ctx.primary.sim.time(), new_agent, trace);
}
}
}
// If there's a current route, then there must be a trip.
let trip = ctx.primary.sim.agent_to_trip(agent).unwrap();
// TODO agent might be stale here! Really need a second match after this or
// something. Expressing a state machine like this isn't really great.
if ctx
.input
.contextual_action(Key::R, &format!("show {}'s route", agent))
{
*self = show_route(trip, ctx);
}
}
ShowRouteState::Active(time, trip, _) => {
ctx.input.set_mode_with_prompt(
"Agent Route Debugger",
format!("Agent Route Debugger for {}", trip),
&ctx.canvas,
);
if ctx.input.modal_action("quit") {
*self = ShowRouteState::Inactive;
} else if ctx.input.modal_action("show route for all agents") {
*self = debug_all_routes(ctx);
} else if *time != ctx.primary.sim.time() {
*self = show_route(*trip, ctx);
}
}
ShowRouteState::DebugAllRoutes(time, _) => {
ctx.input.set_mode_with_prompt(
"Agent Route Debugger",
"Agent Route Debugger for all routes".to_string(),
&ctx.canvas,
);
if ctx.input.modal_action("quit") {
*self = ShowRouteState::Inactive;
} else if *time != ctx.primary.sim.time() {
*self = debug_all_routes(ctx);
}
}
};
}
fn draw(&self, g: &mut GfxCtx, ctx: &DrawCtx) {
match self {
ShowRouteState::Hovering(_, _, ref trace) => {
g.draw_polygon(
ctx.cs.get("route").alpha(0.5),
&trace.make_polygons(LANE_THICKNESS),
);
}
ShowRouteState::Active(_, _, Some(ref trace)) => {
g.draw_polygon(
ctx.cs.get_def("route", Color::RED.alpha(0.8)),
&trace.make_polygons(LANE_THICKNESS),
);
}
ShowRouteState::DebugAllRoutes(_, ref traces) => {
for t in traces {
g.draw_polygon(ctx.cs.get("route"), &t.make_polygons(LANE_THICKNESS));
}
}
_ => {}
}
}
}
fn show_route(trip: TripID, ctx: &mut PluginCtx) -> ShowRouteState {
let time = ctx.primary.sim.time();
if let Some(agent) = ctx.primary.sim.trip_to_agent(trip) {
// Trace along the entire route by passing in max distance
if let Some(trace) = ctx.primary.sim.trace_route(agent, &ctx.primary.map, None) {
ShowRouteState::Active(time, trip, Some(trace))
} else {
println!("{} has no trace right now", agent);
ShowRouteState::Active(time, trip, None)
}
} else {
println!(
"{} has no agent associated right now; is the trip done?",
trip
);
ShowRouteState::Active(time, trip, None)
}
}
fn debug_all_routes(ctx: &mut PluginCtx) -> ShowRouteState {
let mut traces: Vec<PolyLine> = Vec::new();
let trips: Vec<TripID> = ctx
.primary
.sim
.get_stats(&ctx.primary.map)
.canonical_pt_per_trip
.keys()
.cloned()
.collect();
for trip in trips {
if let Some(agent) = ctx.primary.sim.trip_to_agent(trip) {
if let Some(trace) = ctx.primary.sim.trace_route(agent, &ctx.primary.map, None) {
traces.push(trace);
}
}
}
ShowRouteState::DebugAllRoutes(ctx.primary.sim.time(), traces)
}

View File

@ -1,3 +1,4 @@
mod route_viewer;
mod spawner;
use crate::game::{GameState, Mode};
@ -13,6 +14,7 @@ const ADJUST_SPEED: f64 = 0.1;
pub struct SandboxMode {
desired_speed: f64, // sim seconds per real second
following: Option<TripID>,
route_viewer: route_viewer::RouteViewer,
state: State,
}
@ -32,6 +34,7 @@ impl SandboxMode {
desired_speed: 1.0,
state: State::Paused,
following: None,
route_viewer: route_viewer::RouteViewer::Inactive,
}
}
@ -65,6 +68,15 @@ impl SandboxMode {
if let Some(trip) = mode.following {
txt.add_line(format!("Following {}", trip));
}
match mode.route_viewer {
route_viewer::RouteViewer::Active(_, trip, _) => {
txt.add_line(format!("Showing {}'s route", trip));
}
route_viewer::RouteViewer::DebugAllRoutes(_, _) => {
txt.add_line("Showing all routes".to_string());
}
_ => {}
}
ctx.input
.set_mode_with_new_prompt("Sandbox Mode", txt, ctx.canvas);
@ -109,6 +121,7 @@ impl SandboxMode {
mode.following = None;
}
}
mode.route_viewer.event(ctx, &mut state.ui);
if ctx.input.modal_action("quit") {
// TODO This shouldn't be necessary when we plumb state around instead of
@ -252,7 +265,10 @@ impl SandboxMode {
State::Spawning(ref spawner) => {
spawner.draw(g, &state.ui);
}
_ => state.ui.new_draw(g, None, HashMap::new()),
_ => {
state.ui.new_draw(g, None, HashMap::new());
mode.route_viewer.draw(g, &state.ui);
}
},
_ => unreachable!(),
}

View File

@ -0,0 +1,167 @@
use crate::objects::ID;
use crate::ui::UI;
use ezgui::{Color, EventCtx, GfxCtx, Key};
use geom::{Duration, PolyLine};
use map_model::LANE_THICKNESS;
use sim::{AgentID, TripID};
pub enum RouteViewer {
Inactive,
Hovering(Duration, AgentID, PolyLine),
Active(Duration, TripID, Option<PolyLine>),
DebugAllRoutes(Duration, Vec<PolyLine>),
}
impl RouteViewer {
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) {
match self {
RouteViewer::Inactive => {
if let Some(agent) = ui
.state
.primary
.current_selection
.and_then(|id| id.agent_id())
{
if let Some(trace) =
ui.state
.primary
.sim
.trace_route(agent, &ui.state.primary.map, None)
{
*self = RouteViewer::Hovering(ui.state.primary.sim.time(), agent, trace);
}
} else if ctx.input.modal_action("show/hide route for all agents") {
*self = debug_all_routes(ui);
}
}
RouteViewer::Hovering(time, agent, _) => {
// Argh, borrow checker.
let agent = *agent;
if *time != ui.state.primary.sim.time()
|| ui.state.primary.current_selection != Some(ID::from_agent(agent))
{
*self = RouteViewer::Inactive;
if let Some(new_agent) = ui
.state
.primary
.current_selection
.and_then(|id| id.agent_id())
{
if let Some(trace) =
ui.state
.primary
.sim
.trace_route(new_agent, &ui.state.primary.map, None)
{
*self = RouteViewer::Hovering(
ui.state.primary.sim.time(),
new_agent,
trace,
);
}
}
}
// If there's a current route, then there must be a trip.
let trip = ui.state.primary.sim.agent_to_trip(agent).unwrap();
// TODO agent might be stale here! Really need a second match after this or
// something. Expressing a state machine like this isn't really great.
if ctx
.input
.contextual_action(Key::R, &format!("show {}'s route", agent))
{
*self = show_route(trip, ui);
}
}
RouteViewer::Active(time, trip, _) => {
// TODO Using the modal menu from parent is weird...
if ctx.input.modal_action("stop showing agent's route") {
*self = RouteViewer::Inactive;
} else if *time != ui.state.primary.sim.time() {
*self = show_route(*trip, ui);
}
}
RouteViewer::DebugAllRoutes(time, _) => {
if ctx.input.modal_action("show/hide route for all agents") {
*self = RouteViewer::Inactive;
} else if *time != ui.state.primary.sim.time() {
*self = debug_all_routes(ui);
}
}
}
}
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
match self {
RouteViewer::Hovering(_, _, ref trace) => {
g.draw_polygon(
ui.state.cs.get("route").alpha(0.5),
&trace.make_polygons(LANE_THICKNESS),
);
}
RouteViewer::Active(_, _, Some(ref trace)) => {
g.draw_polygon(
ui.state.cs.get_def("route", Color::RED.alpha(0.8)),
&trace.make_polygons(LANE_THICKNESS),
);
}
RouteViewer::DebugAllRoutes(_, ref traces) => {
for t in traces {
g.draw_polygon(ui.state.cs.get("route"), &t.make_polygons(LANE_THICKNESS));
}
}
_ => {}
}
}
}
fn show_route(trip: TripID, ui: &UI) -> RouteViewer {
let time = ui.state.primary.sim.time();
if let Some(agent) = ui.state.primary.sim.trip_to_agent(trip) {
// Trace along the entire route by passing in max distance
if let Some(trace) = ui
.state
.primary
.sim
.trace_route(agent, &ui.state.primary.map, None)
{
RouteViewer::Active(time, trip, Some(trace))
} else {
println!("{} has no trace right now", agent);
RouteViewer::Active(time, trip, None)
}
} else {
println!(
"{} has no agent associated right now; is the trip done?",
trip
);
RouteViewer::Active(time, trip, None)
}
}
fn debug_all_routes(ui: &mut UI) -> RouteViewer {
let mut traces: Vec<PolyLine> = Vec::new();
let trips: Vec<TripID> = ui
.state
.primary
.sim
.get_stats(&ui.state.primary.map)
.canonical_pt_per_trip
.keys()
.cloned()
.collect();
for trip in trips {
if let Some(agent) = ui.state.primary.sim.trip_to_agent(trip) {
if let Some(trace) =
ui.state
.primary
.sim
.trace_route(agent, &ui.state.primary.map, None)
{
traces.push(trace);
}
}
}
RouteViewer::DebugAllRoutes(ui.state.primary.sim.time(), traces)
}

View File

@ -433,7 +433,6 @@ impl PluginsPerMap {
// inactive state.
Box::new(view::show_activity::ShowActivityState::new()),
Box::new(view::show_associated::ShowAssociatedState::new()),
Box::new(view::show_route::ShowRouteState::new()),
Box::new(view::turn_cycler::TurnCyclerState::new()),
],
time_travel: plugins::sim::time_travel::TimeTravel::new(),

View File

@ -138,10 +138,6 @@ impl GUI for UI {
ModalMenu::new("A/B All Trips Explorer", vec![(Key::Enter, "quit")]),
ModalMenu::new("Search", vec![(Key::Enter, "quit")]),
ModalMenu::new("Neighborhood Summaries", vec![(Key::Enter, "quit")]),
ModalMenu::new(
"Agent Route Debugger",
vec![(Key::R, "quit"), (Key::L, "show route for all agents")],
),
ModalMenu::new("Active Traffic Visualizer", vec![(Key::A, "quit")]),
ModalMenu::new("Object Hider", vec![(Key::K, "unhide everything")]),
// TODO F1?
@ -195,10 +191,13 @@ impl GUI for UI {
(Key::U, "load next sim state"),
(Key::Space, "run/pause sim"),
(Key::M, "run one step of sim"),
(Key::R, "reset sim"),
(Key::X, "reset sim"),
(Key::S, "seed the sim with agents"),
// TODO Strange to always have this. Really it's a case of stacked modal?
(Key::F, "stop following agent"),
(Key::R, "stop showing agent's route"),
// TODO This should probably be a debug thing instead
(Key::L, "show/hide route for all agents"),
],
),
ModalMenu::new("Agent Spawner", vec![(Key::Escape, "quit")]),