From 0c6a93d38f2c3ad26d076fd8617e49e9649c779d Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Tue, 4 Jun 2019 09:28:45 -0700 Subject: [PATCH] introducing a basic slider to adjust speed of all route viz player and sim. code duplication. --- editor/src/mission/all_trips.rs | 57 ++++++++++++++++++++------------- editor/src/sandbox/mod.rs | 37 ++++++++++++--------- ezgui/src/widgets/slider.rs | 44 ++++++++++++++++--------- popdat/Cargo.toml | 1 + popdat/src/main.rs | 15 +++++++++ 5 files changed, 101 insertions(+), 53 deletions(-) diff --git a/editor/src/mission/all_trips.rs b/editor/src/mission/all_trips.rs index 7d78775dc2..749ac6df2d 100644 --- a/editor/src/mission/all_trips.rs +++ b/editor/src/mission/all_trips.rs @@ -3,7 +3,8 @@ use crate::mission::trips::{clip_trips, Trip}; use crate::ui::{ShowEverything, UI}; use abstutil::{elapsed_seconds, prettyprint_usize}; use ezgui::{ - hotkey, Color, EventCtx, EventLoopMode, GeomBatch, GfxCtx, Key, ModalMenu, Slider, Text, + hotkey, Color, EventCtx, EventLoopMode, GeomBatch, GfxCtx, Key, ModalMenu, ScreenPt, Slider, + Text, }; use geom::{Circle, Distance, Duration}; use map_model::{PathRequest, LANE_THICKNESS}; @@ -11,14 +12,16 @@ use popdat::psrc::Mode; use std::time::Instant; const ADJUST_SPEED: f64 = 0.1; +// TODO hardcoded cap for now... +const SPEED_CAP: f64 = 60.0; pub struct TripsVisualizer { menu: ModalMenu, trips: Vec, - slider: Slider, + time_slider: Slider, + speed_slider: Slider, active_trips: Vec, - desired_speed: f64, running: Option, } @@ -60,6 +63,9 @@ impl TripsVisualizer { }); // TODO It'd be awesome to use the generic timer controls for this + // TODO hardcoding placement... + let mut speed_slider = Slider::new(Some(ScreenPt::new(500.0, 0.0))); + speed_slider.set_percent(ctx, 1.0 / SPEED_CAP); TripsVisualizer { menu: ModalMenu::new( "Trips Visualizer", @@ -77,21 +83,22 @@ impl TripsVisualizer { ], ctx, ), - desired_speed: 1.0, running: None, trips, - slider: Slider::new(), + time_slider: Slider::new(None), + speed_slider, active_trips: Vec::new(), } } fn current_time(&self) -> Duration { - self.slider.get_percent() * Duration::parse("23:59:59.9").unwrap() + self.time_slider.get_percent() * Duration::parse("23:59:59.9").unwrap() } // Returns None if the we're done pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Option { let time = self.current_time(); + let desired_speed = self.speed_slider.get_percent() * SPEED_CAP; let mut txt = Text::prompt("Trips Visualizer"); txt.add_line(format!( @@ -99,7 +106,7 @@ impl TripsVisualizer { prettyprint_usize(self.active_trips.len()) )); txt.add_line(format!("At {}", time)); - txt.add_line(format!("Speed: {:.2}x", self.desired_speed)); + txt.add_line(format!("Speed: {:.2}x", desired_speed)); if self.running.is_some() { txt.add_line("Playing".to_string()); } else { @@ -115,11 +122,14 @@ impl TripsVisualizer { let ten_secs = Duration::seconds(10.0); let thirty_mins = Duration::minutes(30); - if self.menu.action("speed up") { - self.desired_speed += ADJUST_SPEED; - } else if self.menu.action("slow down") { - self.desired_speed -= ADJUST_SPEED; - self.desired_speed = self.desired_speed.max(0.0); + if desired_speed != SPEED_CAP && self.menu.action("speed up") { + self.speed_slider + .set_percent(ctx, ((desired_speed + ADJUST_SPEED) / SPEED_CAP).min(1.0)); + } else if desired_speed != 0.0 && self.menu.action("slow down") { + self.speed_slider + .set_percent(ctx, ((desired_speed - ADJUST_SPEED) / SPEED_CAP).max(0.0)); + } else if self.speed_slider.event(ctx) { + // Keep going } else if self.menu.action("pause/resume") { if self.running.is_some() { self.running = None; @@ -131,26 +141,28 @@ impl TripsVisualizer { if self.menu.action("quit") { return None; } else if time != last_time && self.menu.action("forwards 10 seconds") { - self.slider.set_percent(ctx, (time + ten_secs) / last_time); + self.time_slider + .set_percent(ctx, (time + ten_secs) / last_time); } else if time + thirty_mins <= last_time && self.menu.action("forwards 30 minutes") { - self.slider + self.time_slider .set_percent(ctx, (time + thirty_mins) / last_time); } else if time != Duration::ZERO && self.menu.action("backwards 10 seconds") { - self.slider.set_percent(ctx, (time - ten_secs) / last_time); + self.time_slider + .set_percent(ctx, (time - ten_secs) / last_time); } else if time - thirty_mins >= Duration::ZERO && self.menu.action("backwards 30 minutes") { - self.slider + self.time_slider .set_percent(ctx, (time - thirty_mins) / last_time); } else if time != Duration::ZERO && self.menu.action("goto start of day") { - self.slider.set_percent(ctx, 0.0); + self.time_slider.set_percent(ctx, 0.0); } else if time != last_time && self.menu.action("goto end of day") { - self.slider.set_percent(ctx, 1.0); - } else if self.slider.event(ctx) { + self.time_slider.set_percent(ctx, 1.0); + } else if self.time_slider.event(ctx) { // Value changed, fall-through } else if let Some(last_step) = self.running { if ctx.input.nonblocking_is_update_event() { ctx.input.use_update_event(); - let dt = Duration::seconds(elapsed_seconds(last_step)) * self.desired_speed; - self.slider + let dt = Duration::seconds(elapsed_seconds(last_step)) * desired_speed; + self.time_slider .set_percent(ctx, ((time + dt) / last_time).min(1.0)); self.running = Some(Instant::now()); // Value changed, fall-through @@ -207,7 +219,8 @@ impl TripsVisualizer { batch.draw(g); self.menu.draw(g); - self.slider.draw(g); + self.time_slider.draw(g); + self.speed_slider.draw(g); CommonState::draw_osd(g, ui, ui.primary.current_selection); } } diff --git a/editor/src/sandbox/mod.rs b/editor/src/sandbox/mod.rs index 3ef1942b2d..8f19e9a3f6 100644 --- a/editor/src/sandbox/mod.rs +++ b/editor/src/sandbox/mod.rs @@ -11,15 +11,17 @@ use crate::game::{GameState, Mode}; use crate::render::DrawOptions; use crate::ui::ShowEverything; use abstutil::elapsed_seconds; -use ezgui::{hotkey, lctrl, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard}; +use ezgui::{hotkey, lctrl, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Slider, Text, Wizard}; use geom::Duration; use sim::{Benchmark, Sim, TripID}; use std::time::Instant; const ADJUST_SPEED: f64 = 0.1; +// TODO hardcoded cap for now... +const SPEED_CAP: f64 = 60.0; pub struct SandboxMode { - desired_speed: f64, // sim seconds per real second + speed_slider: Slider, following: Option, route_viewer: route_viewer::RouteViewer, show_activity: show_activity::ShowActivity, @@ -44,8 +46,10 @@ enum State { impl SandboxMode { pub fn new(ctx: &mut EventCtx) -> SandboxMode { + let mut speed_slider = Slider::new(None); + speed_slider.set_percent(ctx, 1.0 / SPEED_CAP); SandboxMode { - desired_speed: 1.0, + speed_slider, state: State::Paused, following: None, route_viewer: route_viewer::RouteViewer::Inactive, @@ -109,18 +113,17 @@ impl SandboxMode { return EventLoopMode::InputOnly; } + let desired_speed = mode.speed_slider.get_percent() * SPEED_CAP; + let mut txt = Text::prompt("Sandbox Mode"); txt.add_line(state.ui.primary.sim.summary()); if let State::Running { ref speed, .. } = mode.state { txt.add_line(format!( "Speed: {0} / desired {1:.2}x", - speed, mode.desired_speed + speed, desired_speed )); } else { - txt.add_line(format!( - "Speed: paused / desired {0:.2}x", - mode.desired_speed - )); + txt.add_line(format!("Speed: paused / desired {0:.2}x", desired_speed)); } if let Some(trip) = mode.following { txt.add_line(format!("Following {}", trip)); @@ -219,12 +222,14 @@ impl SandboxMode { return EventLoopMode::InputOnly; } - if mode.menu.action("slow down sim") { - mode.desired_speed -= ADJUST_SPEED; - mode.desired_speed = mode.desired_speed.max(0.0); - } - if mode.menu.action("speed up sim") { - mode.desired_speed += ADJUST_SPEED; + if desired_speed != SPEED_CAP && mode.menu.action("speed up sim") { + mode.speed_slider + .set_percent(ctx, ((desired_speed + ADJUST_SPEED) / SPEED_CAP).min(1.0)); + } else if desired_speed != 0.0 && mode.menu.action("slow down sim") { + mode.speed_slider + .set_percent(ctx, ((desired_speed - ADJUST_SPEED) / SPEED_CAP).max(0.0)); + } else if mode.speed_slider.event(ctx) { + // Keep going } match mode.state { @@ -308,8 +313,7 @@ impl SandboxMode { mode.state = State::Paused; } else if ctx.input.nonblocking_is_update_event() { ctx.input.use_update_event(); - let dt = - Duration::seconds(elapsed_seconds(*last_step)) * mode.desired_speed; + let dt = Duration::seconds(elapsed_seconds(*last_step)) * desired_speed; state.ui.primary.sim.step(&state.ui.primary.map, dt); //*ctx.recalculate_current_selection = true; @@ -365,6 +369,7 @@ impl SandboxMode { mode.route_viewer.draw(g, &state.ui); mode.show_activity.draw(g, &state.ui); mode.menu.draw(g); + mode.speed_slider.draw(g); } }, _ => unreachable!(), diff --git a/ezgui/src/widgets/slider.rs b/ezgui/src/widgets/slider.rs index c9861bfd84..7fa4f84dbd 100644 --- a/ezgui/src/widgets/slider.rs +++ b/ezgui/src/widgets/slider.rs @@ -1,5 +1,5 @@ use crate::screen_geom::ScreenRectangle; -use crate::{hotkey, Color, EventCtx, GfxCtx, Key, ModalMenu, MultiKey, Text}; +use crate::{hotkey, Color, EventCtx, GfxCtx, Key, ModalMenu, MultiKey, ScreenPt, Text}; use geom::{Distance, Polygon, Pt2D}; // Pixels @@ -12,14 +12,17 @@ const HORIZ_PADDING: f64 = 60.0; const VERT_PADDING: f64 = 20.0; pub struct Slider { + top_left: ScreenPt, current_percent: f64, mouse_on_slider: bool, dragging: bool, } impl Slider { - pub fn new() -> Slider { + // TODO Easier placement options. + pub fn new(top_left_at: Option) -> Slider { Slider { + top_left: top_left_at.unwrap_or_else(|| ScreenPt::new(0.0, 0.0)), current_percent: 0.0, mouse_on_slider: false, dragging: false, @@ -52,7 +55,8 @@ impl Slider { if self.dragging { if ctx.input.get_moved_mouse().is_some() { let percent = - (ctx.canvas.get_cursor_in_screen_space().x - HORIZ_PADDING) / BAR_WIDTH; + (ctx.canvas.get_cursor_in_screen_space().x - HORIZ_PADDING - self.top_left.x) + / BAR_WIDTH; self.current_percent = percent.min(1.0).max(0.0); return true; } @@ -71,13 +75,16 @@ impl Slider { // Did we click somewhere else on the bar? let pt = ctx.canvas.get_cursor_in_screen_space(); if Polygon::rectangle_topleft( - Pt2D::new(HORIZ_PADDING, VERT_PADDING), + Pt2D::new( + HORIZ_PADDING + self.top_left.x, + VERT_PADDING + self.top_left.y, + ), Distance::meters(BAR_WIDTH), Distance::meters(BAR_HEIGHT), ) .contains_pt(Pt2D::new(pt.x, pt.y)) { - let percent = (pt.x - HORIZ_PADDING) / BAR_WIDTH; + let percent = (pt.x - HORIZ_PADDING - self.top_left.x) / BAR_WIDTH; self.current_percent = percent.min(1.0).max(0.0); self.mouse_on_slider = true; self.dragging = true; @@ -96,23 +103,26 @@ impl Slider { g.draw_polygon( Color::grey(0.3), &Polygon::rectangle_topleft( - Pt2D::new(0.0, 0.0), + Pt2D::new(self.top_left.x, self.top_left.y), Distance::meters(BAR_WIDTH + 2.0 * HORIZ_PADDING), Distance::meters(BAR_HEIGHT + 2.0 * VERT_PADDING), ), ); g.canvas.mark_covered_area(ScreenRectangle { - x1: 0.0, - y1: 0.0, - x2: BAR_WIDTH + 2.0 * HORIZ_PADDING, - y2: BAR_HEIGHT + 2.0 * VERT_PADDING, + x1: self.top_left.x, + y1: self.top_left.y, + x2: self.top_left.x + BAR_WIDTH + 2.0 * HORIZ_PADDING, + y2: self.top_left.y + BAR_HEIGHT + 2.0 * VERT_PADDING, }); // The bar g.draw_polygon( Color::WHITE, &Polygon::rectangle_topleft( - Pt2D::new(HORIZ_PADDING, VERT_PADDING), + Pt2D::new( + self.top_left.x + HORIZ_PADDING, + self.top_left.y + VERT_PADDING, + ), Distance::meters(BAR_WIDTH), Distance::meters(BAR_HEIGHT), ), @@ -123,7 +133,10 @@ impl Slider { g.draw_polygon( Color::GREEN, &Polygon::rectangle_topleft( - Pt2D::new(HORIZ_PADDING, VERT_PADDING), + Pt2D::new( + self.top_left.x + HORIZ_PADDING, + self.top_left.y + VERT_PADDING, + ), Distance::meters(self.current_percent * BAR_WIDTH), Distance::meters(BAR_HEIGHT), ), @@ -144,8 +157,9 @@ impl Slider { fn slider_geom(&self) -> Polygon { Polygon::rectangle_topleft( Pt2D::new( - HORIZ_PADDING + self.current_percent * BAR_WIDTH - (SLIDER_WIDTH / 2.0), - VERT_PADDING - (SLIDER_HEIGHT - BAR_HEIGHT) / 2.0, + self.top_left.x + HORIZ_PADDING + self.current_percent * BAR_WIDTH + - (SLIDER_WIDTH / 2.0), + self.top_left.y + VERT_PADDING - (SLIDER_HEIGHT - BAR_HEIGHT) / 2.0, ), Distance::meters(SLIDER_WIDTH), Distance::meters(SLIDER_HEIGHT), @@ -188,7 +202,7 @@ impl ItemSlider { ItemSlider { items, - slider: Slider::new(), + slider: Slider::new(None), menu: ModalMenu::new(menu_title, choices, ctx), prev, diff --git a/popdat/Cargo.toml b/popdat/Cargo.toml index 1022c615be..f32c971181 100644 --- a/popdat/Cargo.toml +++ b/popdat/Cargo.toml @@ -13,3 +13,4 @@ kml = { path = "../kml" } map_model = { path = "../map_model" } serde = "1.0.89" serde_derive = "1.0.89" +structopt = "0.2.15" diff --git a/popdat/src/main.rs b/popdat/src/main.rs index 8032fe9150..86661d900c 100644 --- a/popdat/src/main.rs +++ b/popdat/src/main.rs @@ -1,4 +1,16 @@ +use structopt::StructOpt; + +#[derive(StructOpt)] +#[structopt(name = "popdat")] +struct Flags { + /// Stop after this many trips, for faster development + #[structopt(long = "cap")] + pub cap: Option, +} + fn main() { + let flags = Flags::from_args(); + let mut timer = abstutil::Timer::new("creating popdat"); let mut popdat = popdat::PopDat::import_all(&mut timer); @@ -11,6 +23,9 @@ fn main() { &mut timer, ) .unwrap(); + if let Some(n) = flags.cap { + popdat.trips = popdat.trips.into_iter().take(n).collect(); + } abstutil::write_binary("../data/shapes/popdat", &popdat).unwrap(); }