diff --git a/game/src/info/trip.rs b/game/src/info/trip.rs index dcd843775a..e6b8f70aff 100644 --- a/game/src/info/trip.rs +++ b/game/src/info/trip.rs @@ -893,14 +893,6 @@ fn make_elevation(ctx: &EventCtx, color: Color, walking: bool, path: &Path, map: dist += step.as_traversable().get_polyline(map).length(); } - // TODO Cache this value? - let max_elevation = map - .all_intersections() - .iter() - .max_by_key(|i| i.elevation) - .unwrap() - .elevation; - // TODO Show roughly where we are in the trip; use distance covered by current path for this LinePlot::new_widget( ctx, @@ -921,7 +913,7 @@ fn make_elevation(ctx: &EventCtx, color: Color, walking: bool, path: &Path, map: // If we use the max elevation encountered along the route, then no matter how we // round, there are always edge cases where the scale will jump. So just use the // maximum elevation from the entire map. - max_y: Some(max_elevation.round_up_for_axis()), + max_y: Some(map.max_elevation().round_up_for_axis()), disabled: HashSet::new(), }, ) diff --git a/game/src/ungap/route.rs b/game/src/ungap/route.rs index aecc5dcc58..a08509b556 100644 --- a/game/src/ungap/route.rs +++ b/game/src/ungap/route.rs @@ -1,13 +1,15 @@ +use std::collections::HashSet; + use rand::seq::SliceRandom; use rand::SeedableRng; use rand_xorshift::XorShiftRng; use geom::{Circle, Distance, Duration, FindClosest, Polygon}; -use map_model::NORMAL_LANE_THICKNESS; +use map_model::{PathStep, NORMAL_LANE_THICKNESS}; use sim::{TripEndpoint, TripMode}; use widgetry::{ - Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, - State, Text, TextExt, VerticalAlignment, Widget, + Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, LinePlot, + Outcome, Panel, PlotOptions, Series, State, Text, TextExt, VerticalAlignment, Widget, }; use crate::app::{App, Transition}; @@ -167,6 +169,9 @@ impl RoutePlanner { let mut total_distance = Distance::ZERO; let mut total_time = Duration::ZERO; + let mut elevation_pts: Vec<(Distance, Distance)> = Vec::new(); + let mut current_dist = Distance::ZERO; + for pair in self.waypoints.windows(2) { if let Some((path, draw_path)) = TripEndpoint::path_req(pair[0].at, pair[1].at, TripMode::Bike, map) @@ -179,11 +184,29 @@ impl RoutePlanner { batch.push(Color::CYAN, draw_path); total_distance += path.total_length(); total_time += path.estimate_duration(map, Some(map_model::MAX_BIKE_SPEED)); + + for step in path.get_steps() { + if let PathStep::Turn(t) = step { + elevation_pts.push((current_dist, map.get_i(t.parent).elevation)); + } + current_dist += step.as_traversable().get_polyline(map).length(); + } } } self.draw_route = ctx.upload(batch); + let mut total_up = Distance::ZERO; + let mut total_down = Distance::ZERO; + for pair in elevation_pts.windows(2) { + let dy = pair[1].1 - pair[0].1; + if dy < Distance::ZERO { + total_down -= dy; + } else { + total_up += dy; + } + } + self.results_panel = Panel::new_builder(Widget::col(vec![ Line("Your route").small_heading().into_widget(ctx), Text::from_all(vec![ @@ -196,6 +219,29 @@ impl RoutePlanner { Line(total_time.to_string(&app.opts.units)), ]) .into_widget(ctx), + Text::from_all(vec![ + Line("Elevation change: ").secondary(), + Line(format!( + "{}↑, {}↓", + total_up.to_string(&app.opts.units), + total_down.to_string(&app.opts.units) + )), + ]) + .into_widget(ctx), + LinePlot::new_widget( + ctx, + 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(), + }, + ), ])) .aligned(HorizontalAlignment::Right, VerticalAlignment::Top) .build(ctx); diff --git a/map_model/src/map.rs b/map_model/src/map.rs index ec9e3e013b..996caef6b8 100644 --- a/map_model/src/map.rs +++ b/map_model/src/map.rs @@ -789,4 +789,14 @@ impl Map { } None } + + /// Returns the highest elevation in the map + pub fn max_elevation(&self) -> Distance { + // TODO Cache? + self.all_intersections() + .iter() + .max_by_key(|i| i.elevation) + .unwrap() + .elevation + } }