mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-26 07:52:05 +03:00
introducing a basic slider to adjust speed of all route viz player and
sim. code duplication.
This commit is contained in:
parent
82120c21a7
commit
0c6a93d38f
@ -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<Trip>,
|
||||
slider: Slider,
|
||||
time_slider: Slider,
|
||||
speed_slider: Slider,
|
||||
|
||||
active_trips: Vec<usize>,
|
||||
desired_speed: f64,
|
||||
running: Option<Instant>,
|
||||
}
|
||||
|
||||
@ -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<EventLoopMode> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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<TripID>,
|
||||
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!(),
|
||||
|
@ -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<ScreenPt>) -> 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<T> ItemSlider<T> {
|
||||
|
||||
ItemSlider {
|
||||
items,
|
||||
slider: Slider::new(),
|
||||
slider: Slider::new(None),
|
||||
menu: ModalMenu::new(menu_title, choices, ctx),
|
||||
|
||||
prev,
|
||||
|
@ -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"
|
||||
|
@ -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<usize>,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user