mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-23 17:07:12 +03:00
Click trip problems to time-warp. Useful for debugging spurious diffs in greater detail
This commit is contained in:
parent
0fd39db440
commit
e66112b3ea
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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,12 +477,29 @@ 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")
|
||||
{
|
||||
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::<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)| {
|
||||
|
@ -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),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user