mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
Make the blocked-by graph more exploreable. Remove older debug code that
did something similar. #392
This commit is contained in:
parent
6dd14c8bed
commit
8501ec70f9
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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[¤t_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[¤t_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()
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)> {
|
||||
|
Loading…
Reference in New Issue
Block a user