mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 16:36:02 +03:00
interpolate along route for trip viz
This commit is contained in:
parent
416d0b0d7d
commit
f674527ef2
@ -1,9 +1,11 @@
|
|||||||
use crate::common::CommonState;
|
use crate::common::CommonState;
|
||||||
use crate::mission::{clip_trips, Trip};
|
use crate::mission::{clip_trips, Trip};
|
||||||
use crate::ui::{ShowEverything, UI};
|
use crate::ui::{ShowEverything, UI};
|
||||||
use abstutil::{prettyprint_usize, Timer};
|
use abstutil::prettyprint_usize;
|
||||||
use ezgui::{Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Text};
|
use ezgui::{Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Text};
|
||||||
use geom::{Circle, Distance, Duration};
|
use geom::{Circle, Distance, Duration};
|
||||||
|
use map_model::LANE_THICKNESS;
|
||||||
|
use popdat::psrc::Mode;
|
||||||
use popdat::PopDat;
|
use popdat::PopDat;
|
||||||
|
|
||||||
pub struct TripsVisualizer {
|
pub struct TripsVisualizer {
|
||||||
@ -16,28 +18,40 @@ pub struct TripsVisualizer {
|
|||||||
|
|
||||||
impl TripsVisualizer {
|
impl TripsVisualizer {
|
||||||
pub fn new(ctx: &mut EventCtx, ui: &UI) -> TripsVisualizer {
|
pub fn new(ctx: &mut EventCtx, ui: &UI) -> TripsVisualizer {
|
||||||
let mut timer = Timer::new("initialize popdat");
|
let trips = ctx.loading_screen(|_, mut timer| {
|
||||||
let popdat: PopDat = abstutil::read_binary("../data/shapes/popdat", &mut timer)
|
let popdat: PopDat = abstutil::read_binary("../data/shapes/popdat", &mut timer)
|
||||||
.expect("Couldn't load popdat");
|
.expect("Couldn't load popdat");
|
||||||
let mut trips = clip_trips(&popdat, ui, &mut timer);
|
let mut all_trips = clip_trips(&popdat, ui, &mut timer);
|
||||||
timer.start_iter("calculate routes", trips.len());
|
timer.start_iter("calculate routes", all_trips.len());
|
||||||
for trip in trips.iter_mut() {
|
let mut final_trips = Vec::new();
|
||||||
timer.next();
|
for mut trip in all_trips.drain(..) {
|
||||||
let req = trip.path_req(&ui.primary.map);
|
timer.next();
|
||||||
trip.route = ui
|
let req = trip.path_req(&ui.primary.map);
|
||||||
.primary
|
if let Some(route) = ui
|
||||||
.map
|
.primary
|
||||||
.pathfind(req.clone())
|
.map
|
||||||
.and_then(|path| path.trace(&ui.primary.map, req.start.dist_along(), None));
|
.pathfind(req.clone())
|
||||||
}
|
.and_then(|path| path.trace(&ui.primary.map, req.start.dist_along(), None))
|
||||||
|
{
|
||||||
|
trip.route = Some(route);
|
||||||
|
final_trips.push(trip);
|
||||||
|
} else {
|
||||||
|
timer.warn(format!("Couldn't satisfy {}", req));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final_trips
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO It'd be awesome to use the generic timer controls for this
|
||||||
TripsVisualizer {
|
TripsVisualizer {
|
||||||
menu: ModalMenu::new(
|
menu: ModalMenu::new(
|
||||||
"Trips Visualizer",
|
"Trips Visualizer",
|
||||||
vec![
|
vec![
|
||||||
(Some(Key::Escape), "quit"),
|
(Some(Key::Escape), "quit"),
|
||||||
(Some(Key::Dot), "forwards 1 minute"),
|
(Some(Key::Dot), "forwards 10 seconds"),
|
||||||
(Some(Key::Comma), "backwards 1 minute"),
|
(Some(Key::RightArrow), "forwards 30 minutes"),
|
||||||
|
(Some(Key::Comma), "backwards 10 seconds"),
|
||||||
|
(Some(Key::LeftArrow), "backwards 30 minutes"),
|
||||||
(Some(Key::F), "goto start of day"),
|
(Some(Key::F), "goto start of day"),
|
||||||
(Some(Key::L), "goto end of day"),
|
(Some(Key::L), "goto end of day"),
|
||||||
],
|
],
|
||||||
@ -63,14 +77,22 @@ impl TripsVisualizer {
|
|||||||
ui.primary.current_selection =
|
ui.primary.current_selection =
|
||||||
ui.handle_mouseover(ctx, &ui.primary.sim, &ShowEverything::new(), false);
|
ui.handle_mouseover(ctx, &ui.primary.sim, &ShowEverything::new(), false);
|
||||||
|
|
||||||
let last_time = Duration::parse("23:59:00.0").unwrap();
|
let last_time = Duration::parse("23:59:50.0").unwrap();
|
||||||
|
let ten_secs = Duration::seconds(10.0);
|
||||||
|
let thirty_mins = Duration::minutes(30);
|
||||||
|
|
||||||
if self.menu.action("quit") {
|
if self.menu.action("quit") {
|
||||||
return true;
|
return true;
|
||||||
} else if self.time != last_time && self.menu.action("forwards 1 minute") {
|
} else if self.time != last_time && self.menu.action("forwards 10 seconds") {
|
||||||
self.time += Duration::minutes(1);
|
self.time += ten_secs;
|
||||||
} else if self.time != Duration::ZERO && self.menu.action("backwards 1 minute") {
|
} else if self.time + thirty_mins <= last_time && self.menu.action("forwards 30 minutes") {
|
||||||
self.time -= Duration::minutes(1);
|
self.time += thirty_mins;
|
||||||
|
} else if self.time != Duration::ZERO && self.menu.action("backwards 10 seconds") {
|
||||||
|
self.time -= ten_secs;
|
||||||
|
} else if self.time - thirty_mins >= Duration::ZERO
|
||||||
|
&& self.menu.action("backwards 30 minutes")
|
||||||
|
{
|
||||||
|
self.time -= thirty_mins;
|
||||||
} else if self.time != Duration::ZERO && self.menu.action("goto start of day") {
|
} else if self.time != Duration::ZERO && self.menu.action("goto start of day") {
|
||||||
self.time = Duration::ZERO;
|
self.time = Duration::ZERO;
|
||||||
} else if self.time != last_time && self.menu.action("goto end of day") {
|
} else if self.time != last_time && self.menu.action("goto end of day") {
|
||||||
@ -95,18 +117,41 @@ impl TripsVisualizer {
|
|||||||
let mut batch = GeomBatch::new();
|
let mut batch = GeomBatch::new();
|
||||||
for idx in &self.active_trips {
|
for idx in &self.active_trips {
|
||||||
let trip = &self.trips[*idx];
|
let trip = &self.trips[*idx];
|
||||||
let from = ui.primary.map.get_b(trip.from);
|
let percent = (self.time - trip.depart_at) / trip.trip_time;
|
||||||
let to = ui.primary.map.get_b(trip.to);
|
|
||||||
|
|
||||||
let percent = ((self.time - trip.depart_at) / trip.trip_time) as f32;
|
if true {
|
||||||
batch.push(
|
let pl = trip.route.as_ref().unwrap();
|
||||||
Color::RED.alpha(1.0 - percent),
|
let color = match trip.mode {
|
||||||
Circle::new(from.polygon.center(), Distance::meters(100.0)).to_polygon(),
|
Mode::Drive => Color::RED,
|
||||||
);
|
Mode::Walk => Color::GREEN,
|
||||||
batch.push(
|
Mode::Bike => Color::BLUE,
|
||||||
Color::BLUE.alpha(percent),
|
}
|
||||||
Circle::new(to.polygon.center(), Distance::meters(100.0)).to_polygon(),
|
.alpha(0.5);
|
||||||
);
|
batch.push(
|
||||||
|
color,
|
||||||
|
Circle::new(
|
||||||
|
pl.dist_along(percent * pl.length()).0,
|
||||||
|
// Draw bigger circles when zoomed out, but don't go smaller than the lane
|
||||||
|
// once fully zoomed in.
|
||||||
|
(Distance::meters(10.0) / g.canvas.cam_zoom).max(LANE_THICKNESS),
|
||||||
|
)
|
||||||
|
.to_polygon(),
|
||||||
|
);
|
||||||
|
// TODO Draw the entire route, once sharp angled polylines are fixed
|
||||||
|
} else {
|
||||||
|
// Draw the start and end, gradually fading the color.
|
||||||
|
let from = ui.primary.map.get_b(trip.from);
|
||||||
|
let to = ui.primary.map.get_b(trip.to);
|
||||||
|
|
||||||
|
batch.push(
|
||||||
|
Color::RED.alpha(1.0 - (percent as f32)),
|
||||||
|
Circle::new(from.polygon.center(), Distance::meters(100.0)).to_polygon(),
|
||||||
|
);
|
||||||
|
batch.push(
|
||||||
|
Color::BLUE.alpha(percent as f32),
|
||||||
|
Circle::new(to.polygon.center(), Distance::meters(100.0)).to_polygon(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
batch.draw(g);
|
batch.draw(g);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use crate::{BusRouteID, BusStopID, LaneID, LaneType, Map, Position, Traversable,
|
|||||||
use geom::{Distance, PolyLine};
|
use geom::{Distance, PolyLine};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::collections::{BTreeSet, VecDeque};
|
use std::collections::{BTreeSet, VecDeque};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum PathStep {
|
pub enum PathStep {
|
||||||
@ -233,7 +234,7 @@ impl Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PathRequest {
|
pub struct PathRequest {
|
||||||
pub start: Position,
|
pub start: Position,
|
||||||
pub end: Position,
|
pub end: Position,
|
||||||
@ -241,6 +242,27 @@ pub struct PathRequest {
|
|||||||
pub can_use_bus_lanes: bool,
|
pub can_use_bus_lanes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PathRequest {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"PathRequest({} along {}... to {} along {}",
|
||||||
|
self.start.dist_along(),
|
||||||
|
self.start.lane(),
|
||||||
|
self.end.dist_along(),
|
||||||
|
self.end.lane()
|
||||||
|
)?;
|
||||||
|
// TODO can_use_bike_lanes and can_use_bus_lanes are mutex, encode that directly.
|
||||||
|
if self.can_use_bike_lanes {
|
||||||
|
write!(f, ", bike lanes)")
|
||||||
|
} else if self.can_use_bus_lanes {
|
||||||
|
write!(f, ", bus lanes)")
|
||||||
|
} else {
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn validate(map: &Map, steps: &Vec<PathStep>) {
|
fn validate(map: &Map, steps: &Vec<PathStep>) {
|
||||||
if steps.is_empty() {
|
if steps.is_empty() {
|
||||||
panic!("Empty Path");
|
panic!("Empty Path");
|
||||||
|
@ -167,7 +167,7 @@ impl TripSpawner {
|
|||||||
self.trips.drain(..).zip(paths)
|
self.trips.drain(..).zip(paths)
|
||||||
{
|
{
|
||||||
if maybe_path.is_none() {
|
if maybe_path.is_none() {
|
||||||
timer.warn(format!("{:?} couldn't find the first path {:?}", spec, req));
|
timer.warn(format!("{:?} couldn't find the first path {}", spec, req));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let path = maybe_path.unwrap();
|
let path = maybe_path.unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user