Try tracing paths more precisely along road edges, even with changing widths. Use in the experimental bus tool. It needs work, but solid start.

This commit is contained in:
Dustin Carlino 2022-04-04 19:12:00 +01:00
parent 6aa71539a5
commit 1c05a99969
4 changed files with 60 additions and 11 deletions

View File

@ -2,12 +2,11 @@ use abstutil::prettyprint_usize;
use geom::Duration;
use map_gui::tools::{InputWaypoints, WaypointID};
use map_model::connectivity::WalkingOptions;
use map_model::NORMAL_LANE_THICKNESS;
use synthpop::{TripEndpoint, TripMode};
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
use widgetry::{
Color, EventCtx, GfxCtx, HorizontalAlignment, Line, Panel, State, Text, Transition,
VerticalAlignment, Widget,
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
Transition, VerticalAlignment, Widget,
};
use crate::isochrone::{Isochrone, MovementOptions, Options};
@ -50,10 +49,10 @@ impl BusExperiment {
.and_then(|req| map.pathfind(req).ok())
{
let duration = path.estimate_duration(map, None);
if let Some(pl) = path.trace(map) {
if let Ok(hitbox) = path.trace_v2(map) {
world
.add(ID::BusRoute(idx))
.hitbox(pl.make_polygons(5.0 * NORMAL_LANE_THICKNESS))
.hitbox(hitbox)
.zorder(0)
.draw_color(self.waypoints.get_waypoint_color(idx))
.hover_alpha(0.8)
@ -78,12 +77,13 @@ impl BusExperiment {
stops,
Options {
movement: MovementOptions::Walking(WalkingOptions::default()),
thresholds: vec![(Duration::minutes(15), Color::grey(0.3).alpha(0.5))],
// TODO The inner colors overlap the outer; this doesn't look right yet
thresholds: vec![
/*thresholds: vec![
(Duration::minutes(5), Color::grey(0.3).alpha(0.5)),
(Duration::minutes(10), Color::grey(0.3).alpha(0.3)),
(Duration::minutes(15), Color::grey(0.3).alpha(0.2)),
],
],*/
},
);
world.draw_master_batch_built(isochrone.draw);
@ -94,6 +94,10 @@ impl BusExperiment {
self.panel = Panel::new_builder(Widget::col(vec![
map_gui::tools::app_header(ctx, app, "Bus planner"),
ctx.style()
.btn_back("15-minute neighborhoods")
.hotkey(Key::Escape)
.build_def(ctx),
Text::from_multiline(vec![
Line("Within a 15 min walk of all stops:"),
Line(format!(
@ -124,6 +128,12 @@ impl BusExperiment {
impl State<App> for BusExperiment {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
let panel_outcome = self.panel.event(ctx);
if let Outcome::Clicked(ref x) = panel_outcome {
if x == "15-minute neighborhoods" {
return Transition::Pop;
}
}
let world_outcome = self.world.event(ctx);
let world_outcome_for_waypoints = world_outcome
.maybe_map_id(|id| match id {

View File

@ -126,8 +126,8 @@ impl State<App> for Viewer {
match self.panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"Bus sketch" => {
return Transition::Replace(crate::bus::BusExperiment::new_state(ctx, app));
"Sketch bus route (experimental)" => {
return Transition::Push(crate::bus::BusExperiment::new_state(ctx, app));
}
"Home" => {
return Transition::Pop;
@ -291,7 +291,7 @@ fn build_panel(ctx: &mut EventCtx, app: &App, start: &Building, isochrone: &Isoc
map_gui::tools::app_header(ctx, app, "15-minute neighborhood explorer"),
ctx.style()
.btn_outline
.text("Bus sketch")
.text("Sketch bus route (experimental)")
.hotkey(Key::B)
.build_def(ctx),
Text::from_all(vec![

View File

@ -39,10 +39,17 @@ impl Ring {
Ok(result)
}
pub fn must_new(pts: Vec<Pt2D>) -> Ring {
Ring::new(pts).unwrap()
}
/// First dedupes adjacent points
pub fn deduping_new(mut pts: Vec<Pt2D>) -> Result<Self> {
pts.dedup();
Self::new(pts)
}
/// Draws the ring with some thickness, with half of it straddling the interor of the ring, and
/// half on the outside.
pub fn to_outline(&self, thickness: Distance) -> Polygon {

View File

@ -5,7 +5,7 @@ use anyhow::Result;
use serde::{Deserialize, Serialize};
use abstutil::prettyprint_usize;
use geom::{Distance, Duration, PolyLine, Speed, EPSILON_DIST};
use geom::{Distance, Duration, PolyLine, Polygon, Ring, Speed, EPSILON_DIST};
use crate::{
BuildingID, DirectedRoadID, LaneID, Map, PathConstraints, Position, Traversable, TurnID,
@ -468,6 +468,38 @@ impl Path {
Some(pts_so_far.unwrap())
}
/// Draws the thickened path, matching entire roads. Ignores the path's exact starting and
/// ending distance.
pub fn trace_v2(&self, map: &Map) -> Result<Polygon> {
let mut left_pts = Vec::new();
let mut right_pts = Vec::new();
for step in &self.steps {
match step {
PathStep::Lane(l) => {
let road = map.get_parent(*l);
let width = road.get_half_width();
if map.get_l(*l).dst_i == road.dst_i {
left_pts.extend(road.center_pts.shift_left(width)?.into_points());
right_pts.extend(road.center_pts.shift_right(width)?.into_points());
} else {
left_pts
.extend(road.center_pts.shift_right(width)?.reversed().into_points());
right_pts
.extend(road.center_pts.shift_left(width)?.reversed().into_points());
}
}
PathStep::ContraflowLane(_) => todo!(),
// Just make a straight line across the intersection. It'd be fancier to try and
// trace along.
PathStep::Turn(_) | PathStep::ContraflowTurn(_) => {}
}
}
right_pts.reverse();
left_pts.extend(right_pts);
left_pts.push(left_pts[0]);
Ok(Ring::deduping_new(left_pts)?.into_polygon())
}
pub fn get_steps(&self) -> &VecDeque<PathStep> {
&self.steps
}