mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 00:12:55 +03:00
overhaul finished trips graph. record times in order, build the graph later
This commit is contained in:
parent
c89c0d2a11
commit
ef01501509
@ -176,7 +176,6 @@ fn launch_test(test: &ABTest, ui: &mut UI, ctx: &mut EventCtx) -> ABTestMode {
|
||||
.sim_flags
|
||||
.opts
|
||||
.disable_block_the_box,
|
||||
record_stats: false,
|
||||
recalc_lanechanging: current_flags
|
||||
.sim_flags
|
||||
.opts
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::trip_stats::{ShowTripStats, TripStats};
|
||||
use super::trip_stats::ShowTripStats;
|
||||
use crate::common::{ObjectColorer, ObjectColorerBuilder, RoadColorer, RoadColorerBuilder};
|
||||
use crate::game::{Transition, WizardState};
|
||||
use crate::helpers::ID;
|
||||
@ -31,7 +31,6 @@ impl Analytics {
|
||||
ctx: &mut EventCtx,
|
||||
ui: &UI,
|
||||
menu: &mut MenuUnderButton,
|
||||
trip_stats: &TripStats,
|
||||
) -> Option<Transition> {
|
||||
if menu.action("change analytics overlay") {
|
||||
return Some(Transition::Push(WizardState::new(Box::new(
|
||||
@ -51,8 +50,7 @@ impl Analytics {
|
||||
})?;
|
||||
Some(Transition::PopWithData(Box::new(move |state, ui, ctx| {
|
||||
let mut sandbox = state.downcast_mut::<SandboxMode>().unwrap();
|
||||
sandbox.analytics =
|
||||
Analytics::recalc(&choice, &sandbox.trip_stats, ui, ctx);
|
||||
sandbox.analytics = Analytics::recalc(&choice, ui, ctx);
|
||||
})))
|
||||
},
|
||||
))));
|
||||
@ -74,7 +72,7 @@ impl Analytics {
|
||||
}
|
||||
};
|
||||
if time != ui.primary.sim.time() {
|
||||
*self = Analytics::recalc(choice, trip_stats, ui, ctx);
|
||||
*self = Analytics::recalc(choice, ui, ctx);
|
||||
}
|
||||
None
|
||||
}
|
||||
@ -111,7 +109,7 @@ impl Analytics {
|
||||
}
|
||||
}
|
||||
|
||||
fn recalc(choice: &str, trip_stats: &TripStats, ui: &UI, ctx: &mut EventCtx) -> Analytics {
|
||||
fn recalc(choice: &str, ui: &UI, ctx: &mut EventCtx) -> Analytics {
|
||||
let time = ui.primary.sim.time();
|
||||
match choice {
|
||||
"none" => Analytics::Inactive,
|
||||
@ -123,10 +121,10 @@ impl Analytics {
|
||||
}
|
||||
"cumulative throughput" => Analytics::Throughput(time, calculate_thruput(ctx, ui)),
|
||||
"finished trips" => {
|
||||
if let Some(s) = ShowTripStats::new(trip_stats, ui, ctx) {
|
||||
if let Some(s) = ShowTripStats::new(ui, ctx) {
|
||||
Analytics::FinishedTrips(time, s)
|
||||
} else {
|
||||
println!("No trip stats available. Pass --record_stats or make sure at least one trip is done.");
|
||||
println!("No data on finished trips yet");
|
||||
Analytics::Inactive
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ pub struct SandboxMode {
|
||||
general_tools: MenuUnderButton,
|
||||
save_tools: MenuUnderButton,
|
||||
agent_tools: AgentTools,
|
||||
trip_stats: trip_stats::TripStats,
|
||||
analytics: analytics::Analytics,
|
||||
gameplay: gameplay::GameplayState,
|
||||
common: CommonState,
|
||||
@ -73,9 +72,6 @@ impl SandboxMode {
|
||||
ctx,
|
||||
),
|
||||
agent_tools: AgentTools::new(),
|
||||
trip_stats: trip_stats::TripStats::new(
|
||||
ui.primary.current_flags.sim_flags.opts.record_stats,
|
||||
),
|
||||
analytics: analytics::Analytics::Inactive,
|
||||
gameplay: gameplay::GameplayState::initialize(mode, ui, ctx),
|
||||
common: CommonState::new(ctx),
|
||||
@ -87,8 +83,6 @@ impl SandboxMode {
|
||||
|
||||
impl State for SandboxMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
|
||||
self.trip_stats.record(ui);
|
||||
|
||||
layout::stack_vertically(
|
||||
layout::ContainerOrientation::TopRight,
|
||||
ctx.canvas,
|
||||
@ -115,10 +109,7 @@ impl State for SandboxMode {
|
||||
if let Some(t) = self.common.event(ctx, ui) {
|
||||
return t;
|
||||
}
|
||||
if let Some(t) = self
|
||||
.analytics
|
||||
.event(ctx, ui, &mut self.info_tools, &self.trip_stats)
|
||||
{
|
||||
if let Some(t) = self.analytics.event(ctx, ui, &mut self.info_tools) {
|
||||
return t;
|
||||
}
|
||||
if let Some(t) = self.gameplay.event(ctx, ui, &mut self.analytics) {
|
||||
|
@ -1,79 +1,14 @@
|
||||
use crate::common::ColorLegend;
|
||||
use crate::ui::UI;
|
||||
use abstutil::Counter;
|
||||
use ezgui::{
|
||||
Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, MultiText, ScreenPt, ScreenRectangle, Text,
|
||||
};
|
||||
use geom::{Distance, Duration, PolyLine, Polygon, Pt2D};
|
||||
use sim::TripMode;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct TripStats {
|
||||
should_record: bool,
|
||||
samples: Vec<StateAtTime>,
|
||||
}
|
||||
|
||||
struct StateAtTime {
|
||||
time: Duration,
|
||||
// These're all cumulative
|
||||
finished_walk_trips: usize,
|
||||
finished_bike_trips: usize,
|
||||
finished_transit_trips: usize,
|
||||
finished_drive_trips: usize,
|
||||
aborted_trips: usize,
|
||||
}
|
||||
|
||||
impl TripStats {
|
||||
pub fn new(should_record: bool) -> TripStats {
|
||||
TripStats {
|
||||
should_record,
|
||||
samples: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn record(&mut self, ui: &UI) {
|
||||
if !self.should_record {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref state) = self.samples.last() {
|
||||
// Already have this
|
||||
if ui.primary.sim.time() == state.time {
|
||||
return;
|
||||
}
|
||||
// We just loaded a new savestate or reset or something. Clear out our memory.
|
||||
if ui.primary.sim.time() < state.time {
|
||||
self.samples.clear();
|
||||
}
|
||||
}
|
||||
|
||||
let t = ui.primary.sim.get_finished_trips();
|
||||
let mut state = StateAtTime {
|
||||
time: ui.primary.sim.time(),
|
||||
finished_walk_trips: 0,
|
||||
finished_bike_trips: 0,
|
||||
finished_transit_trips: 0,
|
||||
finished_drive_trips: 0,
|
||||
aborted_trips: t.aborted_trips,
|
||||
};
|
||||
for (_, m, _) in t.finished_trips {
|
||||
match m {
|
||||
TripMode::Walk => {
|
||||
state.finished_walk_trips += 1;
|
||||
}
|
||||
TripMode::Bike => {
|
||||
state.finished_bike_trips += 1;
|
||||
}
|
||||
TripMode::Transit => {
|
||||
state.finished_transit_trips += 1;
|
||||
}
|
||||
TripMode::Drive => {
|
||||
state.finished_drive_trips += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.samples.push(state);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Show active trips too
|
||||
pub struct ShowTripStats {
|
||||
draw: Drawable,
|
||||
legend: ColorLegend,
|
||||
@ -82,121 +17,61 @@ pub struct ShowTripStats {
|
||||
}
|
||||
|
||||
impl ShowTripStats {
|
||||
pub fn new(stats: &TripStats, ui: &UI, ctx: &mut EventCtx) -> Option<ShowTripStats> {
|
||||
if stats.samples.is_empty() {
|
||||
pub fn new(ui: &UI, ctx: &mut EventCtx) -> Option<ShowTripStats> {
|
||||
if ui.primary.sim.get_analytics().finished_trips.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
let mut labels = MultiText::new();
|
||||
|
||||
let x1 = 0.2 * ctx.canvas.window_width;
|
||||
let x2 = 0.8 * ctx.canvas.window_width;
|
||||
let y1 = 0.2 * ctx.canvas.window_height;
|
||||
let y2 = 0.8 * ctx.canvas.window_height;
|
||||
batch.push(
|
||||
Color::grey(0.8),
|
||||
Polygon::rectangle_topleft(
|
||||
Pt2D::new(x1, y1),
|
||||
Distance::meters(x2 - x1),
|
||||
Distance::meters(y2 - y1),
|
||||
),
|
||||
);
|
||||
|
||||
let lines: Vec<(&str, Color, Box<dyn Fn(&StateAtTime) -> usize>)> = vec![
|
||||
let lines: Vec<(&str, Color, Option<TripMode>)> = vec![
|
||||
(
|
||||
"walking",
|
||||
ui.cs.get("unzoomed pedestrian"),
|
||||
Box::new(|s| s.finished_walk_trips),
|
||||
),
|
||||
(
|
||||
"biking",
|
||||
ui.cs.get("unzoomed bike"),
|
||||
Box::new(|s| s.finished_bike_trips),
|
||||
Some(TripMode::Walk),
|
||||
),
|
||||
("biking", ui.cs.get("unzoomed bike"), Some(TripMode::Bike)),
|
||||
(
|
||||
"transit",
|
||||
ui.cs.get("unzoomed bus"),
|
||||
Box::new(|s| s.finished_transit_trips),
|
||||
),
|
||||
(
|
||||
"driving",
|
||||
ui.cs.get("unzoomed car"),
|
||||
Box::new(|s| s.finished_drive_trips),
|
||||
),
|
||||
(
|
||||
"aborted",
|
||||
Color::PURPLE.alpha(0.5),
|
||||
Box::new(|s| s.aborted_trips),
|
||||
Some(TripMode::Transit),
|
||||
),
|
||||
("driving", ui.cs.get("unzoomed car"), Some(TripMode::Drive)),
|
||||
("aborted", Color::PURPLE.alpha(0.5), None),
|
||||
];
|
||||
let legend = ColorLegend::new(
|
||||
Text::prompt("finished trips"),
|
||||
lines
|
||||
.iter()
|
||||
.map(|(name, color, _)| (*name, *color))
|
||||
.collect(),
|
||||
);
|
||||
let max_y = stats
|
||||
.samples
|
||||
.iter()
|
||||
.map(|s| lines.iter().map(|(_, _, getter)| getter(s)).max().unwrap())
|
||||
.max()
|
||||
.unwrap();
|
||||
// Y-axis labels
|
||||
for i in 0..=5 {
|
||||
let percent = f64::from(i) / 5.0;
|
||||
labels.add(
|
||||
Text::from(Line(((percent * (max_y as f64)) as usize).to_string())),
|
||||
ScreenPt::new(x1, y2 - percent * (y2 - y1)),
|
||||
);
|
||||
}
|
||||
// X-axis labels (currently nonlinear!)
|
||||
if stats.samples.len() > 1 {
|
||||
let num_pts = stats.samples.len().min(5);
|
||||
for i in 0..num_pts {
|
||||
let percent_x = (i as f64) / ((num_pts - 1) as f64);
|
||||
let t =
|
||||
stats.samples[(percent_x * ((stats.samples.len() - 1) as f64)) as usize].time;
|
||||
labels.add(
|
||||
Text::from(Line(t.to_string())),
|
||||
ScreenPt::new(x1 + percent_x * (x2 - x1), y2),
|
||||
);
|
||||
}
|
||||
|
||||
// What times do we use for interpolation?
|
||||
let num_x_pts = 100;
|
||||
let mut times = Vec::new();
|
||||
for i in 0..num_x_pts {
|
||||
let percent_x = (i as f64) / ((num_x_pts - 1) as f64);
|
||||
let t = ui.primary.sim.time() * percent_x;
|
||||
times.push(t);
|
||||
}
|
||||
|
||||
for (_, color, getter) in lines {
|
||||
let mut pts = Vec::new();
|
||||
if max_y == 0 {
|
||||
pts.push(Pt2D::new(x1, y2));
|
||||
pts.push(Pt2D::new(x2, y2));
|
||||
} else {
|
||||
let num_pts = stats.samples.len().min(10);
|
||||
for i in 0..num_pts {
|
||||
let percent_x = (i as f64) / ((num_pts - 1) as f64);
|
||||
let value = getter(
|
||||
&stats.samples[(percent_x * ((stats.samples.len() - 1) as f64)) as usize],
|
||||
);
|
||||
let percent_y = (value as f64) / (max_y as f64);
|
||||
pts.push(Pt2D::new(
|
||||
x1 + (x2 - x1) * percent_x,
|
||||
// Y inversion! :D
|
||||
y2 - (y2 - y1) * percent_y,
|
||||
));
|
||||
// Gather the data
|
||||
let mut counts = Counter::new();
|
||||
let mut pts_per_mode: BTreeMap<Option<TripMode>, Vec<(Duration, usize)>> =
|
||||
lines.iter().map(|(_, _, m)| (*m, Vec::new())).collect();
|
||||
for (t, m) in &ui.primary.sim.get_analytics().finished_trips {
|
||||
counts.inc(*m);
|
||||
if *t > times[0] {
|
||||
times.remove(0);
|
||||
for (_, _, mode) in &lines {
|
||||
pts_per_mode
|
||||
.get_mut(mode)
|
||||
.unwrap()
|
||||
.push((*t, counts.get(*mode)));
|
||||
}
|
||||
}
|
||||
batch.push(
|
||||
color,
|
||||
PolyLine::new(pts).make_polygons(Distance::meters(5.0)),
|
||||
);
|
||||
}
|
||||
|
||||
Some(ShowTripStats {
|
||||
draw: ctx.prerender.upload(batch),
|
||||
labels,
|
||||
legend,
|
||||
rect: ScreenRectangle { x1, y1, x2, y2 },
|
||||
})
|
||||
Some(plot(
|
||||
"finished trips",
|
||||
lines
|
||||
.into_iter()
|
||||
.map(|(name, color, m)| (name, color, pts_per_mode.remove(&m).unwrap()))
|
||||
.collect(),
|
||||
ctx,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
@ -210,3 +85,89 @@ impl ShowTripStats {
|
||||
g.canvas.mark_covered_area(self.rect.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn plot(
|
||||
title: &str,
|
||||
series: Vec<(&str, Color, Vec<(Duration, usize)>)>,
|
||||
ctx: &EventCtx,
|
||||
) -> ShowTripStats {
|
||||
let mut batch = GeomBatch::new();
|
||||
let mut labels = MultiText::new();
|
||||
|
||||
let x1 = 0.2 * ctx.canvas.window_width;
|
||||
let x2 = 0.8 * ctx.canvas.window_width;
|
||||
let y1 = 0.2 * ctx.canvas.window_height;
|
||||
let y2 = 0.8 * ctx.canvas.window_height;
|
||||
batch.push(
|
||||
Color::grey(0.8),
|
||||
Polygon::rectangle_topleft(
|
||||
Pt2D::new(x1, y1),
|
||||
Distance::meters(x2 - x1),
|
||||
Distance::meters(y2 - y1),
|
||||
),
|
||||
);
|
||||
|
||||
// Assume every series has data at exactly the same durations
|
||||
let num_x_labels = 5;
|
||||
let max_x = series[0].2.last().unwrap().0;
|
||||
for i in 0..num_x_labels {
|
||||
let percent_x = (i as f64) / ((num_x_labels - 1) as f64);
|
||||
let t = series[0].2[(percent_x * ((series[0].2.len() - 1) as f64)) as usize].0;
|
||||
labels.add(
|
||||
Text::from(Line(t.to_string())),
|
||||
ScreenPt::new(x1 + percent_x * (x2 - x1), y2),
|
||||
);
|
||||
}
|
||||
|
||||
// Don't assume the data is cumulative
|
||||
let max_y = series
|
||||
.iter()
|
||||
.map(|(_, _, pts)| pts.iter().map(|(_, cnt)| *cnt).max().unwrap())
|
||||
.max()
|
||||
.unwrap();
|
||||
let num_y_labels = 5;
|
||||
for i in 0..num_y_labels {
|
||||
let percent_y = (i as f64) / ((num_y_labels - 1) as f64);
|
||||
labels.add(
|
||||
Text::from(Line(((percent_y * (max_y as f64)) as usize).to_string())),
|
||||
ScreenPt::new(x1, y2 - percent_y * (y2 - y1)),
|
||||
);
|
||||
}
|
||||
|
||||
let legend = ColorLegend::new(
|
||||
Text::prompt(title),
|
||||
series
|
||||
.iter()
|
||||
.map(|(name, color, _)| (*name, *color))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
for (_, color, raw_pts) in series {
|
||||
let mut pts = Vec::new();
|
||||
if max_y == 0 {
|
||||
pts.push(Pt2D::new(x1, y2));
|
||||
pts.push(Pt2D::new(x2, y2));
|
||||
} else {
|
||||
for (t, y) in raw_pts {
|
||||
let percent_x = t / max_x;
|
||||
let percent_y = (y as f64) / (max_y as f64);
|
||||
pts.push(Pt2D::new(
|
||||
x1 + (x2 - x1) * percent_x,
|
||||
// Y inversion! :D
|
||||
y2 - (y2 - y1) * percent_y,
|
||||
));
|
||||
}
|
||||
}
|
||||
batch.push(
|
||||
color,
|
||||
PolyLine::new(pts).make_polygons(Distance::meters(5.0)),
|
||||
);
|
||||
}
|
||||
|
||||
ShowTripStats {
|
||||
draw: ctx.prerender.upload(batch),
|
||||
labels,
|
||||
legend,
|
||||
rect: ScreenRectangle { x1, y1, x2, y2 },
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::Event;
|
||||
use crate::{Event, TripMode};
|
||||
use abstutil::Counter;
|
||||
use geom::Duration;
|
||||
use map_model::{BusRouteID, BusStopID, IntersectionID, Map, RoadID, Traversable};
|
||||
@ -11,6 +11,8 @@ pub struct Analytics {
|
||||
pub(crate) test_expectations: VecDeque<Event>,
|
||||
pub bus_arrivals: HashMap<(BusStopID, BusRouteID), Vec<Duration>>,
|
||||
pub total_bus_passengers: Counter<BusRouteID>,
|
||||
// TODO Hack: No TripMode means aborted
|
||||
pub finished_trips: Vec<(Duration, Option<TripMode>)>,
|
||||
}
|
||||
|
||||
pub struct ThruputStats {
|
||||
@ -28,6 +30,7 @@ impl Default for Analytics {
|
||||
test_expectations: VecDeque::new(),
|
||||
bus_arrivals: HashMap::new(),
|
||||
total_bus_passengers: Counter::new(),
|
||||
finished_trips: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,5 +63,12 @@ impl Analytics {
|
||||
if let Event::PedEntersBus(_, _, route) = ev {
|
||||
self.total_bus_passengers.inc(route);
|
||||
}
|
||||
|
||||
// Finished trips
|
||||
if let Event::TripFinished(_, mode) = ev {
|
||||
self.finished_trips.push((time, Some(mode)));
|
||||
} else if let Event::TripAborted(_) = ev {
|
||||
self.finished_trips.push((time, None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{AgentID, CarID, ParkingSpot, PedestrianID};
|
||||
use crate::{AgentID, CarID, ParkingSpot, PedestrianID, TripID, TripMode};
|
||||
use map_model::{BuildingID, BusRouteID, BusStopID, IntersectionID, LaneID, Traversable};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
@ -20,4 +20,7 @@ pub enum Event {
|
||||
BikeStoppedAtSidewalk(CarID, LaneID),
|
||||
|
||||
AgentEntersTraversable(AgentID, Traversable),
|
||||
|
||||
TripFinished(TripID, TripMode),
|
||||
TripAborted(TripID),
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ impl SimFlags {
|
||||
savestate_every: args.optional_parse("--savestate_every", Duration::parse),
|
||||
use_freeform_policy_everywhere: args.enabled("--freeform_policy"),
|
||||
disable_block_the_box: args.enabled("--disable_block_the_box"),
|
||||
record_stats: args.enabled("--record_stats"),
|
||||
recalc_lanechanging: !args.enabled("--dont_recalc_lc"),
|
||||
},
|
||||
}
|
||||
|
@ -62,7 +62,6 @@ pub struct SimOptions {
|
||||
pub savestate_every: Option<Duration>,
|
||||
pub use_freeform_policy_everywhere: bool,
|
||||
pub disable_block_the_box: bool,
|
||||
pub record_stats: bool,
|
||||
pub recalc_lanechanging: bool,
|
||||
}
|
||||
|
||||
@ -73,7 +72,6 @@ impl SimOptions {
|
||||
savestate_every: None,
|
||||
use_freeform_policy_everywhere: false,
|
||||
disable_block_the_box: false,
|
||||
record_stats: false,
|
||||
recalc_lanechanging: true,
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ impl TripManager {
|
||||
assert!(!trip.finished_at.is_some());
|
||||
trip.finished_at = Some(now);
|
||||
self.unfinished_trips -= 1;
|
||||
self.events.push(Event::TripFinished(trip.id, trip.mode));
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
@ -199,6 +200,7 @@ impl TripManager {
|
||||
);
|
||||
self.unfinished_trips -= 1;
|
||||
trip.aborted = true;
|
||||
self.events.push(Event::TripAborted(trip.id));
|
||||
return;
|
||||
};
|
||||
|
||||
@ -251,6 +253,7 @@ impl TripManager {
|
||||
);
|
||||
self.unfinished_trips -= 1;
|
||||
trip.aborted = true;
|
||||
self.events.push(Event::TripAborted(trip.id));
|
||||
return;
|
||||
};
|
||||
|
||||
@ -306,6 +309,7 @@ impl TripManager {
|
||||
assert!(!trip.finished_at.is_some());
|
||||
trip.finished_at = Some(now);
|
||||
self.unfinished_trips -= 1;
|
||||
self.events.push(Event::TripFinished(trip.id, trip.mode));
|
||||
}
|
||||
|
||||
// If no route is returned, the pedestrian boarded a bus immediately.
|
||||
@ -385,6 +389,7 @@ impl TripManager {
|
||||
assert!(!trip.finished_at.is_some());
|
||||
trip.finished_at = Some(now);
|
||||
self.unfinished_trips -= 1;
|
||||
self.events.push(Event::TripFinished(trip.id, trip.mode));
|
||||
}
|
||||
|
||||
pub fn car_or_bike_reached_border(&mut self, now: Duration, car: CarID, i: IntersectionID) {
|
||||
@ -398,6 +403,7 @@ impl TripManager {
|
||||
assert!(!trip.finished_at.is_some());
|
||||
trip.finished_at = Some(now);
|
||||
self.unfinished_trips -= 1;
|
||||
self.events.push(Event::TripFinished(trip.id, trip.mode));
|
||||
}
|
||||
|
||||
pub fn abort_trip_failed_start(&mut self, id: TripID) {
|
||||
@ -405,6 +411,7 @@ impl TripManager {
|
||||
if !self.trips[id.0].is_bus_trip() {
|
||||
self.unfinished_trips -= 1;
|
||||
}
|
||||
self.events.push(Event::TripAborted(id));
|
||||
}
|
||||
|
||||
pub fn abort_trip_impossible_parking(&mut self, car: CarID) {
|
||||
@ -412,6 +419,7 @@ impl TripManager {
|
||||
assert!(!self.trips[trip.0].is_bus_trip());
|
||||
self.trips[trip.0].aborted = true;
|
||||
self.unfinished_trips -= 1;
|
||||
self.events.push(Event::TripAborted(trip));
|
||||
}
|
||||
|
||||
pub fn active_agents(&self) -> Vec<AgentID> {
|
||||
|
Loading…
Reference in New Issue
Block a user