From e66112b3eab81b7e6ed8b2b3e3345c12b0b9a282 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Tue, 3 May 2022 16:08:22 +0100 Subject: [PATCH] Click trip problems to time-warp. Useful for debugging spurious diffs in greater detail --- apps/game/src/app.rs | 7 ++- apps/game/src/info/mod.rs | 109 +++++++++++++++++++++++-------------- apps/game/src/info/trip.rs | 8 ++- 3 files changed, 79 insertions(+), 45 deletions(-) diff --git a/apps/game/src/app.rs b/apps/game/src/app.rs index 7e3dafa463..321e891ee1 100644 --- a/apps/game/src/app.rs +++ b/apps/game/src/app.rs @@ -794,8 +794,11 @@ impl PerObjectActions { } pub fn left_click>(&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() } } diff --git a/apps/game/src/info/mod.rs b/apps/game/src/info/mod.rs index 46898e3d17..aeab95f12c 100644 --- a/apps/game/src/info/mod.rs +++ b/apps/game/src/info/mod.rs @@ -41,7 +41,7 @@ pub struct InfoPanel { panel: Panel, draw_extra: ToggleZoomed, - tooltips: Vec<(Polygon, Text)>, + tooltips: Vec<(Polygon, Text, (TripID, Time))>, hyperlinks: HashMap, warpers: HashMap, @@ -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, /// When a button with this label is clicked, warp to this ID. @@ -476,12 +477,29 @@ impl InfoPanel { app: &mut App, ctx_actions: &mut dyn ContextualActions, ) -> (bool, Option) { - // 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") - { - return (true, None); + // 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? @@ -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::().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) { + 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::().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>(ctx: &EventCtx, rows: Vec<(I, String)>) -> Vec { rows.into_iter() .map(|(k, v)| { diff --git a/apps/game/src/info/trip.rs b/apps/game/src/info/trip.rs index 5b3f4702b8..ea679a9e9a 100644 --- a/apps/game/src/info/trip.rs +++ b/apps/game/src/info/trip.rs @@ -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), )); } }