basic heatmap showing busiest roads and intersections. have to revive

Sim Events kind of.
This commit is contained in:
Dustin Carlino 2019-10-04 15:33:26 -07:00
parent 9d35f0f92f
commit 1a10e8982a
13 changed files with 212 additions and 49 deletions

View File

@ -75,6 +75,12 @@ impl<T: Ord + PartialEq> Counter<T> {
pub fn get(&self, val: T) -> usize { pub fn get(&self, val: T) -> usize {
self.map.get(&val).cloned().unwrap_or(0) 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 { pub fn wraparound_get<T>(vec: &Vec<T>, idx: isize) -> &T {

View File

@ -3,7 +3,6 @@
- enable more clippy lints - enable more clippy lints
- enforce consistent style (derive order, struct initialization order) - enforce consistent style (derive order, struct initialization order)
- update with mission statement (democratized urb p, that quote, refashion existing space cheaply)
- trailer - trailer
- show common parts of routes in A/B, point of divergence - 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?" - "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?"

View File

@ -22,9 +22,6 @@
- deal with loop roads? - deal with loop roads?
- model U-turns - model U-turns
- degenerate-2's should only have one crosswalk
- then make them thinner
- car turns often clip sidewalk corners now - car turns often clip sidewalk corners now
- draw SharedSidewalkCorners just around the ped path, not arbitrarily thick - draw SharedSidewalkCorners just around the ped path, not arbitrarily thick
- dont forget to draw the notches - dont forget to draw the notches
@ -84,10 +81,6 @@
- just revert intersection and warn - just revert intersection and warn
- or store overrides more granularly and warn or do something reasonable - or store overrides more granularly and warn or do something reasonable
## Release
- publish the map data
## Sim bugs/tests needed ## Sim bugs/tests needed
- do bikes use bike lanes? - 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 - 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 - 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! - 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 ## Discrete-event sim model
- cleanup after the cutover - 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 - proper intersection policies, by seeing full view
- time travel mode can be very smart - time travel mode can be very smart
- dupe code for initially spawning vs spawning when a trip leg starts. - dupe code for initially spawning vs spawning when a trip leg starts.

View File

@ -2,9 +2,6 @@
## Fix existing stuff ## 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" - if a lane could feasibly have multiple turn options but doesnt, print "ONLY"
- audit all panics - audit all panics
- tune text color, size, padding - tune text color, size, padding
@ -15,8 +12,6 @@
- yellow or flashing red/yellow for yields - yellow or flashing red/yellow for yields
- text box entry: highlight char looks like replace mode; draw it btwn chars - text box entry: highlight char looks like replace mode; draw it btwn chars
- traffic signal cycles go offscreen sometimes!
- navigator - navigator
- show options on map - show options on map
- stop jumping text size - stop jumping text size
@ -27,7 +22,6 @@
- when dragging, dont give mouse movement to UI elements - when dragging, dont give mouse movement to UI elements
- start context menu when left click releases and we're not dragging - start context menu when left click releases and we're not dragging
- can we change labels in modal or top menu? show/hide - 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 - distinguish hints from status of modal menus, for hiding purposes
- move context menus out of ezgui - move context menus out of ezgui
- simplify/remove UserInput. - simplify/remove UserInput.
@ -43,8 +37,6 @@
## Better rendering ## Better rendering
- depict residential bldg occupany size somehow - 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? - render cars with textures?
- rooftops - rooftops
- https://thumbs.dreamstime.com/b/top-view-city-street-asphalt-transport-people-walking-down-sidewalk-intersecting-road-pedestrian-81034411.jpg - https://thumbs.dreamstime.com/b/top-view-city-street-asphalt-transport-people-walking-down-sidewalk-intersecting-road-pedestrian-81034411.jpg

View File

@ -76,6 +76,7 @@ impl RoadColorerBuilder {
pub struct ObjectColorerBuilder { pub struct ObjectColorerBuilder {
zoomed_override_colors: HashMap<ID, Color>, zoomed_override_colors: HashMap<ID, Color>,
legend: ColorLegend, legend: ColorLegend,
roads: Vec<(RoadID, Color)>,
} }
pub struct ObjectColorer { pub struct ObjectColorer {
@ -104,14 +105,19 @@ impl ObjectColorerBuilder {
ObjectColorerBuilder { ObjectColorerBuilder {
zoomed_override_colors: HashMap::new(), zoomed_override_colors: HashMap::new(),
legend: ColorLegend::new(title, rows), legend: ColorLegend::new(title, rows),
roads: Vec::new(),
} }
} }
pub fn add(&mut self, id: ID, color: Color) { pub fn add(&mut self, id: ID, color: Color) {
self.zoomed_override_colors.insert(id, 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(); let mut batch = GeomBatch::new();
for (id, color) in &self.zoomed_override_colors { for (id, color) in &self.zoomed_override_colors {
let poly = match id { let poly = match id {
@ -121,6 +127,12 @@ impl ObjectColorerBuilder {
}; };
batch.push(*color, poly); 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 { ObjectColorer {
zoomed_override_colors: self.zoomed_override_colors, zoomed_override_colors: self.zoomed_override_colors,
unzoomed: ctx.prerender.upload(batch), unzoomed: ctx.prerender.upload(batch),

View File

@ -1,7 +1,8 @@
mod score; mod score;
mod spawner; mod spawner;
mod stats; mod thruput_stats;
mod time_travel; mod time_travel;
mod trip_stats;
use crate::common::{ use crate::common::{
time_controls, AgentTools, CommonState, ObjectColorer, ObjectColorerBuilder, RoadColorer, time_controls, AgentTools, CommonState, ObjectColorer, ObjectColorerBuilder, RoadColorer,
@ -25,7 +26,8 @@ pub struct SandboxMode {
speed: SpeedControls, speed: SpeedControls,
agent_tools: AgentTools, agent_tools: AgentTools,
pub time_travel: time_travel::InactiveTimeTravel, pub time_travel: time_travel::InactiveTimeTravel,
stats: stats::TripStats, trip_stats: trip_stats::TripStats,
thruput_stats: thruput_stats::ThruputStats,
common: CommonState, common: CommonState,
parking_heatmap: Option<(Duration, RoadColorer)>, parking_heatmap: Option<(Duration, RoadColorer)>,
intersection_delay_heatmap: Option<(Duration, ObjectColorer)>, intersection_delay_heatmap: Option<(Duration, ObjectColorer)>,
@ -38,7 +40,10 @@ impl SandboxMode {
speed: SpeedControls::new(ctx, None), speed: SpeedControls::new(ctx, None),
agent_tools: AgentTools::new(ctx), agent_tools: AgentTools::new(ctx),
time_travel: time_travel::InactiveTimeTravel::new(), 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(), common: CommonState::new(),
parking_heatmap: None, parking_heatmap: None,
intersection_delay_heatmap: None, intersection_delay_heatmap: None,
@ -68,6 +73,7 @@ impl SandboxMode {
(hotkey(Key::T), "start time traveling"), (hotkey(Key::T), "start time traveling"),
(hotkey(Key::Q), "scoreboard"), (hotkey(Key::Q), "scoreboard"),
(None, "trip stats"), (None, "trip stats"),
(None, "throughput stats"),
], ],
vec![ vec![
(hotkey(Key::Escape), "quit"), (hotkey(Key::Escape), "quit"),
@ -88,7 +94,8 @@ impl SandboxMode {
impl State for SandboxMode { impl State for SandboxMode {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition { fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
self.time_travel.record(ui); 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"); 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))); return Transition::Push(Box::new(score::Scoreboard::new(ctx, ui)));
} }
if self.menu.action("trip stats") { 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.menu.action("show/hide parking availability") {
if self.parking_heatmap.is_some() { if self.parking_heatmap.is_some() {

View 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),
}
}
}

View File

@ -104,7 +104,11 @@ impl State for ShowStats {
} }
impl 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 batch = GeomBatch::new();
let mut labels = MultiText::new(); let mut labels = MultiText::new();
@ -184,9 +188,6 @@ impl ShowStats {
} }
for (_, color, getter) in lines { for (_, color, getter) in lines {
if stats.samples.is_empty() {
continue;
}
let mut pts = Vec::new(); let mut pts = Vec::new();
if max_y == 0 { if max_y == 0 {
pts.push(Pt2D::new(x1, y2)); pts.push(Pt2D::new(x1, y2));
@ -217,12 +218,12 @@ impl ShowStats {
"{} samples", "{} samples",
abstutil::prettyprint_usize(stats.samples.len()) abstutil::prettyprint_usize(stats.samples.len())
))); )));
ShowStats { Some(ShowStats {
menu: ModalMenu::new("Trip Stats", vec![vec![(hotkey(Key::Escape), "quit")]], ctx) menu: ModalMenu::new("Trip Stats", vec![vec![(hotkey(Key::Escape), "quit")]], ctx)
.set_prompt(ctx, txt), .set_prompt(ctx, txt),
draw: ctx.prerender.upload(batch), draw: ctx.prerender.upload(batch),
labels, labels,
legend, legend,
} })
} }
} }

View File

@ -19,6 +19,5 @@ pub enum Event {
BikeStoppedAtSidewalk(CarID, LaneID), BikeStoppedAtSidewalk(CarID, LaneID),
// TODO Remove this one
AgentEntersTraversable(AgentID, Traversable), AgentEntersTraversable(AgentID, Traversable),
} }

View File

@ -1,7 +1,7 @@
use crate::mechanics::car::{Car, CarState}; use crate::mechanics::car::{Car, CarState};
use crate::mechanics::queue::Queue; use crate::mechanics::queue::Queue;
use crate::{ use crate::{
ActionAtEnd, AgentID, CarID, Command, CreateCar, DistanceInterval, DrawCarInput, ActionAtEnd, AgentID, CarID, Command, CreateCar, DistanceInterval, DrawCarInput, Event,
IntersectionSimState, ParkedCar, ParkingSimState, Scheduler, TimeInterval, TransitSimState, IntersectionSimState, ParkedCar, ParkingSimState, Scheduler, TimeInterval, TransitSimState,
TripManager, TripPositions, UnzoomedAgent, WalkingSimState, FOLLOWING_DISTANCE, TripManager, TripPositions, UnzoomedAgent, WalkingSimState, FOLLOWING_DISTANCE,
}; };
@ -32,6 +32,7 @@ pub struct DrivingSimState {
deserialize_with = "deserialize_btreemap" deserialize_with = "deserialize_btreemap"
)] )]
queues: BTreeMap<Traversable, Queue>, queues: BTreeMap<Traversable, Queue>,
events: Vec<Event>,
} }
impl DrivingSimState { impl DrivingSimState {
@ -39,6 +40,7 @@ impl DrivingSimState {
let mut sim = DrivingSimState { let mut sim = DrivingSimState {
cars: BTreeMap::new(), cars: BTreeMap::new(),
queues: BTreeMap::new(), queues: BTreeMap::new(),
events: Vec::new(),
}; };
for l in map.all_lanes() { for l in map.all_lanes() {
@ -342,6 +344,10 @@ impl DrivingSimState {
car.state = car.crossing_state(Distance::ZERO, now, map); car.state = car.crossing_state(Distance::ZERO, now, map);
car.blocked_since = None; car.blocked_since = None;
scheduler.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id)); 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); car.last_steps.push_front(last_step);
if goto.length(map) >= car.vehicle.length + FOLLOWING_DISTANCE { if goto.length(map) >= car.vehicle.length + FOLLOWING_DISTANCE {
@ -899,4 +905,8 @@ impl DrivingSimState {
} }
false false
} }
pub fn collect_events(&mut self) -> Vec<Event> {
std::mem::replace(&mut self.events, Vec::new())
}
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
AgentID, AgentMetadata, Command, CreatePedestrian, DistanceInterval, DrawPedCrowdInput, AgentID, AgentMetadata, Command, CreatePedestrian, DistanceInterval, DrawPedCrowdInput,
DrawPedestrianInput, IntersectionSimState, ParkingSimState, ParkingSpot, PedestrianID, DrawPedestrianInput, Event, IntersectionSimState, ParkingSimState, ParkingSpot, PedestrianID,
Scheduler, SidewalkPOI, SidewalkSpot, TimeInterval, TransitSimState, TripID, TripManager, Scheduler, SidewalkPOI, SidewalkSpot, TimeInterval, TransitSimState, TripID, TripManager,
TripPositions, UnzoomedAgent, TripPositions, UnzoomedAgent,
}; };
@ -22,6 +22,7 @@ pub struct WalkingSimState {
deserialize_with = "deserialize_multimap" deserialize_with = "deserialize_multimap"
)] )]
peds_per_traversable: MultiMap<Traversable, PedestrianID>, peds_per_traversable: MultiMap<Traversable, PedestrianID>,
events: Vec<Event>,
} }
impl WalkingSimState { impl WalkingSimState {
@ -29,6 +30,7 @@ impl WalkingSimState {
WalkingSimState { WalkingSimState {
peds: BTreeMap::new(), peds: BTreeMap::new(),
peds_per_traversable: MultiMap::new(), peds_per_traversable: MultiMap::new(),
events: Vec::new(),
} }
} }
@ -164,6 +166,7 @@ impl WalkingSimState {
map, map,
intersections, intersections,
&mut self.peds_per_traversable, &mut self.peds_per_traversable,
&mut self.events,
scheduler, scheduler,
) { ) {
scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id)); scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id));
@ -180,6 +183,7 @@ impl WalkingSimState {
map, map,
intersections, intersections,
&mut self.peds_per_traversable, &mut self.peds_per_traversable,
&mut self.events,
scheduler, scheduler,
) { ) {
scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id)); scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id));
@ -377,6 +381,10 @@ impl WalkingSimState {
(loners, crowds) (loners, crowds)
} }
pub fn collect_events(&mut self) -> Vec<Event> {
std::mem::replace(&mut self.events, Vec::new())
}
} }
#[derive(Serialize, Deserialize, PartialEq)] #[derive(Serialize, Deserialize, PartialEq)]
@ -529,6 +537,7 @@ impl Pedestrian {
map: &Map, map: &Map,
intersections: &mut IntersectionSimState, intersections: &mut IntersectionSimState,
peds_per_traversable: &mut MultiMap<Traversable, PedestrianID>, peds_per_traversable: &mut MultiMap<Traversable, PedestrianID>,
events: &mut Vec<Event>,
scheduler: &mut Scheduler, scheduler: &mut Scheduler,
) -> bool { ) -> bool {
if let PathStep::Turn(t) = self.path.next_step() { if let PathStep::Turn(t) = self.path.next_step() {
@ -554,6 +563,10 @@ impl Pedestrian {
}; };
self.state = self.crossing_state(start_dist, now, map); self.state = self.crossing_state(start_dist, now, map);
peds_per_traversable.insert(self.path.current_step().as_traversable(), self.id); 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 true
} }
} }

View File

@ -50,10 +50,7 @@ pub struct Sim {
#[derivative(PartialEq = "ignore")] #[derivative(PartialEq = "ignore")]
#[serde(skip_serializing, skip_deserializing)] #[serde(skip_serializing, skip_deserializing)]
trip_positions: Option<TripPositions>, trip_positions: Option<TripPositions>,
// TODO Maybe the buffered events in child objects should also have this.
#[derivative(PartialEq = "ignore")]
#[serde(skip_serializing, skip_deserializing)]
events_since_last_step: Vec<Event>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -112,7 +109,6 @@ impl Sim {
run_name: opts.run_name, run_name: opts.run_name,
step_count: 0, step_count: 0,
trip_positions: None, trip_positions: None,
events_since_last_step: Vec::new(),
} }
} }
@ -518,12 +514,6 @@ impl Sim {
self.time = target_time; self.time = target_time;
self.trip_positions = None; 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) { 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); let mut expectations = VecDeque::from(all_expectations);
self.step(&map, self.time() + time_limit); self.step(&map, self.time() + time_limit);
for ev in self.get_events_since_last_step() { for ev in self.collect_events() {
if ev == expectations.front().unwrap() { if &ev == expectations.front().unwrap() {
println!("At {}, met expectation {:?}", self.time, ev); println!("At {}, met expectation {:?}", self.time, ev);
expectations.pop_front(); expectations.pop_front();
if expectations.is_empty() { if expectations.is_empty() {
@ -864,8 +854,15 @@ impl Sim {
self.trip_positions.as_ref().unwrap() self.trip_positions.as_ref().unwrap()
} }
pub fn get_events_since_last_step(&self) -> &Vec<Event> { // This only supports one caller! And the result isn't time-sorted.
&self.events_since_last_step // 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> { pub fn get_canonical_pt_per_trip(&self, trip: TripID, map: &Map) -> TripResult<Pt2D> {

View File

@ -483,7 +483,7 @@ impl TripManager {
} }
pub fn collect_events(&mut self) -> Vec<Event> { 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 { pub fn trip_status(&self, id: TripID) -> TripStatus {