mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-28 08:53:26 +03:00
making more plugins initiate themselves, instead of UI doing it
This commit is contained in:
parent
853665406b
commit
a059bccfd5
@ -900,7 +900,7 @@ have to add?
|
||||
- a quadtree and way to get onscreen stuff
|
||||
- UI
|
||||
- a toggleablelayer for it
|
||||
- and clearing selection state maybe
|
||||
= and clearing selection state maybe
|
||||
- are we mouseover it? (right order)
|
||||
- draw it (right order)
|
||||
- pick the color for it
|
||||
@ -976,5 +976,11 @@ so it feels like we implicitly have a big enum of active plugin, with each of th
|
||||
= refactor the toggleablelayer stuff, then move them to list of plugins too
|
||||
= clean up selection state... should warp and hider be able to modify it, or just rerun mouseover_something?
|
||||
|
||||
- initiate plugins in the plugin's event; stop doing stuff directly in UI
|
||||
= initiate plugins in the plugin's event; stop doing stuff directly in UI
|
||||
- basically, make UI.event() just the active plugin list thing as much as possible.
|
||||
- deal with overlapping keys that still kinda happen (sim ctrl, escape game)
|
||||
- bug: do need to recalculate current_selection whenever anything potentially changes camera, like follow
|
||||
|
||||
- then rethink colors, with simplified single plugin
|
||||
- then finally try out a unified quadtree!
|
||||
- and see how much boilerplate a new type would need.
|
||||
|
@ -91,7 +91,9 @@ fn debug(id: &ID, map: &Map, control_map: &ControlMap, sim: &mut Sim) {
|
||||
ID::Building(id) => {
|
||||
map.get_b(*id).dump_debug();
|
||||
}
|
||||
ID::Car(_) => {}
|
||||
ID::Car(id) => {
|
||||
sim.debug_car(*id);
|
||||
}
|
||||
ID::Pedestrian(id) => {
|
||||
sim.debug_ped(*id);
|
||||
}
|
||||
|
@ -4,10 +4,12 @@ use colors::{ColorScheme, Colors};
|
||||
use ezgui::UserInput;
|
||||
use graphics::types::Color;
|
||||
use map_model::{Lane, LaneID, Map};
|
||||
use objects::ID;
|
||||
use piston::input::Key;
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
// Keeps track of state so this can be interactively visualized
|
||||
#[derive(PartialEq)]
|
||||
pub enum Floodfiller {
|
||||
Inactive,
|
||||
Active {
|
||||
@ -24,7 +26,7 @@ impl Floodfiller {
|
||||
|
||||
// TODO doesn't guarantee all visited lanes are connected? are dead-ends possible with the
|
||||
// current turn definitions?
|
||||
pub fn start(start: LaneID) -> Floodfiller {
|
||||
fn start(start: LaneID) -> Floodfiller {
|
||||
let mut queue = VecDeque::new();
|
||||
queue.push_back(start);
|
||||
Floodfiller::Active {
|
||||
@ -35,7 +37,19 @@ impl Floodfiller {
|
||||
|
||||
// TODO step backwards!
|
||||
|
||||
pub fn event(&mut self, map: &Map, input: &mut UserInput) -> bool {
|
||||
pub fn event(&mut self, map: &Map, input: &mut UserInput, selected: Option<ID>) -> bool {
|
||||
if *self == Floodfiller::Inactive {
|
||||
match selected {
|
||||
Some(ID::Lane(id)) => {
|
||||
if input.key_pressed(Key::F, "start floodfilling from this lane") {
|
||||
*self = Floodfiller::start(id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_state: Option<Floodfiller> = None;
|
||||
match self {
|
||||
Floodfiller::Inactive => {}
|
||||
|
@ -1,8 +1,10 @@
|
||||
use ezgui::{Canvas, UserInput};
|
||||
use map_model::Map;
|
||||
use objects::ID;
|
||||
use piston::input::Key;
|
||||
use sim::{CarID, PedestrianID, Sim};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum FollowState {
|
||||
Empty,
|
||||
FollowingCar(CarID),
|
||||
@ -16,7 +18,26 @@ impl FollowState {
|
||||
map: &Map,
|
||||
sim: &Sim,
|
||||
canvas: &mut Canvas,
|
||||
selected: Option<ID>,
|
||||
) -> bool {
|
||||
if *self == FollowState::Empty {
|
||||
match selected {
|
||||
Some(ID::Car(id)) => {
|
||||
if input.key_pressed(Key::F, "follow this car") {
|
||||
*self = FollowState::FollowingCar(id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some(ID::Pedestrian(id)) => {
|
||||
if input.key_pressed(Key::F, "follow this pedestrian") {
|
||||
*self = FollowState::FollowingPedestrian(id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let quit = match self {
|
||||
FollowState::Empty => false,
|
||||
// TODO be generic and take an AgentID
|
||||
|
@ -5,7 +5,7 @@ use geo::prelude::Intersects;
|
||||
use geom::{Polygon, Pt2D};
|
||||
use map_model::{geometry, BuildingID, IntersectionID, LaneID, Map, ParcelID};
|
||||
use piston::input::Key;
|
||||
use render;
|
||||
use render::DrawMap;
|
||||
|
||||
// TODO just have one of these
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||
@ -31,7 +31,7 @@ impl Validator {
|
||||
Validator::Inactive
|
||||
}
|
||||
|
||||
pub fn start(draw_map: &render::DrawMap) -> Validator {
|
||||
fn start(draw_map: &DrawMap) -> Validator {
|
||||
let mut objects: Vec<(ID, Vec<geo::Polygon<f64>>)> = Vec::new();
|
||||
for l in &draw_map.lanes {
|
||||
objects.push((ID::Lane(l.id), make_polys(&l.polygon)));
|
||||
@ -88,10 +88,20 @@ impl Validator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(&mut self, input: &mut UserInput, canvas: &mut Canvas, map: &Map) -> bool {
|
||||
pub fn event(
|
||||
&mut self,
|
||||
input: &mut UserInput,
|
||||
canvas: &mut Canvas,
|
||||
map: &Map,
|
||||
draw_map: &DrawMap,
|
||||
) -> bool {
|
||||
let mut new_state: Option<Validator> = None;
|
||||
match self {
|
||||
Validator::Inactive => {}
|
||||
Validator::Inactive => {
|
||||
if input.unimportant_key_pressed(Key::I, "Validate map geometry") {
|
||||
new_state = Some(Validator::start(draw_map));
|
||||
}
|
||||
}
|
||||
Validator::Active {
|
||||
gen,
|
||||
current_problem,
|
||||
|
@ -2,17 +2,37 @@ use colors::{ColorScheme, Colors};
|
||||
use ezgui::UserInput;
|
||||
use graphics::types::Color;
|
||||
use map_model::LaneID;
|
||||
use objects::ID;
|
||||
use piston::input::Key;
|
||||
use sim::{AgentID, Sim};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum ShowRouteState {
|
||||
Empty,
|
||||
Active(AgentID, HashSet<LaneID>),
|
||||
}
|
||||
|
||||
impl ShowRouteState {
|
||||
pub fn event(&mut self, input: &mut UserInput, sim: &Sim) -> bool {
|
||||
pub fn event(&mut self, input: &mut UserInput, sim: &Sim, selected: Option<ID>) -> bool {
|
||||
if *self == ShowRouteState::Empty {
|
||||
match selected {
|
||||
Some(ID::Car(id)) => {
|
||||
if input.key_pressed(Key::R, "show this car's route") {
|
||||
*self = ShowRouteState::Active(AgentID::Car(id), HashSet::new());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some(ID::Pedestrian(id)) => {
|
||||
if input.key_pressed(Key::R, "show this pedestrian's route") {
|
||||
*self = ShowRouteState::Active(AgentID::Pedestrian(id), HashSet::new());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let quit = match self {
|
||||
ShowRouteState::Empty => false,
|
||||
ShowRouteState::Active(agent, ref mut lanes) => {
|
||||
|
@ -3,6 +3,7 @@
|
||||
use control::ControlMap;
|
||||
use ezgui::{EventLoopMode, UserInput};
|
||||
use map_model::Map;
|
||||
use objects::ID;
|
||||
use piston::input::{Key, UpdateEvent};
|
||||
use sim::{Benchmark, Sim, TIMESTEP};
|
||||
use std::time::{Duration, Instant};
|
||||
@ -33,7 +34,11 @@ impl SimController {
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
sim: &mut Sim,
|
||||
selected: Option<ID>,
|
||||
) -> EventLoopMode {
|
||||
if input.unimportant_key_pressed(Key::S, "Seed the map with agents") {
|
||||
sim.small_spawn(map);
|
||||
}
|
||||
if input.unimportant_key_pressed(Key::LeftBracket, "slow down sim") {
|
||||
self.desired_speed -= ADJUST_SPEED;
|
||||
self.desired_speed = self.desired_speed.max(0.0);
|
||||
@ -68,6 +73,22 @@ impl SimController {
|
||||
}
|
||||
}
|
||||
|
||||
match selected {
|
||||
Some(ID::Car(id)) => {
|
||||
if input.key_pressed(Key::A, "start this parked car") {
|
||||
sim.start_parked_car(map, id);
|
||||
}
|
||||
}
|
||||
Some(ID::Lane(id)) => {
|
||||
if map.get_l(id).is_sidewalk()
|
||||
&& input.key_pressed(Key::A, "spawn a pedestrian here")
|
||||
{
|
||||
sim.spawn_pedestrian(map, id);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if input.use_event_directly().update_args().is_some() {
|
||||
if let Some(tick) = self.last_step {
|
||||
// TODO https://gafferongames.com/post/fix_your_timestep/
|
||||
|
@ -10,6 +10,7 @@ use map_model::{Map, Turn};
|
||||
use objects::ID;
|
||||
use piston::input::Key;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum StopSignEditor {
|
||||
Inactive,
|
||||
Active(IntersectionID),
|
||||
@ -20,10 +21,6 @@ impl StopSignEditor {
|
||||
StopSignEditor::Inactive
|
||||
}
|
||||
|
||||
pub fn start(i: IntersectionID) -> StopSignEditor {
|
||||
StopSignEditor::Active(i)
|
||||
}
|
||||
|
||||
pub fn event(
|
||||
&mut self,
|
||||
input: &mut UserInput,
|
||||
@ -31,6 +28,20 @@ impl StopSignEditor {
|
||||
control_map: &mut ControlMap,
|
||||
selected: Option<ID>,
|
||||
) -> bool {
|
||||
if *self == StopSignEditor::Inactive {
|
||||
match selected {
|
||||
Some(ID::Intersection(id)) => {
|
||||
if control_map.stop_signs.contains_key(&id)
|
||||
&& input.key_pressed(Key::E, &format!("edit stop signs for {}", id))
|
||||
{
|
||||
*self = StopSignEditor::Active(id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_state: Option<StopSignEditor> = None;
|
||||
match self {
|
||||
StopSignEditor::Inactive => {}
|
||||
|
@ -11,6 +11,7 @@ use map_model::{IntersectionID, Turn};
|
||||
use objects::ID;
|
||||
use piston::input::Key;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum TrafficSignalEditor {
|
||||
Inactive,
|
||||
Active {
|
||||
@ -24,13 +25,6 @@ impl TrafficSignalEditor {
|
||||
TrafficSignalEditor::Inactive
|
||||
}
|
||||
|
||||
pub fn start(i: IntersectionID) -> TrafficSignalEditor {
|
||||
TrafficSignalEditor::Active {
|
||||
i,
|
||||
current_cycle: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(
|
||||
&mut self,
|
||||
input: &mut UserInput,
|
||||
@ -38,6 +32,23 @@ impl TrafficSignalEditor {
|
||||
control_map: &mut ControlMap,
|
||||
selected: Option<ID>,
|
||||
) -> bool {
|
||||
if *self == TrafficSignalEditor::Inactive {
|
||||
match selected {
|
||||
Some(ID::Intersection(id)) => {
|
||||
if control_map.traffic_signals.contains_key(&id)
|
||||
&& input.key_pressed(Key::E, &format!("edit traffic signal for {}", id))
|
||||
{
|
||||
*self = TrafficSignalEditor::Active {
|
||||
i: id,
|
||||
current_cycle: 0,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_state: Option<TrafficSignalEditor> = None;
|
||||
match self {
|
||||
TrafficSignalEditor::Inactive => {}
|
||||
|
125
editor/src/ui.rs
125
editor/src/ui.rs
@ -36,8 +36,8 @@ use plugins::warp::WarpState;
|
||||
use render;
|
||||
use render::Renderable;
|
||||
use sim;
|
||||
use sim::{AgentID, CarID, CarState, PedestrianID, Sim};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use sim::{CarID, CarState, PedestrianID, Sim};
|
||||
use std::collections::HashMap;
|
||||
use std::process;
|
||||
|
||||
// TODO ideally these would be tuned kind of dynamically based on rendering speed
|
||||
@ -128,7 +128,7 @@ impl UIWrapper {
|
||||
|
||||
hider: Hider::new(),
|
||||
debug_objects: DebugObjectsState::new(),
|
||||
current_search_state: SearchState::Empty,
|
||||
search_state: SearchState::Empty,
|
||||
warp: WarpState::Empty,
|
||||
follow: FollowState::Empty,
|
||||
show_route: ShowRouteState::Empty,
|
||||
@ -196,7 +196,7 @@ impl UIWrapper {
|
||||
&mut ui.sim,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input| ui.current_search_state.event(input)),
|
||||
Box::new(|ui, input| ui.search_state.event(input)),
|
||||
Box::new(|ui, input| {
|
||||
ui.warp.event(
|
||||
input,
|
||||
@ -206,8 +206,16 @@ impl UIWrapper {
|
||||
&mut ui.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input| ui.follow.event(input, &ui.map, &ui.sim, &mut ui.canvas)),
|
||||
Box::new(|ui, input| ui.show_route.event(input, &ui.sim)),
|
||||
Box::new(|ui, input| {
|
||||
ui.follow.event(
|
||||
input,
|
||||
&ui.map,
|
||||
&ui.sim,
|
||||
&mut ui.canvas,
|
||||
ui.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input| ui.show_route.event(input, &ui.sim, ui.current_selection)),
|
||||
Box::new(|ui, input| ui.color_picker.event(input, &mut ui.canvas, &mut ui.cs)),
|
||||
Box::new(|ui, input| ui.steepness_viz.event(input)),
|
||||
Box::new(|ui, input| ui.osm_classifier.event(input)),
|
||||
@ -221,8 +229,11 @@ impl UIWrapper {
|
||||
&ui.control_map,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input| ui.floodfiller.event(&ui.map, input)),
|
||||
Box::new(|ui, input| ui.geom_validator.event(input, &mut ui.canvas, &ui.map)),
|
||||
Box::new(|ui, input| ui.floodfiller.event(&ui.map, input, ui.current_selection)),
|
||||
Box::new(|ui, input| {
|
||||
ui.geom_validator
|
||||
.event(input, &mut ui.canvas, &ui.map, &ui.draw_map)
|
||||
}),
|
||||
Box::new(|ui, input| ui.turn_cycler.event(input, ui.current_selection)),
|
||||
],
|
||||
}
|
||||
@ -247,7 +258,7 @@ struct UI {
|
||||
|
||||
hider: Hider,
|
||||
debug_objects: DebugObjectsState,
|
||||
current_search_state: SearchState,
|
||||
search_state: SearchState,
|
||||
warp: WarpState,
|
||||
follow: FollowState,
|
||||
show_route: ShowRouteState,
|
||||
@ -387,7 +398,7 @@ impl UI {
|
||||
vec![
|
||||
self.color_for_selected(ID::Lane(l.id)),
|
||||
self.show_route.color_l(l.id, &self.cs),
|
||||
self.current_search_state.color_l(l, &self.map, &self.cs),
|
||||
self.search_state.color_l(l, &self.map, &self.cs),
|
||||
self.floodfiller.color_l(l, &self.cs),
|
||||
self.steepness_viz.color_l(&self.map, l),
|
||||
self.osm_classifier.color_l(l, &self.map, &self.cs),
|
||||
@ -438,7 +449,7 @@ impl UI {
|
||||
let b = self.map.get_b(id);
|
||||
vec![
|
||||
self.color_for_selected(ID::Building(b.id)),
|
||||
self.current_search_state.color_b(b, &self.cs),
|
||||
self.search_state.color_b(b, &self.cs),
|
||||
self.osm_classifier.color_b(b, &self.cs),
|
||||
].iter()
|
||||
.filter_map(|c| *c)
|
||||
@ -561,90 +572,9 @@ impl UI {
|
||||
};
|
||||
if layer_changed {
|
||||
self.current_selection = self.mouseover_something();
|
||||
// TODO is it even necessary to update this here? shouldnt all plugins potentilly need
|
||||
// to know? dont we need to also do this when warp/follow/other things potentially move
|
||||
// stuff?
|
||||
self.debug_objects.event(
|
||||
self.current_selection,
|
||||
input,
|
||||
&self.map,
|
||||
&mut self.sim,
|
||||
&self.control_map,
|
||||
);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
if input.unimportant_key_pressed(Key::I, "Validate map geometry") {
|
||||
self.geom_validator = Validator::start(&self.draw_map);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
if input.unimportant_key_pressed(Key::S, "Seed the map with agents") {
|
||||
self.sim.small_spawn(&self.map);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
match self.current_selection {
|
||||
Some(ID::Car(id)) => {
|
||||
// TODO not sure if we should debug like this (pushing the bit down to all the
|
||||
// layers representing an entity) or by using some scary global mutable singleton
|
||||
if input.unimportant_key_pressed(Key::D, "debug") {
|
||||
self.sim.toggle_debug(id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
if input.key_pressed(Key::A, "start this parked car") {
|
||||
self.sim.start_parked_car(&self.map, id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
if input.key_pressed(Key::F, "follow this car") {
|
||||
self.follow = FollowState::FollowingCar(id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
if input.key_pressed(Key::R, "show this car's route") {
|
||||
self.show_route = ShowRouteState::Active(AgentID::Car(id), HashSet::new());
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
Some(ID::Pedestrian(id)) => {
|
||||
if input.key_pressed(Key::F, "follow this pedestrian") {
|
||||
self.follow = FollowState::FollowingPedestrian(id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
if input.key_pressed(Key::R, "show this pedestrian's route") {
|
||||
self.show_route =
|
||||
ShowRouteState::Active(AgentID::Pedestrian(id), HashSet::new());
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
Some(ID::Lane(id)) => {
|
||||
if input.key_pressed(Key::F, "start floodfilling from this lane") {
|
||||
self.floodfiller = Floodfiller::start(id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
if self.map.get_l(id).is_sidewalk()
|
||||
&& input.key_pressed(Key::A, "spawn a pedestrian here")
|
||||
{
|
||||
self.sim.spawn_pedestrian(&self.map, id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
Some(ID::Intersection(id)) => {
|
||||
if self.control_map.traffic_signals.contains_key(&id) {
|
||||
if input.key_pressed(Key::E, &format!("edit traffic signal for {:?}", id)) {
|
||||
self.traffic_signal_editor = TrafficSignalEditor::start(id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
if self.control_map.stop_signs.contains_key(&id) {
|
||||
if input.key_pressed(Key::E, &format!("edit stop sign for {:?}", id)) {
|
||||
self.stop_sign_editor = StopSignEditor::start(id);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if input.unimportant_key_pressed(Key::Escape, "quit") {
|
||||
let state = EditorState {
|
||||
map_name: self.map.get_name().clone(),
|
||||
@ -664,8 +594,13 @@ impl UI {
|
||||
}
|
||||
|
||||
// Sim controller plugin is kind of always active? If nothing else ran, let it use keys.
|
||||
self.sim_ctrl
|
||||
.event(input, &self.map, &self.control_map, &mut self.sim)
|
||||
self.sim_ctrl.event(
|
||||
input,
|
||||
&self.map,
|
||||
&self.control_map,
|
||||
&mut self.sim,
|
||||
self.current_selection,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, input: UserInput) {
|
||||
@ -780,7 +715,7 @@ impl UI {
|
||||
osd_lines.push(String::from(""));
|
||||
osd_lines.extend(action_lines);
|
||||
}
|
||||
let search_lines = self.current_search_state.get_osd_lines();
|
||||
let search_lines = self.search_state.get_osd_lines();
|
||||
if !search_lines.is_empty() {
|
||||
osd_lines.push(String::from(""));
|
||||
osd_lines.extend(search_lines);
|
||||
|
@ -296,7 +296,7 @@ impl Sim {
|
||||
.unwrap_or(vec![format!("{} is parked", car)])
|
||||
}
|
||||
|
||||
pub fn toggle_debug(&mut self, id: CarID) {
|
||||
pub fn debug_car(&mut self, id: CarID) {
|
||||
self.driving_state.toggle_debug(id);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user