From 147901790c6b6c10b5a453d6647a6bac7aa82064 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Wed, 12 Sep 2018 17:39:53 -0700 Subject: [PATCH] first lift sim out of sim ctrl --- docs/design.md | 34 ++++++++++++++- editor/src/plugins/sim_controls.rs | 37 +++++++++-------- editor/src/ui.rs | 66 +++++++++++++++--------------- 3 files changed, 87 insertions(+), 50 deletions(-) diff --git a/docs/design.md b/docs/design.md index f5995316b6..ce9b4605a4 100644 --- a/docs/design.md +++ b/docs/design.md @@ -933,6 +933,38 @@ alright, time to move color logic. let's see what it takes for each Renderable t OK, so where was I? - colors are still currently missing for things that need two of them. -- having one active plugin at a time simplifies the color problem and solves a few others, so try that now. +** having one active plugin at a time simplifies the color problem and solves a few others, so try that now. + - another refactor to do -- initiate plugins based on current_selection_state in UI or the plugin, but stop mixing so much - make car and ped also Renderable, for great consistency! - work on generic quadtree idea + +### One active plugin at a time + +I wonder if the UI will have a need to reach into plugins beyond event(). Let's find out! +- exceptions + - hider needs to given to finding onscreen stuff + - search can specify colors and OSD lines + - warp can add OSD lines + - show_route can color + - floodfiller can color + - steepness can color + - osm can color + - signal and stop sign editor can color and indicate what icons are onscreen + - road editor can be asked for state to serialize + - sim ctrl can contribute OSD lines (and everything grabs sim from it directly too -- maybe sim should live in UI directly) + - color picker can draw + - turn cycler can draw + +- the stuff they take in event() is different. hmm. + - box lil closures + +so it feels like we implicitly have a big enum of active plugin, with each of their states kinda hidden inside. + +- the simple idea + - UI keeps having a bunch of separate plugins with their real type + - have a list of closures that take UI and do event(). return true if that plugin is active + NOT if something was done with the input + - in event(), go through the list and stop when something becomes + active. remember it's active and just call it directly next time in + event(), until it says its no longer active. + - then figure out the implications for color diff --git a/editor/src/plugins/sim_controls.rs b/editor/src/plugins/sim_controls.rs index d491b2559e..f3ef398942 100644 --- a/editor/src/plugins/sim_controls.rs +++ b/editor/src/plugins/sim_controls.rs @@ -4,14 +4,12 @@ use control::ControlMap; use ezgui::UserInput; use map_model::Map; use piston::input::{Key, UpdateEvent}; -use sim; -use sim::{Benchmark, Sim}; +use sim::{Benchmark, Sim, TIMESTEP}; use std::time::{Duration, Instant}; const ADJUST_SPEED: f64 = 0.1; pub struct SimController { - pub sim: Sim, desired_speed: f64, // sim seconds per real second // If None, then the sim is paused last_step: Option, @@ -20,9 +18,8 @@ pub struct SimController { } impl SimController { - pub fn new(sim: Sim) -> SimController { + pub fn new() -> SimController { SimController { - sim, desired_speed: 1.0, last_step: None, benchmark: None, @@ -31,7 +28,13 @@ impl SimController { } // true if the sim is running - pub fn event(&mut self, input: &mut UserInput, map: &Map, control_map: &ControlMap) -> bool { + pub fn event( + &mut self, + input: &mut UserInput, + map: &Map, + control_map: &ControlMap, + sim: &mut Sim, + ) -> bool { if input.unimportant_key_pressed(Key::LeftBracket, "slow down sim") { self.desired_speed -= ADJUST_SPEED; self.desired_speed = self.desired_speed.max(0.0); @@ -40,12 +43,12 @@ impl SimController { self.desired_speed += ADJUST_SPEED; } if input.unimportant_key_pressed(Key::O, "save sim state") { - self.sim.save(); + sim.save(); } if input.unimportant_key_pressed(Key::P, "load sim state") { - match self.sim.load_most_recent() { - Ok(sim) => { - self.sim = sim; + match sim.load_most_recent() { + Ok(new_sim) => { + *sim = new_sim; self.benchmark = None; } Err(e) => println!("Couldn't load savestate: {}", e), @@ -60,9 +63,9 @@ impl SimController { } else { if input.unimportant_key_pressed(Key::Space, "run sim") { self.last_step = Some(Instant::now()); - self.benchmark = Some(self.sim.start_benchmark()); + self.benchmark = Some(sim.start_benchmark()); } else if input.unimportant_key_pressed(Key::M, "run one step") { - self.sim.step(map, control_map); + sim.step(map, control_map); } } @@ -71,14 +74,14 @@ impl SimController { // TODO https://gafferongames.com/post/fix_your_timestep/ let dt = tick.elapsed(); let dt_s = dt.as_secs() as f64 + f64::from(dt.subsec_nanos()) * 1e-9; - if dt_s >= sim::TIMESTEP.value_unsafe / self.desired_speed { - self.sim.step(map, control_map); + if dt_s >= TIMESTEP.value_unsafe / self.desired_speed { + sim.step(map, control_map); self.last_step = Some(Instant::now()); } if let Some(ref mut b) = self.benchmark { if b.has_real_time_passed(Duration::from_secs(1)) { - self.sim_speed = format!("{0:.2}x", self.sim.measure_speed(b)); + self.sim_speed = format!("{0:.2}x", sim.measure_speed(b)); } } } @@ -86,9 +89,9 @@ impl SimController { self.last_step.is_some() } - pub fn get_osd_lines(&self) -> Vec { + pub fn get_osd_lines(&self, sim: &Sim) -> Vec { vec![ - self.sim.summary(), + sim.summary(), format!( "Speed: {0} / desired {1:.2}x", self.sim_speed, self.desired_speed diff --git a/editor/src/ui.rs b/editor/src/ui.rs index aeb46f4b25..2c8a2d90d0 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -34,7 +34,7 @@ use plugins::warp::WarpState; use render; use render::Renderable; use sim; -use sim::{AgentID, CarID, CarState, PedestrianID}; +use sim::{AgentID, CarID, CarState, PedestrianID, Sim}; use std::collections::{HashMap, HashSet}; use std::process; @@ -48,6 +48,7 @@ pub struct UI { map: map_model::Map, draw_map: render::DrawMap, control_map: ControlMap, + sim: Sim, show_lanes: ToggleableLayer, show_buildings: ToggleableLayer, @@ -69,7 +70,6 @@ pub struct UI { floodfiller: Floodfiller, steepness_viz: SteepnessVisualizer, osm_classifier: OsmClassifier, - turn_colors: TurnColors, traffic_signal_editor: TrafficSignalEditor, stop_sign_editor: StopSignEditor, road_editor: RoadEditor, @@ -78,6 +78,9 @@ pub struct UI { geom_validator: Validator, turn_cycler: TurnCyclerState, + // Not really a plugin; it doesn't react to anything. + turn_colors: TurnColors, + canvas: Canvas, // TODO maybe never pass this to other places? Always resolve colors here? cs: ColorScheme, @@ -113,15 +116,17 @@ impl UI { let steepness_viz = SteepnessVisualizer::new(&map); let turn_colors = TurnColors::new(&control_map); - let sim_ctrl = SimController::new(sim); let mut ui = UI { + // TODO organize this by section map, draw_map, control_map, + sim, + steepness_viz, turn_colors, - sim_ctrl, + sim_ctrl: SimController::new(), show_lanes: ToggleableLayer::new("lanes", Key::D3, Some(MIN_ZOOM_FOR_LANES)), show_buildings: ToggleableLayer::new("buildings", Key::D1, Some(0.0)), @@ -212,12 +217,12 @@ impl UI { Vec::new() }; for l in &lanes_onscreen { - for c in &self.sim_ctrl.sim.get_draw_cars_on_lane(l.id, &self.map) { + for c in &self.sim.get_draw_cars_on_lane(l.id, &self.map) { if c.contains_pt(pt) { return Some(ID::Car(c.id)); } } - for p in &self.sim_ctrl.sim.get_draw_peds_on_lane(l.id, &self.map) { + for p in &self.sim.get_draw_peds_on_lane(l.id, &self.map) { if p.contains_pt(pt) { return Some(ID::Pedestrian(p.id)); } @@ -235,12 +240,12 @@ impl UI { return Some(ID::Turn(*t)); } - for c in &self.sim_ctrl.sim.get_draw_cars_on_turn(*t, &self.map) { + for c in &self.sim.get_draw_cars_on_turn(*t, &self.map) { if c.contains_pt(pt) { return Some(ID::Car(c.id)); } } - for p in &self.sim_ctrl.sim.get_draw_peds_on_turn(*t, &self.map) { + for p in &self.sim.get_draw_peds_on_turn(*t, &self.map) { if p.contains_pt(pt) { return Some(ID::Pedestrian(p.id)); } @@ -389,7 +394,7 @@ impl UI { return c; } // TODO if it's a bus, color it differently -- but how? :\ - match self.sim_ctrl.sim.get_car_state(id) { + match self.sim.get_car_state(id) { CarState::Debug => shift_color(self.cs.get(Colors::DebugCar), id.0), CarState::Moving => shift_color(self.cs.get(Colors::MovingCar), id.0), CarState::Stuck => shift_color(self.cs.get(Colors::StuckCar), id.0), @@ -460,21 +465,21 @@ impl GUI for UI { &mut self.map, &mut self.draw_map, &self.control_map, - &mut self.sim_ctrl.sim + &mut self.sim )); stop_if_done!(self.current_search_state.event(input)); stop_if_done!(self.warp.event( input, &self.map, - &self.sim_ctrl.sim, + &self.sim, &mut self.canvas, &mut self.current_selection_state, )); stop_if_done!( self.follow - .event(input, &self.map, &self.sim_ctrl.sim, &mut self.canvas,) + .event(input, &self.map, &self.sim, &mut self.canvas,) ); - stop_if_done!(self.show_route.event(input, &self.sim_ctrl.sim)); + stop_if_done!(self.show_route.event(input, &self.sim)); stop_if_done!( self.color_picker .handle_event(input, &mut self.canvas, &mut self.cs) @@ -543,7 +548,7 @@ impl GUI for UI { return EventLoopMode::InputOnly; } if input.unimportant_key_pressed(Key::S, "Seed the map with agents") { - self.sim_ctrl.sim.small_spawn(&self.map); + self.sim.small_spawn(&self.map); return EventLoopMode::InputOnly; } @@ -552,11 +557,11 @@ impl GUI for UI { // 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_ctrl.sim.toggle_debug(id); + self.sim.toggle_debug(id); return EventLoopMode::InputOnly; } if input.key_pressed(Key::A, "start this parked car") { - self.sim_ctrl.sim.start_parked_car(&self.map, id); + self.sim.start_parked_car(&self.map, id); return EventLoopMode::InputOnly; } if input.key_pressed(Key::F, "follow this car") { @@ -588,7 +593,7 @@ impl GUI for UI { if self.map.get_l(id).is_sidewalk() && input.key_pressed(Key::A, "spawn a pedestrian here") { - self.sim_ctrl.sim.spawn_pedestrian(&self.map, id); + self.sim.spawn_pedestrian(&self.map, id); return EventLoopMode::InputOnly; } } @@ -613,7 +618,7 @@ impl GUI for UI { stop_if_done!(self.current_selection_state.event( input, &self.map, - &mut self.sim_ctrl.sim, + &mut self.sim, &self.control_map )); @@ -636,7 +641,9 @@ impl GUI for UI { } // Sim controller plugin is kind of always active? If nothing else ran, let it use keys. - if self.sim_ctrl.event(input, &self.map, &self.control_map) { + if self.sim_ctrl + .event(input, &self.map, &self.control_map, &mut self.sim) + { EventLoopMode::Animation } else { EventLoopMode::InputOnly @@ -683,10 +690,10 @@ impl GUI for UI { .get_t(*t) .draw(g, self.color_turn_icon(*t), &self.cs); } - for c in &self.sim_ctrl.sim.get_draw_cars_on_turn(*t, &self.map) { + for c in &self.sim.get_draw_cars_on_turn(*t, &self.map) { c.draw(g, self.color_car(c.id)); } - for p in &self.sim_ctrl.sim.get_draw_peds_on_turn(*t, &self.map) { + for p in &self.sim.get_draw_peds_on_turn(*t, &self.map) { p.draw(g, self.color_ped(p.id)); } } @@ -716,10 +723,10 @@ impl GUI for UI { } for l in &lanes_onscreen { - for c in &self.sim_ctrl.sim.get_draw_cars_on_lane(l.id, &self.map) { + for c in &self.sim.get_draw_cars_on_lane(l.id, &self.map) { c.draw(g, self.color_car(c.id)); } - for p in &self.sim_ctrl.sim.get_draw_peds_on_lane(l.id, &self.map) { + for p in &self.sim.get_draw_peds_on_lane(l.id, &self.map) { p.draw(g, self.color_ped(p.id)); } } @@ -743,21 +750,16 @@ impl GUI for UI { &self.map, &self.draw_map, &self.control_map, - &self.sim_ctrl.sim, + &self.sim, &self.cs, g, ); - self.current_selection_state.draw( - &self.map, - &self.canvas, - &self.draw_map, - &self.sim_ctrl.sim, - g, - ); + self.current_selection_state + .draw(&self.map, &self.canvas, &self.draw_map, &self.sim, g); self.color_picker.draw(&self.canvas, g); - let mut osd_lines = self.sim_ctrl.get_osd_lines(); + let mut osd_lines = self.sim_ctrl.get_osd_lines(&self.sim); let action_lines = input.get_possible_actions(); if !action_lines.is_empty() { osd_lines.push(String::from(""));