mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-29 04:35:51 +03:00
very basic tool to explore an agent's full trip
This commit is contained in:
parent
218082140f
commit
334081b844
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
92
editor/src/common/trip_explorer.rs
Normal file
92
editor/src/common/trip_explorer.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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") {
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user