mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 01:15:12 +03:00
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:
parent
6aa71539a5
commit
1c05a99969
@ -2,12 +2,11 @@ use abstutil::prettyprint_usize;
|
|||||||
use geom::Duration;
|
use geom::Duration;
|
||||||
use map_gui::tools::{InputWaypoints, WaypointID};
|
use map_gui::tools::{InputWaypoints, WaypointID};
|
||||||
use map_model::connectivity::WalkingOptions;
|
use map_model::connectivity::WalkingOptions;
|
||||||
use map_model::NORMAL_LANE_THICKNESS;
|
|
||||||
use synthpop::{TripEndpoint, TripMode};
|
use synthpop::{TripEndpoint, TripMode};
|
||||||
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
|
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
|
||||||
use widgetry::{
|
use widgetry::{
|
||||||
Color, EventCtx, GfxCtx, HorizontalAlignment, Line, Panel, State, Text, Transition,
|
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
|
||||||
VerticalAlignment, Widget,
|
Transition, VerticalAlignment, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::isochrone::{Isochrone, MovementOptions, Options};
|
use crate::isochrone::{Isochrone, MovementOptions, Options};
|
||||||
@ -50,10 +49,10 @@ impl BusExperiment {
|
|||||||
.and_then(|req| map.pathfind(req).ok())
|
.and_then(|req| map.pathfind(req).ok())
|
||||||
{
|
{
|
||||||
let duration = path.estimate_duration(map, None);
|
let duration = path.estimate_duration(map, None);
|
||||||
if let Some(pl) = path.trace(map) {
|
if let Ok(hitbox) = path.trace_v2(map) {
|
||||||
world
|
world
|
||||||
.add(ID::BusRoute(idx))
|
.add(ID::BusRoute(idx))
|
||||||
.hitbox(pl.make_polygons(5.0 * NORMAL_LANE_THICKNESS))
|
.hitbox(hitbox)
|
||||||
.zorder(0)
|
.zorder(0)
|
||||||
.draw_color(self.waypoints.get_waypoint_color(idx))
|
.draw_color(self.waypoints.get_waypoint_color(idx))
|
||||||
.hover_alpha(0.8)
|
.hover_alpha(0.8)
|
||||||
@ -78,12 +77,13 @@ impl BusExperiment {
|
|||||||
stops,
|
stops,
|
||||||
Options {
|
Options {
|
||||||
movement: MovementOptions::Walking(WalkingOptions::default()),
|
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
|
// 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(5), Color::grey(0.3).alpha(0.5)),
|
||||||
(Duration::minutes(10), Color::grey(0.3).alpha(0.3)),
|
(Duration::minutes(10), Color::grey(0.3).alpha(0.3)),
|
||||||
(Duration::minutes(15), Color::grey(0.3).alpha(0.2)),
|
(Duration::minutes(15), Color::grey(0.3).alpha(0.2)),
|
||||||
],
|
],*/
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
world.draw_master_batch_built(isochrone.draw);
|
world.draw_master_batch_built(isochrone.draw);
|
||||||
@ -94,6 +94,10 @@ impl BusExperiment {
|
|||||||
|
|
||||||
self.panel = Panel::new_builder(Widget::col(vec![
|
self.panel = Panel::new_builder(Widget::col(vec![
|
||||||
map_gui::tools::app_header(ctx, app, "Bus planner"),
|
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![
|
Text::from_multiline(vec![
|
||||||
Line("Within a 15 min walk of all stops:"),
|
Line("Within a 15 min walk of all stops:"),
|
||||||
Line(format!(
|
Line(format!(
|
||||||
@ -124,6 +128,12 @@ impl BusExperiment {
|
|||||||
impl State<App> for BusExperiment {
|
impl State<App> for BusExperiment {
|
||||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
|
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
|
||||||
let panel_outcome = self.panel.event(ctx);
|
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 = self.world.event(ctx);
|
||||||
let world_outcome_for_waypoints = world_outcome
|
let world_outcome_for_waypoints = world_outcome
|
||||||
.maybe_map_id(|id| match id {
|
.maybe_map_id(|id| match id {
|
||||||
|
@ -126,8 +126,8 @@ impl State<App> for Viewer {
|
|||||||
|
|
||||||
match self.panel.event(ctx) {
|
match self.panel.event(ctx) {
|
||||||
Outcome::Clicked(x) => match x.as_ref() {
|
Outcome::Clicked(x) => match x.as_ref() {
|
||||||
"Bus sketch" => {
|
"Sketch bus route (experimental)" => {
|
||||||
return Transition::Replace(crate::bus::BusExperiment::new_state(ctx, app));
|
return Transition::Push(crate::bus::BusExperiment::new_state(ctx, app));
|
||||||
}
|
}
|
||||||
"Home" => {
|
"Home" => {
|
||||||
return Transition::Pop;
|
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"),
|
map_gui::tools::app_header(ctx, app, "15-minute neighborhood explorer"),
|
||||||
ctx.style()
|
ctx.style()
|
||||||
.btn_outline
|
.btn_outline
|
||||||
.text("Bus sketch")
|
.text("Sketch bus route (experimental)")
|
||||||
.hotkey(Key::B)
|
.hotkey(Key::B)
|
||||||
.build_def(ctx),
|
.build_def(ctx),
|
||||||
Text::from_all(vec![
|
Text::from_all(vec![
|
||||||
|
@ -39,10 +39,17 @@ impl Ring {
|
|||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn must_new(pts: Vec<Pt2D>) -> Ring {
|
pub fn must_new(pts: Vec<Pt2D>) -> Ring {
|
||||||
Ring::new(pts).unwrap()
|
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
|
/// Draws the ring with some thickness, with half of it straddling the interor of the ring, and
|
||||||
/// half on the outside.
|
/// half on the outside.
|
||||||
pub fn to_outline(&self, thickness: Distance) -> Polygon {
|
pub fn to_outline(&self, thickness: Distance) -> Polygon {
|
||||||
|
@ -5,7 +5,7 @@ use anyhow::Result;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use abstutil::prettyprint_usize;
|
use abstutil::prettyprint_usize;
|
||||||
use geom::{Distance, Duration, PolyLine, Speed, EPSILON_DIST};
|
use geom::{Distance, Duration, PolyLine, Polygon, Ring, Speed, EPSILON_DIST};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildingID, DirectedRoadID, LaneID, Map, PathConstraints, Position, Traversable, TurnID,
|
BuildingID, DirectedRoadID, LaneID, Map, PathConstraints, Position, Traversable, TurnID,
|
||||||
@ -468,6 +468,38 @@ impl Path {
|
|||||||
Some(pts_so_far.unwrap())
|
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> {
|
pub fn get_steps(&self) -> &VecDeque<PathStep> {
|
||||||
&self.steps
|
&self.steps
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user