Make the blocked-by graph more exploreable. Remove older debug code that

did something similar. #392
This commit is contained in:
Dustin Carlino 2020-11-30 15:15:13 -08:00
parent 6dd14c8bed
commit 8501ec70f9
5 changed files with 77 additions and 180 deletions

View File

@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use geom::{ArrowCap, Distance, Duration, PolyLine, Pt2D};
use geom::{ArrowCap, Distance, Duration, PolyLine, Polygon, Pt2D};
use sim::{AgentID, DelayCause};
use widgetry::{
Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Line, Outcome, Panel,
@ -15,43 +15,22 @@ use crate::common::CommonState;
pub struct Viewer {
panel: Panel,
graph: BTreeMap<AgentID, (Duration, DelayCause)>,
agent_positions: BTreeMap<AgentID, Pt2D>,
arrows: Drawable,
}
impl Viewer {
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
let graph = app.primary.sim.get_blocked_by_graph(&app.primary.map);
let agent_positions: BTreeMap<AgentID, Pt2D> = app
.primary
.sim
.get_unzoomed_agents(&app.primary.map)
.into_iter()
.map(|a| (a.id, a.pos))
.collect();
let mut arrows = GeomBatch::new();
for (id, (_, cause)) in &graph {
let (to, color) = match cause {
DelayCause::Agent(a) => {
if let Some(pos) = agent_positions.get(a) {
(*pos, Color::RED)
} else {
warn!("{} blocked by {}, but they're gone?", id, a);
continue;
}
}
DelayCause::Intersection(i) => {
(app.primary.map.get_i(*i).polygon.center(), Color::BLUE)
}
};
let arrow = PolyLine::must_new(vec![agent_positions[id], to])
.make_arrow(Distance::meters(0.5), ArrowCap::Triangle);
arrows.push(color.alpha(0.5), arrow);
}
Box::new(Viewer {
graph,
arrows: ctx.upload(arrows),
let mut viewer = Viewer {
graph: app.primary.sim.get_blocked_by_graph(&app.primary.map),
agent_positions: app
.primary
.sim
.get_unzoomed_agents(&app.primary.map)
.into_iter()
.map(|a| (a.id, a.pos))
.collect(),
arrows: Drawable::empty(ctx),
panel: Panel::new(
Widget::row(vec![
Line("What agents are blocked by others?")
@ -63,7 +42,36 @@ impl Viewer {
)
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
.build(ctx),
})
};
let mut arrows = GeomBatch::new();
for id in viewer.agent_positions.keys() {
if let Some((arrow, color)) = viewer.arrow_for(app, *id) {
arrows.push(color.alpha(0.5), arrow);
}
}
viewer.arrows = ctx.upload(arrows);
Box::new(viewer)
}
fn arrow_for(&self, app: &App, id: AgentID) -> Option<(Polygon, Color)> {
let (_, cause) = self.graph.get(&id)?;
let (to, color) = match cause {
DelayCause::Agent(a) => {
if let Some(pos) = self.agent_positions.get(a) {
(*pos, Color::RED)
} else {
warn!("{} blocked by {}, but they're gone?", id, a);
return None;
}
}
DelayCause::Intersection(i) => {
(app.primary.map.get_i(*i).polygon.center(), Color::BLUE)
}
};
let arrow = PolyLine::must_new(vec![self.agent_positions[&id], to])
.make_arrow(Distance::meters(0.5), ArrowCap::Triangle);
Some((arrow, color))
}
}
@ -101,6 +109,9 @@ impl State<App> for Viewer {
if let Some((delay, _)) = self.graph.get(&id) {
g.draw_mouse_tooltip(Text::from(Line(format!("Waiting {}", delay))));
}
if let Some((arrow, _)) = self.arrow_for(app, id) {
g.draw_polygon(Color::CYAN, arrow);
}
}
}
}

View File

@ -6,9 +6,9 @@ use map_gui::load::MapLoader;
use map_gui::options::OptionsPanel;
use map_gui::render::{calculate_corners, DrawOptions};
use map_gui::tools::{ChooseSomething, PopupMsg, PromptInput};
use map_gui::ID;
use map_model::{osm, ControlTrafficSignal, NORMAL_LANE_THICKNESS};
use sim::{AgentID, Sim};
use map_gui::{Cached, ID};
use map_model::{osm, ControlTrafficSignal, IntersectionID, NORMAL_LANE_THICKNESS};
use sim::Sim;
use widgetry::{
lctrl, Btn, Checkbox, Choice, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
HorizontalAlignment, Key, Line, Outcome, Panel, State, Text, UpdateType, VerticalAlignment,
@ -38,7 +38,7 @@ pub struct DebugMode {
search_results: Option<SearchResults>,
all_routes: Option<(usize, Drawable)>,
highlighted_agents: Option<(ID, Drawable)>,
highlighted_agents: Cached<IntersectionID, Drawable>,
}
impl DebugMode {
@ -88,7 +88,7 @@ impl DebugMode {
layers: ShowLayers::new(),
search_results: None,
all_routes: None,
highlighted_agents: None,
highlighted_agents: Cached::new(),
})
}
@ -281,53 +281,31 @@ impl State<App> for DebugMode {
_ => {}
}
match app.primary.current_selection {
Some(ID::Intersection(_)) | Some(ID::Car(_)) => {
let id = app.primary.current_selection.clone().unwrap();
if self
.highlighted_agents
.as_ref()
.map(|(x, _)| *x != id)
.unwrap_or(true)
{
let mut batch = GeomBatch::new();
let agents = match id {
ID::Intersection(i) => app
.primary
.sim
.get_accepted_agents(i)
.into_iter()
.map(|(a, _)| a)
.collect(),
ID::Car(c) => app.primary.sim.get_blocked_by(AgentID::Car(c)),
_ => unreachable!(),
};
for a in agents {
if let Some(obj) = app.primary.draw_map.get_obj(
ctx,
ID::from_agent(a),
app,
&mut app.primary.agents.borrow_mut(),
) {
batch.push(Color::PURPLE, obj.get_outline(&app.primary.map));
} else {
return Transition::Push(PopupMsg::new(
ctx,
"Wat",
vec![format!(
"{} is accepted at or blocked by by {:?}, but no longer exists",
a, id
)],
));
}
self.highlighted_agents.update(
match app.primary.current_selection {
Some(ID::Intersection(i)) => Some(i),
_ => None,
},
|key| {
let mut batch = GeomBatch::new();
for (a, _) in app.primary.sim.get_accepted_agents(key) {
if let Some(obj) = app.primary.draw_map.get_obj(
ctx,
ID::from_agent(a),
app,
&mut app.primary.agents.borrow_mut(),
) {
batch.push(Color::PURPLE, obj.get_outline(&app.primary.map));
} else {
warn!(
"{} is accepted at or blocked by by {:?}, but no longer exists",
a, key
);
}
self.highlighted_agents = Some((id, ctx.upload(batch)));
}
}
_ => {
self.highlighted_agents = None;
}
}
ctx.upload(batch)
},
);
if let Some(t) = self.common.event(ctx, app, &mut Actions {}) {
return t;
@ -354,7 +332,7 @@ impl State<App> for DebugMode {
if let Some(ref results) = self.search_results {
g.redraw(&results.draw);
}
if let Some((_, ref draw)) = self.highlighted_agents {
if let Some(draw) = self.highlighted_agents.value() {
g.redraw(draw);
}
@ -492,7 +470,6 @@ impl ContextualActions for Actions {
}
ID::Car(_) => {
actions.push((Key::Backspace, "forcibly delete this car".to_string()));
actions.push((Key::G, "find front of blockage".to_string()));
}
ID::Area(_) => {
actions.push((Key::X, "debug area geometry".to_string()));
@ -538,15 +515,6 @@ impl ContextualActions for Actions {
app.primary.current_selection = None;
Transition::Keep
}
(ID::Car(c), "find front of blockage") => Transition::Push(PopupMsg::new(
ctx,
"Blockage results",
vec![format!(
"{} is ultimately blocked by {}",
c,
app.primary.sim.find_blockage_front(c, &app.primary.map)
)],
)),
(ID::Lane(l), "floodfill from this lane") => {
Transition::Push(floodfill::Floodfiller::floodfill(ctx, app, l))
}

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use abstutil::{deserialize_hashmap, serialize_hashmap, FixedMap, IndexableKey};
use geom::{Distance, Duration, PolyLine, Speed, Time};
use map_model::{IntersectionID, LaneID, Map, Path, PathStep, Traversable};
use map_model::{IntersectionID, LaneID, Map, Path, Traversable};
use crate::mechanics::car::{Car, CarState};
use crate::mechanics::Queue;
@ -1044,68 +1044,6 @@ impl DrivingSimState {
car.vehicle.owner
}
// TODO Clean this up
pub fn find_blockage_front(
&self,
start: CarID,
map: &Map,
intersections: &IntersectionSimState,
) -> String {
let mut seen_intersections = HashSet::new();
let mut current_head = start;
let mut current_lane = match self.cars[&start].router.head() {
Traversable::Lane(l) => l,
Traversable::Turn(_) => {
return "TODO Doesn't support starting from a turn yet".to_string();
}
};
loop {
current_head =
if let Some(c) = self.queues[&Traversable::Lane(current_lane)].cars.get(0) {
*c
} else {
return format!("no gridlock, {}", current_head);
};
let i = map.get_l(current_lane).dst_i;
if seen_intersections.contains(&i) {
return format!("Gridlock near {}! {:?}", i, seen_intersections);
}
seen_intersections.insert(i);
// Why isn't current_head proceeding? Pedestrians can never get stuck in an
// intersection.
if intersections
.get_accepted_agents(i)
.iter()
.any(|(a, _)| matches!(a, AgentID::Car(_)))
{
return format!("someone's turning in {} still", i);
}
current_lane = if let Some(PathStep::Lane(l)) = self.cars[&current_head]
.router
.get_path()
.get_steps()
.get(2)
{
*l
} else {
return format!(
"{} is near end of path, probably tmp blockage",
current_head
);
};
// Lack of capacity?
if self.queues[&Traversable::Lane(current_lane)].room_for_car(&self.cars[&current_head])
{
return format!("{} is about to proceed, tmp blockage", current_head);
}
}
}
pub fn target_lane_penalty(&self, l: LaneID) -> (usize, usize) {
self.queues[&Traversable::Lane(l)].target_lane_penalty()
}

View File

@ -573,19 +573,7 @@ impl IntersectionSimState {
.collect()
}
pub fn get_blocked_by(&self, a: AgentID) -> HashSet<AgentID> {
let mut blocked_by = HashSet::new();
if let AgentID::Car(c) = a {
for (c1, c2) in &self.blocked_by {
if *c1 == c {
blocked_by.insert(AgentID::Car(*c2));
}
}
}
blocked_by
}
/// returns intersections with travelers waiting for at least `threshold` since `now`, ordered
/// Returns intersections with travelers waiting for at least `threshold` since `now`, ordered
/// so the longest delayed intersection is first.
pub fn delayed_intersections(
&self,

View File

@ -1,6 +1,6 @@
//! All sorts of read-only queries about a simulation
use std::collections::{BTreeMap, HashSet};
use std::collections::BTreeMap;
use abstutil::Counter;
use geom::{Distance, Duration, PolyLine, Pt2D, Time};
@ -263,9 +263,6 @@ impl Sim {
pub fn get_waiting_agents(&self, id: IntersectionID) -> Vec<(AgentID, TurnID, Time)> {
self.intersections.get_waiting_agents(id)
}
pub fn get_blocked_by(&self, a: AgentID) -> HashSet<AgentID> {
self.intersections.get_blocked_by(a)
}
/// For every agent that's currently not moving, figure out how long they've been waiting and
/// why they're blocked.
@ -297,11 +294,6 @@ impl Sim {
&self.analytics
}
pub fn find_blockage_front(&self, car: CarID, map: &Map) -> String {
self.driving
.find_blockage_front(car, map, &self.intersections)
}
/// For intersections with an agent waiting beyond some threshold, return when they started
/// waiting. Sorted by earliest waiting (likely the root cause of gridlock).
pub fn delayed_intersections(&self, threshold: Duration) -> Vec<(IntersectionID, Time)> {