changing the activity plugin to show a heatmap of what's currently in view

This commit is contained in:
Dustin Carlino 2018-11-01 15:38:46 -07:00
parent a610575701
commit db34ddb2b7
5 changed files with 91 additions and 54 deletions

View File

@ -1,14 +1,14 @@
use ezgui::Color;
use map_model::LaneID;
use objects::{Ctx, DEBUG, ID};
use ezgui::{Color, GfxCtx};
use geom::{Bounds, Pt2D};
use map_model::Map;
use objects::{Ctx, DEBUG};
use piston::input::Key;
use plugins::{Plugin, PluginCtx};
use sim::Tick;
use std::collections::HashSet;
use sim::{Sim, Tick};
pub enum ShowActivityState {
Inactive,
Active(Tick, HashSet<LaneID>),
Active(Tick, Heatmap),
}
impl ShowActivityState {
@ -29,21 +29,26 @@ impl Plugin for ShowActivityState {
) {
new_state = Some(ShowActivityState::Active(
ctx.primary.sim.time,
ctx.primary.sim.find_lanes_with_movement(),
active_agent_heatmap(
ctx.canvas.get_screen_bounds(),
&ctx.primary.sim,
&ctx.primary.map,
),
));
}
}
ShowActivityState::Active(time, _) => {
ShowActivityState::Active(time, ref old_heatmap) => {
if ctx
.input
.key_pressed(Key::Return, "stop showing lanes with active traffic")
{
new_state = Some(ShowActivityState::Inactive);
}
if *time != ctx.primary.sim.time {
let bounds = ctx.canvas.get_screen_bounds();
if *time != ctx.primary.sim.time || bounds != old_heatmap.bounds {
new_state = Some(ShowActivityState::Active(
ctx.primary.sim.time,
ctx.primary.sim.find_lanes_with_movement(),
active_agent_heatmap(bounds, &ctx.primary.sim, &ctx.primary.map),
));
}
}
@ -57,18 +62,81 @@ impl Plugin for ShowActivityState {
}
}
fn color_for(&self, obj: ID, ctx: Ctx) -> Option<Color> {
match (obj, self) {
(ID::Lane(id), ShowActivityState::Active(_, ref lanes)) => {
if lanes.contains(&id) {
None
} else {
// TODO I want to modify the color that'd happen anyway and just make it more
// transparent. But how?
Some(ctx.cs.get("inactive lane", Color::rgba(0, 0, 0, 0.2)))
}
}
_ => None,
fn draw(&self, g: &mut GfxCtx, _ctx: Ctx) {
if let ShowActivityState::Active(_, ref heatmap) = self {
heatmap.draw(g);
}
}
}
// A nice 10x10
const NUM_TILES: usize = 10;
pub struct Heatmap {
bounds: Bounds,
counts: [[usize; NUM_TILES]; NUM_TILES],
max: usize,
}
impl Heatmap {
fn new(bounds: Bounds) -> Heatmap {
Heatmap {
bounds,
counts: [[0; NUM_TILES]; NUM_TILES],
max: 0,
}
}
fn add(&mut self, pt: Pt2D) {
// TODO Could also query sim with this filter
if !self.bounds.contains(pt) {
return;
}
let x = ((pt.x() - self.bounds.min_x) / (self.bounds.max_x - self.bounds.min_x)
* (NUM_TILES as f64))
.floor() as usize;
let y = ((pt.y() - self.bounds.min_y) / (self.bounds.max_y - self.bounds.min_y)
* (NUM_TILES as f64))
.floor() as usize;
self.counts[x][y] += 1;
self.max = self.max.max(self.counts[x][y]);
}
fn draw(&self, g: &mut GfxCtx) {
let tile_width = (self.bounds.max_x - self.bounds.min_x) / (NUM_TILES as f64);
let tile_height = (self.bounds.max_y - self.bounds.min_y) / (NUM_TILES as f64);
for x in 0..NUM_TILES {
for y in 0..NUM_TILES {
if self.counts[x][y] == 0 {
continue;
}
let percent = (self.counts[x][y] as f32) / (self.max as f32);
// TODO Map percent to hot/cold colors
let color = Color::rgba(255, 0, 0, percent);
g.draw_rectangle(
color,
[
(x as f64) * tile_width,
(y as f64) * tile_height,
tile_width,
tile_height,
],
);
}
}
}
}
fn active_agent_heatmap(bounds: Bounds, sim: &Sim, map: &Map) -> Heatmap {
let mut h = Heatmap::new(bounds);
for trip in sim.get_active_trips().into_iter() {
if let Some(pt) = sim.get_canonical_point_for_trip(trip, map) {
h.add(pt);
}
}
h
}

View File

@ -144,7 +144,7 @@ impl From<Pt2D> for HashablePt2D {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Bounds {
pub min_x: f64,
pub min_y: f64,

View File

@ -874,17 +874,6 @@ impl DrivingSimState {
(moving_cars, stuck_cars, buses)
}
pub fn find_lanes_with_movement(&self, active: &mut HashSet<LaneID>) {
for c in self.cars.values() {
if c.speed > kinematics::EPSILON_SPEED {
match c.on {
Traversable::Lane(id) => active.insert(id),
Traversable::Turn(t) => active.insert(t.src),
};
}
}
}
}
pub struct CreateCar {

View File

@ -452,15 +452,6 @@ impl Sim {
trips_with_ab_test_divergence: 0,
}
}
// TODO This is another query like summarize()
// Turns count as activity on the origin lane
pub fn find_lanes_with_movement(&self) -> HashSet<LaneID> {
let mut active: HashSet<LaneID> = HashSet::new();
self.driving_state.find_lanes_with_movement(&mut active);
self.walking_state.find_lanes_with_movement(&mut active);
active
}
}
pub struct Benchmark {

View File

@ -680,17 +680,6 @@ impl WalkingSimState {
(moving_peds, stuck_peds)
}
pub fn find_lanes_with_movement(&self, active: &mut HashSet<LaneID>) {
for p in self.peds.values() {
if p.waiting_for.is_none() {
match p.on {
Traversable::Lane(id) => active.insert(id),
Traversable::Turn(t) => active.insert(t.src),
};
}
}
}
}
fn is_contraflow(map: &Map, from: LaneID, to: LaneID) -> bool {