very basic tool to explore an agent's full trip

This commit is contained in:
Dustin Carlino 2019-06-27 10:42:33 -07:00
parent 218082140f
commit 334081b844
9 changed files with 197 additions and 20 deletions

View File

@ -1,7 +1,9 @@
mod score;
pub mod setup;
use crate::common::{time_controls, AgentTools, CommonState, RouteExplorer, SpeedControls};
use crate::common::{
time_controls, AgentTools, CommonState, RouteExplorer, SpeedControls, TripExplorer,
};
use crate::game::{State, Transition};
use crate::render::MIN_ZOOM_FOR_DETAIL;
use crate::ui::{PerMapUI, UI};
@ -119,6 +121,9 @@ impl State for ABTestMode {
if let Some(explorer) = RouteExplorer::new(ctx, ui) {
return Transition::Push(Box::new(explorer));
}
if let Some(explorer) = TripExplorer::new(ctx, ui) {
return Transition::Push(Box::new(explorer));
}
self.primary_agent_tools.event(ctx, ui, &mut self.menu);

View File

@ -5,6 +5,7 @@ mod route_explorer;
mod route_viewer;
mod speed;
mod time;
mod trip_explorer;
mod turn_cycler;
mod warp;
@ -12,6 +13,7 @@ pub use self::agent::AgentTools;
pub use self::route_explorer::RouteExplorer;
pub use self::speed::SpeedControls;
pub use self::time::time_controls;
pub use self::trip_explorer::TripExplorer;
use crate::game::Transition;
use crate::helpers::ID;
use crate::render::DrawOptions;

View File

@ -0,0 +1,92 @@
use crate::common::CommonState;
use crate::game::{State, Transition};
use crate::helpers::ID;
use crate::ui::UI;
use ezgui::{EventCtx, GfxCtx, Key, Text, WarpingItemSlider};
use geom::Pt2D;
use sim::{TripEnd, TripStart};
// TODO More info, like each leg of the trip, times, separate driving leg for looking for
// parking...
pub struct TripExplorer {
slider: WarpingItemSlider<ID>,
}
impl TripExplorer {
pub fn new(ctx: &mut EventCtx, ui: &UI) -> Option<TripExplorer> {
let map = &ui.primary.map;
let agent = ui.primary.current_selection.and_then(|id| id.agent_id())?;
let trip = ui.primary.sim.agent_to_trip(agent)?;
let status = ui.primary.sim.trip_status(trip)?;
if !ctx.input.contextual_action(Key::T, "explore trip") {
return None;
}
let steps: Vec<(Pt2D, ID, Text)> = vec![
match status.start {
TripStart::Bldg(b) => (
map.get_b(b).front_path.line.pt1(),
ID::Building(b),
Text::from_line(format!("start at {}", map.get_b(b).get_name())),
),
TripStart::Appearing(pos) => (
pos.pt(map),
ID::Lane(pos.lane()),
Text::from_line(format!(
"start by appearing at {}",
map.get_parent(pos.lane()).get_name()
)),
),
},
(
ui.primary.sim.get_canonical_pt_per_trip(trip, map).unwrap(),
ID::from_agent(agent),
Text::from_line("currently here".to_string()),
),
match status.end {
TripEnd::Bldg(b) => (
map.get_b(b).front_path.line.pt1(),
ID::Building(b),
Text::from_line(format!("end at {}", map.get_b(b).get_name())),
),
TripEnd::Border(i) => (
map.get_i(i).polygon.center(),
ID::Intersection(i),
Text::from_line(format!("leave map via {}", i)),
),
},
];
Some(TripExplorer {
slider: WarpingItemSlider::new(
steps,
&format!("Trip Explorer for {}", trip),
"step",
ctx,
),
})
}
}
impl State for TripExplorer {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
if ctx.redo_mouseover() {
ui.recalculate_current_selection(ctx);
}
ctx.canvas.handle_event(ctx.input);
if let Some((evmode, done_warping)) = self.slider.event(ctx) {
if done_warping {
ui.primary.current_selection = Some(*self.slider.get().1);
}
Transition::KeepWithMode(evmode)
} else {
Transition::Pop
}
}
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
self.slider.draw(g);
CommonState::draw_osd(g, ui, ui.primary.current_selection);
}
}

View File

@ -3,7 +3,9 @@ mod show_activity;
mod spawner;
mod time_travel;
use crate::common::{time_controls, AgentTools, CommonState, RouteExplorer, SpeedControls};
use crate::common::{
time_controls, AgentTools, CommonState, RouteExplorer, SpeedControls, TripExplorer,
};
use crate::debug::DebugMode;
use crate::edit::EditMode;
use crate::game::{State, Transition};
@ -99,10 +101,13 @@ impl State for SandboxMode {
if let Some(explorer) = RouteExplorer::new(ctx, ui) {
return Transition::Push(Box::new(explorer));
}
if let Some(explorer) = TripExplorer::new(ctx, ui) {
return Transition::Push(Box::new(explorer));
}
self.agent_tools.event(ctx, ui, &mut self.menu);
self.show_activity.event(ctx, ui, &mut self.menu);
if self.menu.action("start time traveling") {
if ui.primary.current_selection.is_none() && self.menu.action("start time traveling") {
return self.time_travel.start(ctx, ui);
}
if self.menu.action("scoreboard") {

View File

@ -20,7 +20,7 @@ pub(crate) use self::router::{ActionAtEnd, Router};
pub(crate) use self::scheduler::{Command, Scheduler};
pub use self::sim::Sim;
pub(crate) use self::transit::TransitSimState;
pub use self::trips::{FinishedTrips, TripMode};
pub use self::trips::{FinishedTrips, TripEnd, TripMode, TripStart, TripStatus};
pub(crate) use self::trips::{TripLeg, TripManager};
pub use crate::render::{CarStatus, DrawCarInput, DrawPedestrianInput, GetDrawAgents};
use abstutil::Cloneable;

View File

@ -60,10 +60,8 @@ impl SimFlags {
if self.load.starts_with(Path::new("../data/save/")) {
timer.note(format!("Resuming from {}", self.load.display()));
timer.start("read sim savestate");
let sim: Sim =
abstutil::read_json(self.load.to_str().unwrap()).expect("loading sim state failed");
timer.stop("read sim savestate");
let sim: Sim = abstutil::read_binary(self.load.to_str().unwrap(), timer)
.expect("loading sim state failed");
let mut map: Map =
abstutil::read_binary(&format!("../data/maps/{}.bin", sim.map_name), timer)

View File

@ -1,7 +1,7 @@
use crate::{
CarID, Command, CreateCar, CreatePedestrian, DrivingGoal, ParkingSimState, ParkingSpot,
PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot, TripLeg, TripManager, VehicleSpec,
VehicleType, MAX_CAR_LENGTH,
PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot, TripLeg, TripManager, TripStart,
VehicleSpec, VehicleType, MAX_CAR_LENGTH,
};
use abstutil::Timer;
use geom::{Duration, Speed, EPSILON_DIST};
@ -187,7 +187,8 @@ impl TripSpawner {
SidewalkSpot::building(b, map),
));
}
let trip = trips.new_trip(start_time, legs);
let trip =
trips.new_trip(start_time, Some(TripStart::Appearing(start_pos)), legs);
let router = goal.make_router(path, map, vehicle.vehicle_type);
scheduler.quick_push(
start_time,
@ -225,7 +226,11 @@ impl TripSpawner {
}
DrivingGoal::Border(_, _) => {}
}
let trip = trips.new_trip(start_time, legs);
let trip = trips.new_trip(
start_time,
Some(TripStart::Bldg(vehicle.owner.unwrap())),
legs,
);
scheduler.quick_push(
start_time,
@ -246,6 +251,13 @@ impl TripSpawner {
} => {
let trip = trips.new_trip(
start_time,
match start.connection {
SidewalkPOI::Building(b) => Some(TripStart::Bldg(b)),
SidewalkPOI::SuddenlyAppear | SidewalkPOI::Border(_) => {
Some(TripStart::Appearing(start.sidewalk_pos))
}
_ => unreachable!(),
},
vec![TripLeg::Walk(ped_id.unwrap(), ped_speed, goal.clone())],
);
@ -282,7 +294,17 @@ impl TripSpawner {
}
DrivingGoal::Border(_, _) => {}
};
let trip = trips.new_trip(start_time, legs);
let trip = trips.new_trip(
start_time,
match start.connection {
SidewalkPOI::Building(b) => Some(TripStart::Bldg(b)),
SidewalkPOI::SuddenlyAppear | SidewalkPOI::Border(_) => {
Some(TripStart::Appearing(start.sidewalk_pos))
}
_ => unreachable!(),
},
legs,
);
scheduler.quick_push(
start_time,
@ -307,6 +329,13 @@ impl TripSpawner {
let walk_to = SidewalkSpot::bus_stop(stop1, map);
let trip = trips.new_trip(
start_time,
match start.connection {
SidewalkPOI::Building(b) => Some(TripStart::Bldg(b)),
SidewalkPOI::SuddenlyAppear | SidewalkPOI::Border(_) => {
Some(TripStart::Appearing(start.sidewalk_pos))
}
_ => unreachable!(),
},
vec![
TripLeg::Walk(ped_id.unwrap(), ped_speed, walk_to.clone()),
TripLeg::RideBus(ped_id.unwrap(), route, stop2),

View File

@ -2,8 +2,8 @@ use crate::{
AgentID, CarID, Command, CreateCar, DrawCarInput, DrawPedestrianInput, DrivingGoal,
DrivingSimState, Event, FinishedTrips, GetDrawAgents, IntersectionSimState, ParkedCar,
ParkingSimState, ParkingSpot, PedestrianID, Router, Scheduler, TransitSimState, TripID,
TripLeg, TripManager, TripPositions, TripSpawner, TripSpec, VehicleSpec, VehicleType,
WalkingSimState, BUS_LENGTH,
TripLeg, TripManager, TripPositions, TripSpawner, TripSpec, TripStatus, VehicleSpec,
VehicleType, WalkingSimState, BUS_LENGTH,
};
use abstutil::{elapsed_seconds, Timer};
use derivative::Derivative;
@ -198,9 +198,9 @@ impl Sim {
// Bypass some layers of abstraction that don't make sense for buses.
// TODO Aww, we create an orphan trip if the bus can't spawn.
let trip = self
.trips
.new_trip(self.time, vec![TripLeg::ServeBusRoute(id, route.id)]);
let trip =
self.trips
.new_trip(self.time, None, vec![TripLeg::ServeBusRoute(id, route.id)]);
if self.driving.start_car_on_lane(
self.time,
CreateCar {
@ -698,6 +698,10 @@ impl Sim {
self.trips.trip_to_agent(id)
}
pub fn trip_status(&self, id: TripID) -> Option<TripStatus> {
self.trips.trip_status(id)
}
pub fn lookup_car_id(&self, idx: usize) -> Option<CarID> {
for vt in &[VehicleType::Car, VehicleType::Bike, VehicleType::Bus] {
let id = CarID(idx, *vt);

View File

@ -5,7 +5,7 @@ use crate::{
};
use abstutil::{deserialize_btreemap, serialize_btreemap};
use geom::{Duration, Speed};
use map_model::{BuildingID, BusRouteID, BusStopID, IntersectionID, Map, PathRequest};
use map_model::{BuildingID, BusRouteID, BusStopID, IntersectionID, Map, PathRequest, Position};
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, VecDeque};
@ -35,7 +35,12 @@ impl TripManager {
}
}
pub fn new_trip(&mut self, spawned_at: Duration, legs: Vec<TripLeg>) -> TripID {
pub fn new_trip(
&mut self,
spawned_at: Duration,
start: Option<TripStart>,
legs: Vec<TripLeg>,
) -> TripID {
assert!(!legs.is_empty());
// TODO Make sure the legs constitute a valid state machine.
@ -63,6 +68,7 @@ impl TripManager {
finished_at: None,
mode,
legs: VecDeque::from(legs),
start,
};
if !trip.is_bus_trip() {
self.unfinished_trips += 1;
@ -419,6 +425,23 @@ impl TripManager {
pub fn collect_events(&mut self) -> Vec<Event> {
self.events.drain(..).collect()
}
pub fn trip_status(&self, id: TripID) -> Option<TripStatus> {
let trip = &self.trips[id.0];
let start = trip.start.clone()?;
let end = match trip.legs.back() {
Some(TripLeg::Walk(_, _, ref spot)) => match spot.connection {
SidewalkPOI::Building(b) => TripEnd::Bldg(b),
_ => unreachable!(),
},
Some(TripLeg::Drive(_, ref goal)) => match goal {
DrivingGoal::ParkNear(b) => TripEnd::Bldg(*b),
DrivingGoal::Border(i, _) => TripEnd::Border(*i),
},
_ => unreachable!(),
};
Some(TripStatus { start, end })
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
@ -428,6 +451,7 @@ struct Trip {
finished_at: Option<Duration>,
legs: VecDeque<TripLeg>,
mode: TripMode,
start: Option<TripStart>,
}
impl Trip {
@ -516,3 +540,21 @@ pub enum TripMode {
Transit,
Drive,
}
// TODO Argh no, not more of these variants!
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub enum TripStart {
Bldg(BuildingID),
Appearing(Position),
}
pub enum TripEnd {
Bldg(BuildingID),
Border(IntersectionID),
}
pub struct TripStatus {
pub start: TripStart,
pub end: TripEnd,
}