mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
basic heatmap showing busiest roads and intersections. have to revive
Sim Events kind of.
This commit is contained in:
parent
9d35f0f92f
commit
1a10e8982a
@ -75,6 +75,12 @@ impl<T: Ord + PartialEq> Counter<T> {
|
||||
pub fn get(&self, val: T) -> usize {
|
||||
self.map.get(&val).cloned().unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn sorted_asc(&self) -> Vec<&T> {
|
||||
let mut list = self.map.iter().collect::<Vec<_>>();
|
||||
list.sort_by_key(|(_, cnt)| *cnt);
|
||||
list.into_iter().map(|(t, _)| t).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wraparound_get<T>(vec: &Vec<T>, idx: isize) -> &T {
|
||||
|
@ -3,7 +3,6 @@
|
||||
- enable more clippy lints
|
||||
- enforce consistent style (derive order, struct initialization order)
|
||||
|
||||
- update with mission statement (democratized urb p, that quote, refashion existing space cheaply)
|
||||
- trailer
|
||||
- show common parts of routes in A/B, point of divergence
|
||||
- "Two parallel universes sit at your fingertips, and with the flick of a key, you can glide between the two. Buses jumping past traffic in one world, snarly traffic jam in the other. An A/B test revealing what currently is, and what could be, compared meticulously and deterministically. A/B Street -- which world do you prefer?"
|
||||
|
@ -22,9 +22,6 @@
|
||||
- deal with loop roads?
|
||||
- model U-turns
|
||||
|
||||
- degenerate-2's should only have one crosswalk
|
||||
- then make them thinner
|
||||
|
||||
- car turns often clip sidewalk corners now
|
||||
- draw SharedSidewalkCorners just around the ped path, not arbitrarily thick
|
||||
- dont forget to draw the notches
|
||||
@ -84,10 +81,6 @@
|
||||
- just revert intersection and warn
|
||||
- or store overrides more granularly and warn or do something reasonable
|
||||
|
||||
## Release
|
||||
|
||||
- publish the map data
|
||||
|
||||
## Sim bugs/tests needed
|
||||
|
||||
- do bikes use bike lanes?
|
||||
@ -95,11 +88,11 @@
|
||||
- make sure that we can jump to a ped on a bus and see the bus
|
||||
- park/unpark needs to jump two lanes in the case of crossing a bike lane or something
|
||||
- should only be able to park from the closest lane, though!
|
||||
- explicit tests making cars park at 0 and max_dist, peds walk to 0 and max_dist
|
||||
|
||||
## Discrete-event sim model
|
||||
|
||||
- cleanup after the cutover
|
||||
- explicit tests making cars park at 0 and max_dist, peds walk to 0 and max_dist
|
||||
- proper intersection policies, by seeing full view
|
||||
- time travel mode can be very smart
|
||||
- dupe code for initially spawning vs spawning when a trip leg starts.
|
||||
|
@ -2,9 +2,6 @@
|
||||
|
||||
## Fix existing stuff
|
||||
|
||||
- try showing traffic signals by little boxes at the end of lanes
|
||||
- red circle means right turn on red OK, red right arrow means nope, green means normal turns ok, green arrow means protected left, crosswalk hand or stick figure
|
||||
|
||||
- if a lane could feasibly have multiple turn options but doesnt, print "ONLY"
|
||||
- audit all panics
|
||||
- tune text color, size, padding
|
||||
@ -15,8 +12,6 @@
|
||||
- yellow or flashing red/yellow for yields
|
||||
- text box entry: highlight char looks like replace mode; draw it btwn chars
|
||||
|
||||
- traffic signal cycles go offscreen sometimes!
|
||||
|
||||
- navigator
|
||||
- show options on map
|
||||
- stop jumping text size
|
||||
@ -27,7 +22,6 @@
|
||||
- when dragging, dont give mouse movement to UI elements
|
||||
- start context menu when left click releases and we're not dragging
|
||||
- can we change labels in modal or top menu? show/hide
|
||||
- label sections of modal menus
|
||||
- distinguish hints from status of modal menus, for hiding purposes
|
||||
- move context menus out of ezgui
|
||||
- simplify/remove UserInput.
|
||||
@ -43,8 +37,6 @@
|
||||
## Better rendering
|
||||
|
||||
- depict residential bldg occupany size somehow
|
||||
- render overlapping peds reasonably
|
||||
- draw moving / blocked colors (gradually more red as they wait longer)
|
||||
- render cars with textures?
|
||||
- rooftops
|
||||
- https://thumbs.dreamstime.com/b/top-view-city-street-asphalt-transport-people-walking-down-sidewalk-intersecting-road-pedestrian-81034411.jpg
|
||||
|
@ -76,6 +76,7 @@ impl RoadColorerBuilder {
|
||||
pub struct ObjectColorerBuilder {
|
||||
zoomed_override_colors: HashMap<ID, Color>,
|
||||
legend: ColorLegend,
|
||||
roads: Vec<(RoadID, Color)>,
|
||||
}
|
||||
|
||||
pub struct ObjectColorer {
|
||||
@ -104,14 +105,19 @@ impl ObjectColorerBuilder {
|
||||
ObjectColorerBuilder {
|
||||
zoomed_override_colors: HashMap::new(),
|
||||
legend: ColorLegend::new(title, rows),
|
||||
roads: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, id: ID, color: Color) {
|
||||
if let ID::Road(r) = id {
|
||||
self.roads.push((r, color));
|
||||
} else {
|
||||
self.zoomed_override_colors.insert(id, color);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self, ctx: &mut EventCtx, map: &Map) -> ObjectColorer {
|
||||
pub fn build(mut self, ctx: &mut EventCtx, map: &Map) -> ObjectColorer {
|
||||
let mut batch = GeomBatch::new();
|
||||
for (id, color) in &self.zoomed_override_colors {
|
||||
let poly = match id {
|
||||
@ -121,6 +127,12 @@ impl ObjectColorerBuilder {
|
||||
};
|
||||
batch.push(*color, poly);
|
||||
}
|
||||
for (r, color) in self.roads {
|
||||
batch.push(color, map.get_r(r).get_thick_polygon().unwrap());
|
||||
for l in map.get_r(r).all_lanes() {
|
||||
self.zoomed_override_colors.insert(ID::Lane(l), color);
|
||||
}
|
||||
}
|
||||
ObjectColorer {
|
||||
zoomed_override_colors: self.zoomed_override_colors,
|
||||
unzoomed: ctx.prerender.upload(batch),
|
||||
|
@ -1,7 +1,8 @@
|
||||
mod score;
|
||||
mod spawner;
|
||||
mod stats;
|
||||
mod thruput_stats;
|
||||
mod time_travel;
|
||||
mod trip_stats;
|
||||
|
||||
use crate::common::{
|
||||
time_controls, AgentTools, CommonState, ObjectColorer, ObjectColorerBuilder, RoadColorer,
|
||||
@ -25,7 +26,8 @@ pub struct SandboxMode {
|
||||
speed: SpeedControls,
|
||||
agent_tools: AgentTools,
|
||||
pub time_travel: time_travel::InactiveTimeTravel,
|
||||
stats: stats::TripStats,
|
||||
trip_stats: trip_stats::TripStats,
|
||||
thruput_stats: thruput_stats::ThruputStats,
|
||||
common: CommonState,
|
||||
parking_heatmap: Option<(Duration, RoadColorer)>,
|
||||
intersection_delay_heatmap: Option<(Duration, ObjectColorer)>,
|
||||
@ -38,7 +40,10 @@ impl SandboxMode {
|
||||
speed: SpeedControls::new(ctx, None),
|
||||
agent_tools: AgentTools::new(ctx),
|
||||
time_travel: time_travel::InactiveTimeTravel::new(),
|
||||
stats: stats::TripStats::new(ui.primary.current_flags.sim_flags.opts.record_stats),
|
||||
trip_stats: trip_stats::TripStats::new(
|
||||
ui.primary.current_flags.sim_flags.opts.record_stats,
|
||||
),
|
||||
thruput_stats: thruput_stats::ThruputStats::new(),
|
||||
common: CommonState::new(),
|
||||
parking_heatmap: None,
|
||||
intersection_delay_heatmap: None,
|
||||
@ -68,6 +73,7 @@ impl SandboxMode {
|
||||
(hotkey(Key::T), "start time traveling"),
|
||||
(hotkey(Key::Q), "scoreboard"),
|
||||
(None, "trip stats"),
|
||||
(None, "throughput stats"),
|
||||
],
|
||||
vec![
|
||||
(hotkey(Key::Escape), "quit"),
|
||||
@ -88,7 +94,8 @@ impl SandboxMode {
|
||||
impl State for SandboxMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
|
||||
self.time_travel.record(ui);
|
||||
self.stats.record(ui);
|
||||
self.trip_stats.record(ui);
|
||||
self.thruput_stats.record(ui);
|
||||
|
||||
{
|
||||
let mut txt = Text::prompt("Sandbox Mode");
|
||||
@ -124,7 +131,18 @@ impl State for SandboxMode {
|
||||
return Transition::Push(Box::new(score::Scoreboard::new(ctx, ui)));
|
||||
}
|
||||
if self.menu.action("trip stats") {
|
||||
return Transition::Push(Box::new(stats::ShowStats::new(&self.stats, ui, ctx)));
|
||||
if let Some(s) = trip_stats::ShowStats::new(&self.trip_stats, ui, ctx) {
|
||||
return Transition::Push(Box::new(s));
|
||||
} else {
|
||||
println!("No trip stats available");
|
||||
}
|
||||
}
|
||||
if self.menu.action("throughput stats") {
|
||||
return Transition::Push(Box::new(thruput_stats::ShowStats::new(
|
||||
&self.thruput_stats,
|
||||
ui,
|
||||
ctx,
|
||||
)));
|
||||
}
|
||||
if self.menu.action("show/hide parking availability") {
|
||||
if self.parking_heatmap.is_some() {
|
||||
|
123
game/src/sandbox/thruput_stats.rs
Normal file
123
game/src/sandbox/thruput_stats.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use crate::common::{ObjectColorer, ObjectColorerBuilder};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::ui::UI;
|
||||
use abstutil::Counter;
|
||||
use ezgui::{hotkey, Color, EventCtx, GfxCtx, Key, ModalMenu};
|
||||
use map_model::{IntersectionID, RoadID, Traversable};
|
||||
use sim::Event;
|
||||
|
||||
pub struct ThruputStats {
|
||||
count_per_road: Counter<RoadID>,
|
||||
count_per_intersection: Counter<IntersectionID>,
|
||||
}
|
||||
|
||||
impl ThruputStats {
|
||||
pub fn new() -> ThruputStats {
|
||||
ThruputStats {
|
||||
count_per_road: Counter::new(),
|
||||
count_per_intersection: Counter::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn record(&mut self, ui: &mut UI) {
|
||||
for ev in ui.primary.sim.collect_events() {
|
||||
if let Event::AgentEntersTraversable(_, to) = ev {
|
||||
match to {
|
||||
Traversable::Lane(l) => self.count_per_road.inc(ui.primary.map.get_l(l).parent),
|
||||
Traversable::Turn(t) => self.count_per_intersection.inc(t.parent),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShowStats {
|
||||
menu: ModalMenu,
|
||||
heatmap: ObjectColorer,
|
||||
}
|
||||
|
||||
impl State for ShowStats {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if ctx.redo_mouseover() {
|
||||
ui.recalculate_current_selection(ctx);
|
||||
}
|
||||
|
||||
self.menu.handle_event(ctx, None);
|
||||
if self.menu.action("quit") {
|
||||
return Transition::Pop;
|
||||
}
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw_default_ui(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
self.heatmap.draw(g, ui);
|
||||
self.menu.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowStats {
|
||||
pub fn new(stats: &ThruputStats, ui: &UI, ctx: &mut EventCtx) -> ShowStats {
|
||||
let light = Color::GREEN;
|
||||
let medium = Color::YELLOW;
|
||||
let heavy = Color::RED;
|
||||
let mut colorer = ObjectColorerBuilder::new(
|
||||
"Throughput",
|
||||
vec![
|
||||
("< 50%ile", light),
|
||||
("< 90%ile", medium),
|
||||
(">= 90%ile", heavy),
|
||||
],
|
||||
);
|
||||
|
||||
// TODO If there are many duplicate counts, arbitrarily some will look heavier! Find the
|
||||
// disribution of counts instead.
|
||||
// TODO Actually display the counts at these percentiles
|
||||
// TODO Dump the data in debug mode
|
||||
{
|
||||
let roads = stats.count_per_road.sorted_asc();
|
||||
let p50_idx = ((roads.len() as f64) * 0.5) as usize;
|
||||
let p90_idx = ((roads.len() as f64) * 0.9) as usize;
|
||||
for (idx, r) in roads.into_iter().enumerate() {
|
||||
let color = if idx < p50_idx {
|
||||
light
|
||||
} else if idx < p90_idx {
|
||||
medium
|
||||
} else {
|
||||
heavy
|
||||
};
|
||||
colorer.add(ID::Road(*r), color);
|
||||
}
|
||||
}
|
||||
// TODO dedupe
|
||||
{
|
||||
let intersections = stats.count_per_intersection.sorted_asc();
|
||||
let p50_idx = ((intersections.len() as f64) * 0.5) as usize;
|
||||
let p90_idx = ((intersections.len() as f64) * 0.9) as usize;
|
||||
for (idx, i) in intersections.into_iter().enumerate() {
|
||||
let color = if idx < p50_idx {
|
||||
light
|
||||
} else if idx < p90_idx {
|
||||
medium
|
||||
} else {
|
||||
heavy
|
||||
};
|
||||
colorer.add(ID::Intersection(*i), color);
|
||||
}
|
||||
}
|
||||
|
||||
ShowStats {
|
||||
menu: ModalMenu::new(
|
||||
"Thruput Stats",
|
||||
vec![vec![(hotkey(Key::Escape), "quit")]],
|
||||
ctx,
|
||||
),
|
||||
heatmap: colorer.build(ctx, &ui.primary.map),
|
||||
}
|
||||
}
|
||||
}
|
@ -104,7 +104,11 @@ impl State for ShowStats {
|
||||
}
|
||||
|
||||
impl ShowStats {
|
||||
pub fn new(stats: &TripStats, ui: &UI, ctx: &mut EventCtx) -> ShowStats {
|
||||
pub fn new(stats: &TripStats, ui: &UI, ctx: &mut EventCtx) -> Option<ShowStats> {
|
||||
if stats.samples.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
let mut labels = MultiText::new();
|
||||
|
||||
@ -184,9 +188,6 @@ impl ShowStats {
|
||||
}
|
||||
|
||||
for (_, color, getter) in lines {
|
||||
if stats.samples.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut pts = Vec::new();
|
||||
if max_y == 0 {
|
||||
pts.push(Pt2D::new(x1, y2));
|
||||
@ -217,12 +218,12 @@ impl ShowStats {
|
||||
"{} samples",
|
||||
abstutil::prettyprint_usize(stats.samples.len())
|
||||
)));
|
||||
ShowStats {
|
||||
Some(ShowStats {
|
||||
menu: ModalMenu::new("Trip Stats", vec![vec![(hotkey(Key::Escape), "quit")]], ctx)
|
||||
.set_prompt(ctx, txt),
|
||||
draw: ctx.prerender.upload(batch),
|
||||
labels,
|
||||
legend,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -19,6 +19,5 @@ pub enum Event {
|
||||
|
||||
BikeStoppedAtSidewalk(CarID, LaneID),
|
||||
|
||||
// TODO Remove this one
|
||||
AgentEntersTraversable(AgentID, Traversable),
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::mechanics::car::{Car, CarState};
|
||||
use crate::mechanics::queue::Queue;
|
||||
use crate::{
|
||||
ActionAtEnd, AgentID, CarID, Command, CreateCar, DistanceInterval, DrawCarInput,
|
||||
ActionAtEnd, AgentID, CarID, Command, CreateCar, DistanceInterval, DrawCarInput, Event,
|
||||
IntersectionSimState, ParkedCar, ParkingSimState, Scheduler, TimeInterval, TransitSimState,
|
||||
TripManager, TripPositions, UnzoomedAgent, WalkingSimState, FOLLOWING_DISTANCE,
|
||||
};
|
||||
@ -32,6 +32,7 @@ pub struct DrivingSimState {
|
||||
deserialize_with = "deserialize_btreemap"
|
||||
)]
|
||||
queues: BTreeMap<Traversable, Queue>,
|
||||
events: Vec<Event>,
|
||||
}
|
||||
|
||||
impl DrivingSimState {
|
||||
@ -39,6 +40,7 @@ impl DrivingSimState {
|
||||
let mut sim = DrivingSimState {
|
||||
cars: BTreeMap::new(),
|
||||
queues: BTreeMap::new(),
|
||||
events: Vec::new(),
|
||||
};
|
||||
|
||||
for l in map.all_lanes() {
|
||||
@ -342,6 +344,10 @@ impl DrivingSimState {
|
||||
car.state = car.crossing_state(Distance::ZERO, now, map);
|
||||
car.blocked_since = None;
|
||||
scheduler.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
||||
self.events.push(Event::AgentEntersTraversable(
|
||||
AgentID::Car(car.vehicle.id),
|
||||
goto,
|
||||
));
|
||||
|
||||
car.last_steps.push_front(last_step);
|
||||
if goto.length(map) >= car.vehicle.length + FOLLOWING_DISTANCE {
|
||||
@ -899,4 +905,8 @@ impl DrivingSimState {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn collect_events(&mut self) -> Vec<Event> {
|
||||
std::mem::replace(&mut self.events, Vec::new())
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
AgentID, AgentMetadata, Command, CreatePedestrian, DistanceInterval, DrawPedCrowdInput,
|
||||
DrawPedestrianInput, IntersectionSimState, ParkingSimState, ParkingSpot, PedestrianID,
|
||||
DrawPedestrianInput, Event, IntersectionSimState, ParkingSimState, ParkingSpot, PedestrianID,
|
||||
Scheduler, SidewalkPOI, SidewalkSpot, TimeInterval, TransitSimState, TripID, TripManager,
|
||||
TripPositions, UnzoomedAgent,
|
||||
};
|
||||
@ -22,6 +22,7 @@ pub struct WalkingSimState {
|
||||
deserialize_with = "deserialize_multimap"
|
||||
)]
|
||||
peds_per_traversable: MultiMap<Traversable, PedestrianID>,
|
||||
events: Vec<Event>,
|
||||
}
|
||||
|
||||
impl WalkingSimState {
|
||||
@ -29,6 +30,7 @@ impl WalkingSimState {
|
||||
WalkingSimState {
|
||||
peds: BTreeMap::new(),
|
||||
peds_per_traversable: MultiMap::new(),
|
||||
events: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,6 +166,7 @@ impl WalkingSimState {
|
||||
map,
|
||||
intersections,
|
||||
&mut self.peds_per_traversable,
|
||||
&mut self.events,
|
||||
scheduler,
|
||||
) {
|
||||
scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id));
|
||||
@ -180,6 +183,7 @@ impl WalkingSimState {
|
||||
map,
|
||||
intersections,
|
||||
&mut self.peds_per_traversable,
|
||||
&mut self.events,
|
||||
scheduler,
|
||||
) {
|
||||
scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id));
|
||||
@ -377,6 +381,10 @@ impl WalkingSimState {
|
||||
|
||||
(loners, crowds)
|
||||
}
|
||||
|
||||
pub fn collect_events(&mut self) -> Vec<Event> {
|
||||
std::mem::replace(&mut self.events, Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq)]
|
||||
@ -529,6 +537,7 @@ impl Pedestrian {
|
||||
map: &Map,
|
||||
intersections: &mut IntersectionSimState,
|
||||
peds_per_traversable: &mut MultiMap<Traversable, PedestrianID>,
|
||||
events: &mut Vec<Event>,
|
||||
scheduler: &mut Scheduler,
|
||||
) -> bool {
|
||||
if let PathStep::Turn(t) = self.path.next_step() {
|
||||
@ -554,6 +563,10 @@ impl Pedestrian {
|
||||
};
|
||||
self.state = self.crossing_state(start_dist, now, map);
|
||||
peds_per_traversable.insert(self.path.current_step().as_traversable(), self.id);
|
||||
events.push(Event::AgentEntersTraversable(
|
||||
AgentID::Pedestrian(self.id),
|
||||
self.path.current_step().as_traversable(),
|
||||
));
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -50,10 +50,7 @@ pub struct Sim {
|
||||
#[derivative(PartialEq = "ignore")]
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
trip_positions: Option<TripPositions>,
|
||||
|
||||
#[derivative(PartialEq = "ignore")]
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
events_since_last_step: Vec<Event>,
|
||||
// TODO Maybe the buffered events in child objects should also have this.
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -112,7 +109,6 @@ impl Sim {
|
||||
run_name: opts.run_name,
|
||||
step_count: 0,
|
||||
trip_positions: None,
|
||||
events_since_last_step: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -518,12 +514,6 @@ impl Sim {
|
||||
self.time = target_time;
|
||||
|
||||
self.trip_positions = None;
|
||||
|
||||
self.events_since_last_step.clear();
|
||||
self.events_since_last_step
|
||||
.extend(self.trips.collect_events());
|
||||
self.events_since_last_step
|
||||
.extend(self.transit.collect_events());
|
||||
}
|
||||
|
||||
pub fn timed_step(&mut self, map: &Map, dt: Duration, timer: &mut Timer) {
|
||||
@ -646,8 +636,8 @@ impl Sim {
|
||||
|
||||
let mut expectations = VecDeque::from(all_expectations);
|
||||
self.step(&map, self.time() + time_limit);
|
||||
for ev in self.get_events_since_last_step() {
|
||||
if ev == expectations.front().unwrap() {
|
||||
for ev in self.collect_events() {
|
||||
if &ev == expectations.front().unwrap() {
|
||||
println!("At {}, met expectation {:?}", self.time, ev);
|
||||
expectations.pop_front();
|
||||
if expectations.is_empty() {
|
||||
@ -864,8 +854,15 @@ impl Sim {
|
||||
self.trip_positions.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_events_since_last_step(&self) -> &Vec<Event> {
|
||||
&self.events_since_last_step
|
||||
// This only supports one caller! And the result isn't time-sorted.
|
||||
// TODO If nobody calls this, slow sad memory leak. Push style would probably be much nicer.
|
||||
pub fn collect_events(&mut self) -> Vec<Event> {
|
||||
let mut events = Vec::new();
|
||||
events.extend(self.trips.collect_events());
|
||||
events.extend(self.transit.collect_events());
|
||||
events.extend(self.driving.collect_events());
|
||||
events.extend(self.walking.collect_events());
|
||||
events
|
||||
}
|
||||
|
||||
pub fn get_canonical_pt_per_trip(&self, trip: TripID, map: &Map) -> TripResult<Pt2D> {
|
||||
|
@ -483,7 +483,7 @@ impl TripManager {
|
||||
}
|
||||
|
||||
pub fn collect_events(&mut self) -> Vec<Event> {
|
||||
self.events.drain(..).collect()
|
||||
std::mem::replace(&mut self.events, Vec::new())
|
||||
}
|
||||
|
||||
pub fn trip_status(&self, id: TripID) -> TripStatus {
|
||||
|
Loading…
Reference in New Issue
Block a user