Prepare for switching to alt routes. #743

- workaround dragging / clicking conflicts
- make AltRouteResults wrap RouteResults
- plumb names/params around
This commit is contained in:
Dustin Carlino 2021-09-29 07:57:07 -07:00
parent 62be41f2c1
commit 3cf75063c3
2 changed files with 97 additions and 86 deletions

View File

@ -36,7 +36,7 @@ impl RoutePlanner {
input_panel: Panel::empty(ctx),
waypoints: InputWaypoints::new(ctx, app),
main_route: RouteResults::main_route(ctx, app, Vec::new()).0,
main_route: RouteResults::main_route(ctx, app, Vec::new()),
files: files::RouteManagement::new(app),
alt_routes: Vec::new(),
@ -46,17 +46,14 @@ impl RoutePlanner {
}
fn update_input_panel(&mut self, ctx: &mut EventCtx, app: &App) {
let (main_route, results_widget) =
RouteResults::main_route(ctx, app, self.waypoints.get_waypoints());
self.main_route = main_route;
self.main_route = RouteResults::main_route(ctx, app, self.waypoints.get_waypoints());
self.alt_routes.clear();
let low_stress =
AltRouteResults::low_stress(ctx, app, self.waypoints.get_waypoints(), &self.main_route);
if low_stress.stats != self.main_route.stats {
if low_stress.results.stats != self.main_route.stats {
self.alt_routes.push(low_stress);
}
let params = &app.session.routing_params;
let col = Widget::col(vec![
self.files.get_panel_widget(ctx),
Widget::col(vec![
@ -65,7 +62,7 @@ impl RoutePlanner {
Slider::area(
ctx,
100.0,
params.avoid_steep_incline_penalty / MAX_AVOID_PARAM,
self.main_route.params.avoid_steep_incline_penalty / MAX_AVOID_PARAM,
"avoid_steep_incline_penalty",
),
]),
@ -74,14 +71,14 @@ impl RoutePlanner {
Slider::area(
ctx,
100.0,
params.avoid_high_stress / MAX_AVOID_PARAM,
self.main_route.params.avoid_high_stress / MAX_AVOID_PARAM,
"avoid_high_stress",
),
]),
])
.section(ctx),
self.waypoints.get_panel_widget(ctx).section(ctx),
results_widget.section(ctx),
self.main_route.to_widget(ctx, app).section(ctx),
]);
let mut new_panel = Tab::Route.make_left_panel(ctx, app, col);
@ -110,6 +107,16 @@ impl State<App> for RoutePlanner {
});
}
let mut focused_on_alt_route = false;
for r in &mut self.alt_routes {
r.event(ctx);
focused_on_alt_route |= r.has_focus();
if r.has_focus() && ctx.normal_left_click() {
println!("SWITCH");
return Transition::Keep;
}
}
let outcome = self.input_panel.event(ctx);
if let Outcome::Clicked(ref x) = outcome {
if let Some(t) = Tab::Route.handle_action::<RoutePlanner>(ctx, app, x) {
@ -144,7 +151,10 @@ impl State<App> for RoutePlanner {
{
return t;
}
if self.waypoints.event(ctx, app, outcome) {
// Dragging behavior inside here only works if we're not hovering on an alternate route
// TODO But then that prevents dragging some waypoints! Can we give waypoints precedence
// instead?
if !focused_on_alt_route && self.waypoints.event(ctx, app, outcome) {
// Sync from waypoints to file management
// TODO Maaaybe this directly live in the InputWaypoints system?
self.files.current.waypoints = self.waypoints.get_waypoints();
@ -155,10 +165,6 @@ impl State<App> for RoutePlanner {
return t;
}
for r in &mut self.alt_routes {
r.event(ctx);
}
Transition::Keep
}

View File

@ -12,6 +12,9 @@ use widgetry::{
use crate::app::{App, Transition};
pub struct RouteResults {
name: String,
pub params: RoutingParams,
// It's tempting to glue together all of the paths. But since some waypoints might force the
// path to double back on itself, rendering the path as a single PolyLine would break.
paths: Vec<(Path, Option<PolyLine>)>,
@ -26,6 +29,9 @@ pub struct RouteResults {
draw_high_stress: Drawable,
draw_traffic_signals: Drawable,
draw_unprotected_turns: Drawable,
// Possibly a bit large to stash
elevation_pts: Vec<(Distance, Distance)>,
}
#[derive(PartialEq)]
@ -40,27 +46,25 @@ pub struct RouteStats {
}
impl RouteResults {
pub fn main_route(
ctx: &mut EventCtx,
app: &App,
waypoints: Vec<TripEndpoint>,
) -> (RouteResults, Widget) {
pub fn main_route(ctx: &mut EventCtx, app: &App, waypoints: Vec<TripEndpoint>) -> RouteResults {
RouteResults::new(
ctx,
app,
"main",
waypoints,
Color::CYAN,
&app.session.routing_params,
app.session.routing_params.clone(),
)
}
fn new(
ctx: &mut EventCtx,
app: &App,
name: &str,
waypoints: Vec<TripEndpoint>,
route_color: Color,
params: &RoutingParams,
) -> (RouteResults, Widget) {
params: RoutingParams,
) -> RouteResults {
let mut draw_route = ToggleZoomed::builder();
let mut draw_high_stress = GeomBatch::new();
let mut draw_traffic_signals = GeomBatch::new();
@ -82,7 +86,7 @@ impl RouteResults {
for pair in waypoints.windows(2) {
if let Some(path) = TripEndpoint::path_req(pair[0], pair[1], TripMode::Bike, map)
.and_then(|req| map.pathfind_with_params(req, params).ok())
.and_then(|req| map.pathfind_with_params(req, &params).ok())
{
total_distance += path.total_length();
total_time += path.estimate_duration(map, Some(map_model::MAX_BIKE_SPEED));
@ -146,47 +150,29 @@ impl RouteResults {
}
}
let stats = RouteStats {
total_distance,
dist_along_high_stress_roads,
total_time,
num_traffic_signals,
num_unprotected_turns,
total_up,
total_down,
};
let elevation_plot = LinePlot::new_widget(
ctx,
"elevation",
vec![Series {
label: "Elevation".to_string(),
color: Color::RED,
pts: elevation_pts,
}],
PlotOptions {
filterable: false,
max_x: Some(current_dist.round_up_for_axis()),
max_y: Some(map.max_elevation().round_up_for_axis()),
disabled: HashSet::new(),
},
app.opts.units,
);
let widget = stats.to_widget(ctx, app, elevation_plot);
RouteResults {
name: name.to_string(),
params,
(
RouteResults {
draw_route: draw_route.build(ctx),
draw_high_stress: ctx.upload(draw_high_stress),
draw_traffic_signals: ctx.upload(draw_traffic_signals),
draw_unprotected_turns: ctx.upload(draw_unprotected_turns),
paths,
closest_path_segment,
hover_on_line_plot: None,
hover_on_route_tooltip: None,
stats,
draw_route: draw_route.build(ctx),
draw_high_stress: ctx.upload(draw_high_stress),
draw_traffic_signals: ctx.upload(draw_traffic_signals),
draw_unprotected_turns: ctx.upload(draw_unprotected_turns),
paths,
closest_path_segment,
hover_on_line_plot: None,
hover_on_route_tooltip: None,
elevation_pts,
stats: RouteStats {
total_distance,
dist_along_high_stress_roads,
total_time,
num_traffic_signals,
num_unprotected_turns,
total_up,
total_down,
},
widget,
)
}
}
pub fn event(
@ -314,28 +300,45 @@ impl RouteResults {
g.redraw(&self.draw_unprotected_turns);
}
}
}
impl RouteStats {
fn to_widget(&self, ctx: &mut EventCtx, app: &App, elevation_plot: Widget) -> Widget {
let pct_stressful = if self.total_distance == Distance::ZERO {
pub fn to_widget(&self, ctx: &mut EventCtx, app: &App) -> Widget {
let pct_stressful = if self.stats.total_distance == Distance::ZERO {
0.0
} else {
((self.dist_along_high_stress_roads / self.total_distance) * 100.0).round()
((self.stats.dist_along_high_stress_roads / self.stats.total_distance) * 100.0).round()
};
let elevation_plot = LinePlot::new_widget(
ctx,
"elevation",
vec![Series {
label: "Elevation".to_string(),
color: Color::RED,
pts: self.elevation_pts.clone(),
}],
PlotOptions {
filterable: false,
max_x: Some(self.stats.total_distance.round_up_for_axis()),
max_y: Some(app.primary.map.max_elevation().round_up_for_axis()),
disabled: HashSet::new(),
},
app.opts.units,
);
Widget::col(vec![
Line("Route details").small_heading().into_widget(ctx),
Text::from_all(vec![
Line("Distance: ").secondary(),
Line(self.total_distance.to_string(&app.opts.units)),
Line(self.stats.total_distance.to_string(&app.opts.units)),
])
.into_widget(ctx),
Widget::row(vec![
Text::from_all(vec![
Line(format!(
" {} or {}%",
self.dist_along_high_stress_roads.to_string(&app.opts.units),
self.stats
.dist_along_high_stress_roads
.to_string(&app.opts.units),
pct_stressful
)),
Line(" along ").secondary(),
@ -350,7 +353,7 @@ impl RouteStats {
]),
Text::from_all(vec![
Line("Estimated time: ").secondary(),
Line(self.total_time.to_string(&app.opts.units)),
Line(self.stats.total_time.to_string(&app.opts.units)),
])
.into_widget(ctx),
Widget::row(vec![
@ -361,7 +364,7 @@ impl RouteStats {
ctx.style()
.btn_plain
.btn()
.label_underlined_text(self.num_traffic_signals.to_string())
.label_underlined_text(self.stats.num_traffic_signals.to_string())
.build_widget(ctx, "traffic signals"),
]),
Widget::row(vec![
@ -372,15 +375,15 @@ impl RouteStats {
ctx.style()
.btn_plain
.btn()
.label_underlined_text(self.num_unprotected_turns.to_string())
.label_underlined_text(self.stats.num_unprotected_turns.to_string())
.build_widget(ctx, "unprotected turns"),
]),
Text::from_all(vec![
Line("Elevation change: ").secondary(),
Line(format!(
"{}↑, {}↓",
self.total_up.to_string(&app.opts.units),
self.total_down.to_string(&app.opts.units)
self.stats.total_up.to_string(&app.opts.units),
self.stats.total_down.to_string(&app.opts.units)
)),
])
.into_widget(ctx),
@ -390,10 +393,8 @@ impl RouteStats {
}
pub struct AltRouteResults {
closest_path_segment: FindClosest<usize>,
pub stats: RouteStats,
pub results: RouteResults,
hovering: bool,
draw_route: ToggleZoomed,
tooltip: Text,
}
@ -404,31 +405,35 @@ impl AltRouteResults {
waypoints: Vec<TripEndpoint>,
main: &RouteResults,
) -> AltRouteResults {
let (results, _) = RouteResults::new(
let results = RouteResults::new(
ctx,
app,
"low-stress",
waypoints,
Color::grey(0.3),
&RoutingParams {
RoutingParams {
avoid_high_stress: 2.0,
..Default::default()
},
);
let tooltip = compare_routes(app, &main.stats, &results.stats);
let tooltip = compare_routes(app, &main.stats, &results.stats, &results.name);
AltRouteResults {
closest_path_segment: results.closest_path_segment,
stats: results.stats,
results,
hovering: false,
draw_route: results.draw_route,
tooltip,
}
}
pub fn has_focus(&self) -> bool {
self.hovering
}
pub fn event(&mut self, ctx: &mut EventCtx) {
if ctx.redo_mouseover() {
self.hovering = false;
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
if self
.results
.closest_path_segment
.closest_pt(pt, 10.0 * NORMAL_LANE_THICKNESS)
.is_some()
@ -440,7 +445,7 @@ impl AltRouteResults {
}
pub fn draw(&self, g: &mut GfxCtx, app: &App) {
self.draw_route.draw(g, app);
self.results.draw_route.draw(g, app);
if self.hovering {
g.draw_mouse_tooltip(self.tooltip.clone());
@ -448,9 +453,9 @@ impl AltRouteResults {
}
}
fn compare_routes(app: &App, main: &RouteStats, alt: &RouteStats) -> Text {
fn compare_routes(app: &App, main: &RouteStats, alt: &RouteStats, alt_name: &str) -> Text {
let mut txt = Text::new();
txt.add_line(Line("Click to use low-stress route"));
txt.add_line(Line(format!("Click to use {} route", alt_name)));
cmp_dist(
&mut txt,