making more plugins initiate themselves, instead of UI doing it

This commit is contained in:
Dustin Carlino 2018-09-13 11:33:22 -07:00
parent 853665406b
commit a059bccfd5
11 changed files with 168 additions and 117 deletions

View File

@ -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.

View File

@ -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);
}

View File

@ -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 => {}

View File

@ -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

View File

@ -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,

View File

@ -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) => {

View File

@ -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/

View File

@ -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 => {}

View File

@ -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 => {}

View File

@ -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);

View File

@ -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);
}