diff --git a/editor/src/plugins/mod.rs b/editor/src/plugins/mod.rs index 5edea8c5f8..add01f2dc4 100644 --- a/editor/src/plugins/mod.rs +++ b/editor/src/plugins/mod.rs @@ -6,6 +6,7 @@ pub mod geom_validation; pub mod road_editor; pub mod search; pub mod selection; +pub mod show_route; pub mod sim_controls; pub mod steep; pub mod stop_sign_editor; diff --git a/editor/src/plugins/show_route.rs b/editor/src/plugins/show_route.rs new file mode 100644 index 0000000000..b47ab7fd77 --- /dev/null +++ b/editor/src/plugins/show_route.rs @@ -0,0 +1,50 @@ +use colors::{ColorScheme, Colors}; +use ezgui::input::UserInput; +use graphics::types::Color; +use map_model::LaneID; +use piston::input::Key; +use sim::{AgentID, Sim}; +use std::collections::HashSet; + +pub enum ShowRouteState { + Empty, + Active(AgentID, HashSet), +} + +impl ShowRouteState { + pub fn event(&mut self, input: &mut UserInput, sim: &Sim) -> bool { + let quit = match self { + ShowRouteState::Empty => false, + ShowRouteState::Active(agent, ref mut lanes) => { + if input.key_pressed(Key::Return, "stop showing route") { + true + } else { + match sim.get_current_route(*agent) { + Some(route) => { + lanes.clear(); + lanes.extend(route); + false + } + None => true, + } + } + } + }; + if quit { + *self = ShowRouteState::Empty; + } + quit + } + + pub fn color_l(&self, l: LaneID, cs: &ColorScheme) -> Option { + let highlight = match self { + ShowRouteState::Empty => false, + ShowRouteState::Active(_, lanes) => lanes.contains(&l), + }; + if highlight { + Some(cs.get(Colors::Queued)) + } else { + None + } + } +} diff --git a/editor/src/ui.rs b/editor/src/ui.rs index 103803a645..07a14c7dda 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -26,6 +26,7 @@ use plugins::geom_validation::Validator; use plugins::road_editor::RoadEditor; use plugins::search::SearchState; use plugins::selection::{Hider, SelectionState, ID}; +use plugins::show_route::ShowRouteState; use plugins::sim_controls::SimController; use plugins::steep::SteepnessVisualizer; use plugins::stop_sign_editor::StopSignEditor; @@ -34,8 +35,8 @@ use plugins::turn_colors::TurnColors; use plugins::warp::WarpState; use render; use sim; -use sim::{CarID, CarState, PedestrianID}; -use std::collections::HashMap; +use sim::{AgentID, CarID, CarState, PedestrianID}; +use std::collections::{HashMap, HashSet}; use std::process; // TODO ideally these would be tuned kind of dynamically based on rendering speed @@ -65,6 +66,7 @@ pub struct UI { current_search_state: SearchState, warp: WarpState, follow: FollowState, + show_route: ShowRouteState, floodfiller: Floodfiller, steepness_viz: SteepnessVisualizer, osm_classifier: OsmClassifier, @@ -137,6 +139,7 @@ impl UI { current_search_state: SearchState::Empty, warp: WarpState::Empty, follow: FollowState::Empty, + show_route: ShowRouteState::Empty, floodfiller: Floodfiller::new(), osm_classifier: OsmClassifier::new(), traffic_signal_editor: TrafficSignalEditor::new(), @@ -281,6 +284,7 @@ impl UI { // chaining is harder to read. :( vec![ self.current_selection_state.color_l(l, &self.cs), + self.show_route.color_l(l.id, &self.cs), self.current_search_state.color_l(l, &self.map, &self.cs), self.floodfiller.color_l(l, &self.cs), self.steepness_viz.color_l(&self.map, l), @@ -457,6 +461,7 @@ impl gui::GUI for UI { self.follow .event(input, &self.map, &self.sim_ctrl.sim, &mut self.canvas,) ); + stop_if_done!(self.show_route.event(input, &self.sim_ctrl.sim)); stop_if_done!( self.color_picker .handle_event(input, &mut self.canvas, &mut self.cs) @@ -544,12 +549,21 @@ impl gui::GUI for UI { self.follow = FollowState::FollowingCar(id); return gui::EventLoopMode::InputOnly; } + if input.key_pressed(Key::R, "show this car's route") { + self.show_route = ShowRouteState::Active(AgentID::Car(id), HashSet::new()); + return gui::EventLoopMode::InputOnly; + } } SelectionState::SelectedPedestrian(id) => { if input.key_pressed(Key::F, "follow this pedestrian") { self.follow = FollowState::FollowingPedestrian(id); return gui::EventLoopMode::InputOnly; } + if input.key_pressed(Key::R, "show this pedestrian's route") { + self.show_route = + ShowRouteState::Active(AgentID::Pedestrian(id), HashSet::new()); + return gui::EventLoopMode::InputOnly; + } } SelectionState::SelectedLane(id, _) => { if input.key_pressed(Key::F, "start floodfilling from this lane") { diff --git a/sim/src/driving.rs b/sim/src/driving.rs index 1972a80393..cbff6293a0 100644 --- a/sim/src/driving.rs +++ b/sim/src/driving.rs @@ -842,6 +842,10 @@ impl DrivingSimState { } view } + + pub fn get_current_route(&self, id: CarID) -> Option> { + self.routers.get(&id).map(|r| r.get_current_route()) + } } // The immutable view that cars see of other cars. diff --git a/sim/src/router.rs b/sim/src/router.rs index e07444a8cb..4cf0d0917f 100644 --- a/sim/src/router.rs +++ b/sim/src/router.rs @@ -163,6 +163,10 @@ impl Router { self.path.push_back(choice); None } + + pub fn get_current_route(&self) -> Vec { + self.path.iter().map(|id| *id).collect() + } } fn find_parking_spot( diff --git a/sim/src/sim.rs b/sim/src/sim.rs index db23dd0e60..5ae9e8afaa 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -18,7 +18,7 @@ use std::f64; use std::time::{Duration, Instant}; use transit::TransitSimState; use walking::WalkingSimState; -use {CarID, CarState, Event, InvariantViolated, PedestrianID, Tick, TIMESTEP}; +use {AgentID, CarID, CarState, Event, InvariantViolated, PedestrianID, Tick, TIMESTEP}; #[derive(Serialize, Deserialize, Derivative)] #[derivative(PartialEq, Eq)] @@ -414,6 +414,13 @@ impl Sim { )) } } + + pub fn get_current_route(&self, id: AgentID) -> Option> { + match id { + AgentID::Car(car) => self.driving_state.get_current_route(car), + AgentID::Pedestrian(ped) => self.walking_state.get_current_route(ped), + } + } } pub struct Benchmark { diff --git a/sim/src/walking.rs b/sim/src/walking.rs index 183422fee6..0689151ccc 100644 --- a/sim/src/walking.rs +++ b/sim/src/walking.rs @@ -504,6 +504,12 @@ impl WalkingSimState { pub fn is_done(&self) -> bool { self.peds.is_empty() } + + pub fn get_current_route(&self, id: PedestrianID) -> Option> { + self.peds + .get(&id) + .map(|p| p.path.iter().map(|id| *id).collect()) + } } fn is_contraflow(map: &Map, from: LaneID, to: LaneID) -> bool {