Click trip problems to time-warp. Useful for debugging spurious diffs in greater detail

This commit is contained in:
Dustin Carlino 2022-05-03 16:08:22 +01:00
parent 0fd39db440
commit e66112b3ea
3 changed files with 79 additions and 45 deletions

View File

@ -794,8 +794,11 @@ impl PerObjectActions {
}
pub fn left_click<S: Into<String>>(&mut self, ctx: &mut EventCtx, label: S) -> bool {
assert!(self.click_action.is_none());
self.click_action = Some(label.into());
let label = label.into();
if let Some(ref old) = self.click_action {
panic!("left_click for \"{old}\" already called; can't also do \"{label}\"");
}
self.click_action = Some(label);
ctx.normal_left_click()
}
}

View File

@ -41,7 +41,7 @@ pub struct InfoPanel {
panel: Panel,
draw_extra: ToggleZoomed,
tooltips: Vec<(Polygon, Text)>,
tooltips: Vec<(Polygon, Text, (TripID, Time))>,
hyperlinks: HashMap<String, Tab>,
warpers: HashMap<String, ID>,
@ -290,8 +290,9 @@ impl Tab {
pub struct Details {
/// Draw extra things when unzoomed or zoomed.
pub draw_extra: ToggleZoomedBuilder,
/// Show these tooltips over the map.
pub tooltips: Vec<(Polygon, Text)>,
/// Show these tooltips over the map. If the tooltip is clicked, time-warp and open the info
/// panel.
pub tooltips: Vec<(Polygon, Text, (TripID, Time))>,
/// When a button with this label is clicked, open this info panel tab instead.
pub hyperlinks: HashMap<String, Tab>,
/// When a button with this label is clicked, warp to this ID.
@ -476,13 +477,30 @@ impl InfoPanel {
app: &mut App,
ctx_actions: &mut dyn ContextualActions,
) -> (bool, Option<Transition>) {
// Can click on the map to cancel
if ctx.canvas.get_cursor_in_map_space().is_some()
&& app.primary.current_selection.is_none()
&& app.per_obj.left_click(ctx, "stop showing info")
// Let the user click on the map to cancel out this info panel, or click on a tooltip to
// time warp.
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
// TODO This'll fire left_click elsewhere and conflict; we can't override here
if app.primary.current_selection.is_none() {
let mut found_tooltip = false;
if let Some((_, _, (trip, time))) = self
.tooltips
.iter()
.find(|(poly, _, _)| poly.contains_pt(pt))
{
found_tooltip = true;
if app
.per_obj
.left_click(ctx, &format!("warp here at {}", time))
{
return do_time_warp(ctx_actions, app, *trip, *time);
}
}
if !found_tooltip && app.per_obj.left_click(ctx, "stop showing info") {
return (true, None);
}
}
}
// Live update?
if app.primary.sim.time() != self.time || ctx_actions.is_paused() != self.is_paused {
@ -538,38 +556,7 @@ impl InfoPanel {
))),
)
} else if let Some((trip, time)) = self.time_warpers.get(&action) {
let trip = *trip;
let time = *time;
let person = app.primary.sim.trip_to_person(trip).unwrap();
// When executed, this assumes the SandboxMode is the top of the stack. It'll
// reopen the info panel, then launch the jump-to-time UI.
let jump_to_time =
Transition::ConsumeState(Box::new(move |state, ctx, app| {
let mut sandbox = state.downcast::<SandboxMode>().ok().unwrap();
let mut actions = sandbox.contextual_actions();
sandbox.controls.common.as_mut().unwrap().launch_info_panel(
ctx,
app,
Tab::PersonTrips(person, OpenTrip::single(trip)),
&mut actions,
);
vec![sandbox, TimeWarpScreen::new_state(ctx, app, time, None)]
}));
if time >= app.primary.sim.time() {
return (false, Some(jump_to_time));
}
// We need to first rewind the simulation
let rewind_sim = Transition::Replace(SandboxMode::async_new(
app,
ctx_actions.gameplay_mode(),
Box::new(move |_, _| vec![jump_to_time]),
));
(false, Some(rewind_sim))
do_time_warp(ctx_actions, app, *trip, *time)
} else if let Some(url) = action.strip_prefix("open ") {
open_browser(url);
(false, None)
@ -638,7 +625,7 @@ impl InfoPanel {
self.panel.draw(g);
self.draw_extra.draw(g);
if let Some(pt) = g.canvas.get_cursor_in_map_space() {
for (poly, txt) in &self.tooltips {
for (poly, txt, _) in &self.tooltips {
if poly.contains_pt(pt) {
g.draw_mouse_tooltip(txt.clone());
break;
@ -656,6 +643,44 @@ impl InfoPanel {
}
}
// Internal helper method for InfoPanel::event
fn do_time_warp(
ctx_actions: &mut dyn ContextualActions,
app: &mut App,
trip: TripID,
time: Time,
) -> (bool, Option<Transition>) {
let person = app.primary.sim.trip_to_person(trip).unwrap();
// When executed, this assumes the SandboxMode is the top of the stack. It'll
// reopen the info panel, then launch the jump-to-time UI.
let jump_to_time = Transition::ConsumeState(Box::new(move |state, ctx, app| {
let mut sandbox = state.downcast::<SandboxMode>().ok().unwrap();
let mut actions = sandbox.contextual_actions();
sandbox.controls.common.as_mut().unwrap().launch_info_panel(
ctx,
app,
Tab::PersonTrips(person, OpenTrip::single(trip)),
&mut actions,
);
vec![sandbox, TimeWarpScreen::new_state(ctx, app, time, None)]
}));
if time >= app.primary.sim.time() {
return (false, Some(jump_to_time));
}
// We need to first rewind the simulation
let rewind_sim = Transition::Replace(SandboxMode::async_new(
app,
ctx_actions.gameplay_mode(),
Box::new(move |_, _| vec![jump_to_time]),
));
(false, Some(rewind_sim))
}
fn make_table<I: Into<String>>(ctx: &EventCtx, rows: Vec<(I, String)>) -> Vec<Widget> {
rows.into_iter()
.map(|(k, v)| {

View File

@ -491,7 +491,7 @@ fn draw_problems(
map: &Map,
) {
let empty = Vec::new();
for (_, problem) in analytics.problems_per_trip.get(&id).unwrap_or(&empty) {
for (time, problem) in analytics.problems_per_trip.get(&id).unwrap_or(&empty) {
match problem {
Problem::IntersectionDelay(i, delay) => {
let i = map.get_i(*i);
@ -524,6 +524,8 @@ fn draw_problems(
details.tooltips.push((
i.polygon.clone(),
Text::from(Line(format!("{} delay here", delay))),
// Rewind to just before the agent starts waiting
(id, *time - *delay - Duration::seconds(5.0)),
));
}
Problem::ComplexIntersectionCrossing(i) => {
@ -546,6 +548,7 @@ fn draw_problems(
Line("This has an increased risk of crash or injury for cyclists"),
Line("Source: 2020 Seattle DOT Safety Analysis"),
]),
(id, *time),
));
}
Problem::OvertakeDesired(on) => {
@ -567,6 +570,7 @@ fn draw_problems(
Traversable::Turn(t) => map.get_i(t.parent).polygon.clone(),
},
Text::from("A vehicle wanted to over-take this cyclist near here."),
(id, *time),
));
}
Problem::ArterialIntersectionCrossing(t) => {
@ -590,6 +594,7 @@ fn draw_problems(
Line("Arterial intersections have an increased risk of crash or injury for pedestrians"),
Line("Source: 2020 Seattle DOT Safety Analysis"),
]),
(id, *time)
));
}
Problem::PedestrianOvercrowding(on) => {
@ -611,6 +616,7 @@ fn draw_problems(
Traversable::Turn(t) => map.get_i(t.parent).polygon.clone(),
},
Text::from("Too many pedestrians are crowded together here."),
(id, *time),
));
}
}