1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::collections::BTreeMap;
use geom::{ArrowCap, Distance, Duration, PolyLine, Polygon, Pt2D};
use sim::{AgentID, DelayCause};
use widgetry::{
Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Line, Outcome, Panel,
State, Text, VerticalAlignment, Widget,
};
use crate::app::App;
use crate::app::Transition;
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 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?")
.small_heading()
.draw(ctx),
Btn::close(ctx),
]),
)
.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))
}
}
impl State<App> for Viewer {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
ctx.canvas_movement();
if ctx.redo_mouseover() {
app.recalculate_current_selection(ctx);
}
match self.panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"close" => {
return Transition::Pop;
}
_ => unreachable!(),
},
_ => {}
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
self.panel.draw(g);
CommonState::draw_osd(g, app);
g.redraw(&self.arrows);
if let Some(id) = app
.primary
.current_selection
.as_ref()
.and_then(|id| id.agent_id())
{
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);
}
}
}
}