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 {
|
pub fn left_click<S: Into<String>>(&mut self, ctx: &mut EventCtx, label: S) -> bool {
|
||||||
assert!(self.click_action.is_none());
|
let label = label.into();
|
||||||
self.click_action = Some(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()
|
ctx.normal_left_click()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ pub struct InfoPanel {
|
|||||||
panel: Panel,
|
panel: Panel,
|
||||||
|
|
||||||
draw_extra: ToggleZoomed,
|
draw_extra: ToggleZoomed,
|
||||||
tooltips: Vec<(Polygon, Text)>,
|
tooltips: Vec<(Polygon, Text, (TripID, Time))>,
|
||||||
|
|
||||||
hyperlinks: HashMap<String, Tab>,
|
hyperlinks: HashMap<String, Tab>,
|
||||||
warpers: HashMap<String, ID>,
|
warpers: HashMap<String, ID>,
|
||||||
@ -290,8 +290,9 @@ impl Tab {
|
|||||||
pub struct Details {
|
pub struct Details {
|
||||||
/// Draw extra things when unzoomed or zoomed.
|
/// Draw extra things when unzoomed or zoomed.
|
||||||
pub draw_extra: ToggleZoomedBuilder,
|
pub draw_extra: ToggleZoomedBuilder,
|
||||||
/// Show these tooltips over the map.
|
/// Show these tooltips over the map. If the tooltip is clicked, time-warp and open the info
|
||||||
pub tooltips: Vec<(Polygon, Text)>,
|
/// panel.
|
||||||
|
pub tooltips: Vec<(Polygon, Text, (TripID, Time))>,
|
||||||
/// When a button with this label is clicked, open this info panel tab instead.
|
/// When a button with this label is clicked, open this info panel tab instead.
|
||||||
pub hyperlinks: HashMap<String, Tab>,
|
pub hyperlinks: HashMap<String, Tab>,
|
||||||
/// When a button with this label is clicked, warp to this ID.
|
/// When a button with this label is clicked, warp to this ID.
|
||||||
@ -476,13 +477,30 @@ impl InfoPanel {
|
|||||||
app: &mut App,
|
app: &mut App,
|
||||||
ctx_actions: &mut dyn ContextualActions,
|
ctx_actions: &mut dyn ContextualActions,
|
||||||
) -> (bool, Option<Transition>) {
|
) -> (bool, Option<Transition>) {
|
||||||
// Can click on the map to cancel
|
// Let the user click on the map to cancel out this info panel, or click on a tooltip to
|
||||||
if ctx.canvas.get_cursor_in_map_space().is_some()
|
// time warp.
|
||||||
&& app.primary.current_selection.is_none()
|
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
|
||||||
&& app.per_obj.left_click(ctx, "stop showing info")
|
// 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);
|
return (true, None);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Live update?
|
// Live update?
|
||||||
if app.primary.sim.time() != self.time || ctx_actions.is_paused() != self.is_paused {
|
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) {
|
} else if let Some((trip, time)) = self.time_warpers.get(&action) {
|
||||||
let trip = *trip;
|
do_time_warp(ctx_actions, app, *trip, *time)
|
||||||
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))
|
|
||||||
} else if let Some(url) = action.strip_prefix("open ") {
|
} else if let Some(url) = action.strip_prefix("open ") {
|
||||||
open_browser(url);
|
open_browser(url);
|
||||||
(false, None)
|
(false, None)
|
||||||
@ -638,7 +625,7 @@ impl InfoPanel {
|
|||||||
self.panel.draw(g);
|
self.panel.draw(g);
|
||||||
self.draw_extra.draw(g);
|
self.draw_extra.draw(g);
|
||||||
if let Some(pt) = g.canvas.get_cursor_in_map_space() {
|
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) {
|
if poly.contains_pt(pt) {
|
||||||
g.draw_mouse_tooltip(txt.clone());
|
g.draw_mouse_tooltip(txt.clone());
|
||||||
break;
|
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> {
|
fn make_table<I: Into<String>>(ctx: &EventCtx, rows: Vec<(I, String)>) -> Vec<Widget> {
|
||||||
rows.into_iter()
|
rows.into_iter()
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
|
@ -491,7 +491,7 @@ fn draw_problems(
|
|||||||
map: &Map,
|
map: &Map,
|
||||||
) {
|
) {
|
||||||
let empty = Vec::new();
|
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 {
|
match problem {
|
||||||
Problem::IntersectionDelay(i, delay) => {
|
Problem::IntersectionDelay(i, delay) => {
|
||||||
let i = map.get_i(*i);
|
let i = map.get_i(*i);
|
||||||
@ -524,6 +524,8 @@ fn draw_problems(
|
|||||||
details.tooltips.push((
|
details.tooltips.push((
|
||||||
i.polygon.clone(),
|
i.polygon.clone(),
|
||||||
Text::from(Line(format!("{} delay here", delay))),
|
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) => {
|
Problem::ComplexIntersectionCrossing(i) => {
|
||||||
@ -546,6 +548,7 @@ fn draw_problems(
|
|||||||
Line("This has an increased risk of crash or injury for cyclists"),
|
Line("This has an increased risk of crash or injury for cyclists"),
|
||||||
Line("Source: 2020 Seattle DOT Safety Analysis"),
|
Line("Source: 2020 Seattle DOT Safety Analysis"),
|
||||||
]),
|
]),
|
||||||
|
(id, *time),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Problem::OvertakeDesired(on) => {
|
Problem::OvertakeDesired(on) => {
|
||||||
@ -567,6 +570,7 @@ fn draw_problems(
|
|||||||
Traversable::Turn(t) => map.get_i(t.parent).polygon.clone(),
|
Traversable::Turn(t) => map.get_i(t.parent).polygon.clone(),
|
||||||
},
|
},
|
||||||
Text::from("A vehicle wanted to over-take this cyclist near here."),
|
Text::from("A vehicle wanted to over-take this cyclist near here."),
|
||||||
|
(id, *time),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Problem::ArterialIntersectionCrossing(t) => {
|
Problem::ArterialIntersectionCrossing(t) => {
|
||||||
@ -590,6 +594,7 @@ fn draw_problems(
|
|||||||
Line("Arterial intersections have an increased risk of crash or injury for pedestrians"),
|
Line("Arterial intersections have an increased risk of crash or injury for pedestrians"),
|
||||||
Line("Source: 2020 Seattle DOT Safety Analysis"),
|
Line("Source: 2020 Seattle DOT Safety Analysis"),
|
||||||
]),
|
]),
|
||||||
|
(id, *time)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Problem::PedestrianOvercrowding(on) => {
|
Problem::PedestrianOvercrowding(on) => {
|
||||||
@ -611,6 +616,7 @@ fn draw_problems(
|
|||||||
Traversable::Turn(t) => map.get_i(t.parent).polygon.clone(),
|
Traversable::Turn(t) => map.get_i(t.parent).polygon.clone(),
|
||||||
},
|
},
|
||||||
Text::from("Too many pedestrians are crowded together here."),
|
Text::from("Too many pedestrians are crowded together here."),
|
||||||
|
(id, *time),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user