mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
Only make crosswalks in one direction.
Don't create duplicate crosswalks in edit_movement Remove other_crosswalk_ids and switch to make_walking_turns_v2 Allow make_shared_sidewalk_corner and make_crosswalks to work in either direction with a point order check Don't skip rendering some corners since they're no longer duplicated Draw sidewalk corners the same regardless of lane direction with a point order check Only make one crosswalk at dead ends and degenerate intersections Make footways only get sharedsidewalkcorner turns Don't panic on bad sharedsidewalkcorner geometry
This commit is contained in:
parent
65f086ecb3
commit
9369ac229f
@ -55,10 +55,7 @@ impl DrawIntersection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for turn in &i.turns {
|
for turn in &i.turns {
|
||||||
// Avoid double-rendering
|
if turn.turn_type.pedestrian_crossing() {
|
||||||
if turn.turn_type.pedestrian_crossing()
|
|
||||||
&& !turn.other_crosswalk_ids.iter().any(|id| *id < turn.id)
|
|
||||||
{
|
|
||||||
make_crosswalk(&mut default_geom, turn, map, app.cs());
|
make_crosswalk(&mut default_geom, turn, map, app.cs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,10 +262,6 @@ pub fn calculate_corners(i: &Intersection, map: &Map) -> Vec<Polygon> {
|
|||||||
|
|
||||||
for turn in &i.turns {
|
for turn in &i.turns {
|
||||||
if turn.turn_type == TurnType::SharedSidewalkCorner {
|
if turn.turn_type == TurnType::SharedSidewalkCorner {
|
||||||
// Avoid double-rendering
|
|
||||||
if map.get_l(turn.id.src).dst_i != i.id {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let l1 = map.get_l(turn.id.src);
|
let l1 = map.get_l(turn.id.src);
|
||||||
let l2 = map.get_l(turn.id.dst);
|
let l2 = map.get_l(turn.id.dst);
|
||||||
|
|
||||||
@ -278,23 +271,36 @@ pub fn calculate_corners(i: &Intersection, map: &Map) -> Vec<Polygon> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is point2 counter-clockwise of point1?
|
||||||
|
let dir = if i
|
||||||
|
.polygon
|
||||||
|
.center()
|
||||||
|
.angle_to(turn.geom.first_pt())
|
||||||
|
.simple_shortest_rotation_towards(i.polygon.center().angle_to(turn.geom.last_pt()))
|
||||||
|
> 0.0
|
||||||
|
{
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
-1.0
|
||||||
|
};
|
||||||
|
|
||||||
if l1.width == l2.width {
|
if l1.width == l2.width {
|
||||||
// When two sidewalks or two shoulders meet, use the turn geometry to create some
|
// When two sidewalks or two shoulders meet, use the turn geometry to create some
|
||||||
// nice rounding.
|
// nice rounding.
|
||||||
let width = l1.width;
|
let shift = dir * l1.width / 2.0;
|
||||||
if let Some(poly) = (|| {
|
if let Some(poly) = (|| {
|
||||||
let mut pts = turn.geom.shift_left(width / 2.0).ok()?.into_points();
|
let mut pts = turn.geom.shift_either_direction(-shift).ok()?.into_points();
|
||||||
pts.push(l2.first_line().shift_left(width / 2.0).pt1());
|
pts.push(l2.end_line(i.id).shift_either_direction(shift).pt2());
|
||||||
pts.push(l2.first_line().shift_right(width / 2.0).pt1());
|
pts.push(l2.end_line(i.id).shift_either_direction(-shift).pt2());
|
||||||
pts.extend(
|
pts.extend(
|
||||||
turn.geom
|
turn.geom
|
||||||
.shift_right(width / 2.0)
|
.shift_either_direction(shift)
|
||||||
.ok()?
|
.ok()?
|
||||||
.reversed()
|
.reversed()
|
||||||
.into_points(),
|
.into_points(),
|
||||||
);
|
);
|
||||||
pts.push(l1.last_line().shift_right(width / 2.0).pt2());
|
pts.push(l1.end_line(i.id).shift_either_direction(shift).pt2());
|
||||||
pts.push(l1.last_line().shift_left(width / 2.0).pt2());
|
pts.push(l1.end_line(i.id).shift_either_direction(-shift).pt2());
|
||||||
pts.push(pts[0]);
|
pts.push(pts[0]);
|
||||||
// Many resulting shapes aren't valid rings, but we can still triangulate them.
|
// Many resulting shapes aren't valid rings, but we can still triangulate them.
|
||||||
Some(Polygon::buggy_new(pts))
|
Some(Polygon::buggy_new(pts))
|
||||||
@ -304,10 +310,18 @@ pub fn calculate_corners(i: &Intersection, map: &Map) -> Vec<Polygon> {
|
|||||||
} else {
|
} else {
|
||||||
// When a sidewalk and a shoulder meet, use a simpler shape to connect them.
|
// When a sidewalk and a shoulder meet, use a simpler shape to connect them.
|
||||||
let mut pts = vec![
|
let mut pts = vec![
|
||||||
l2.first_line().shift_left(l2.width / 2.0).pt1(),
|
l2.end_line(i.id)
|
||||||
l2.first_line().shift_right(l2.width / 2.0).pt1(),
|
.shift_either_direction(dir * l2.width / 2.0)
|
||||||
l1.last_line().shift_right(l1.width / 2.0).pt2(),
|
.pt2(),
|
||||||
l1.last_line().shift_left(l1.width / 2.0).pt2(),
|
l2.end_line(i.id)
|
||||||
|
.shift_either_direction(-dir * l2.width / 2.0)
|
||||||
|
.pt2(),
|
||||||
|
l1.end_line(i.id)
|
||||||
|
.shift_either_direction(-dir * l1.width / 2.0)
|
||||||
|
.pt2(),
|
||||||
|
l1.end_line(i.id)
|
||||||
|
.shift_either_direction(dir * l1.width / 2.0)
|
||||||
|
.pt2(),
|
||||||
];
|
];
|
||||||
pts.push(pts[0]);
|
pts.push(pts[0]);
|
||||||
if let Ok(ring) = Ring::new(pts) {
|
if let Ok(ring) = Ring::new(pts) {
|
||||||
@ -332,20 +346,26 @@ fn calculate_corner_curbs(i: &Intersection, map: &Map) -> Vec<Polygon> {
|
|||||||
|
|
||||||
for turn in &i.turns {
|
for turn in &i.turns {
|
||||||
if turn.turn_type == TurnType::SharedSidewalkCorner {
|
if turn.turn_type == TurnType::SharedSidewalkCorner {
|
||||||
// Avoid double-rendering
|
let dir = if turn
|
||||||
if map.get_l(turn.id.src).dst_i != i.id {
|
.geom
|
||||||
continue;
|
.first_pt()
|
||||||
}
|
.angle_to(i.polygon.center())
|
||||||
|
.simple_shortest_rotation_towards(
|
||||||
|
turn.geom.first_pt().angle_to(turn.geom.last_pt()),
|
||||||
|
)
|
||||||
|
> 0.0
|
||||||
|
{
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
-1.0
|
||||||
|
};
|
||||||
let l1 = map.get_l(turn.id.src);
|
let l1 = map.get_l(turn.id.src);
|
||||||
let l2 = map.get_l(turn.id.dst);
|
let l2 = map.get_l(turn.id.dst);
|
||||||
|
|
||||||
if l1.width == l2.width {
|
if l1.width == l2.width {
|
||||||
// When two sidewalks or two shoulders meet, use the turn geometry to create some
|
// When two sidewalks or two shoulders meet, use the turn geometry to create some
|
||||||
// nice rounding.
|
// nice rounding.
|
||||||
let mut width = shift(l1.width);
|
let width = dir * shift(l1.width);
|
||||||
if map.get_config().driving_side == DrivingSide::Right {
|
|
||||||
width *= -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(pl) = (|| {
|
if let Some(pl) = (|| {
|
||||||
let mut pts = turn.geom.shift_either_direction(width).ok()?.into_points();
|
let mut pts = turn.geom.shift_either_direction(width).ok()?.into_points();
|
||||||
@ -354,15 +374,18 @@ fn calculate_corner_curbs(i: &Intersection, map: &Map) -> Vec<Polygon> {
|
|||||||
// this causes "zig-zaggy" artifacts. The approx_eq check helps some (but not
|
// this causes "zig-zaggy" artifacts. The approx_eq check helps some (but not
|
||||||
// all) of those cases, but sometimes introduces visual "gaps". This still
|
// all) of those cases, but sometimes introduces visual "gaps". This still
|
||||||
// needs more work.
|
// needs more work.
|
||||||
let first_line = l2.first_line().shift_either_direction(width);
|
let first_line = l2.end_line(i.id).shift_either_direction(-width);
|
||||||
if !pts.last().unwrap().approx_eq(first_line.pt1(), thickness) {
|
if !pts.last().unwrap().approx_eq(first_line.pt2(), thickness) {
|
||||||
pts.push(first_line.pt1());
|
pts.push(first_line.pt2());
|
||||||
pts.push(first_line.unbounded_dist_along(thickness));
|
pts.push(first_line.unbounded_dist_along(first_line.length() - thickness));
|
||||||
}
|
}
|
||||||
let last_line = l1.last_line().shift_either_direction(width).reversed();
|
let last_line = l1.end_line(i.id).shift_either_direction(width);
|
||||||
if !pts[0].approx_eq(last_line.pt1(), thickness) {
|
if !pts[0].approx_eq(last_line.pt2(), thickness) {
|
||||||
pts.insert(0, last_line.pt1());
|
pts.insert(0, last_line.pt2());
|
||||||
pts.insert(0, last_line.unbounded_dist_along(thickness));
|
pts.insert(
|
||||||
|
0,
|
||||||
|
last_line.unbounded_dist_along(last_line.length() - thickness),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
PolyLine::deduping_new(pts).ok()
|
PolyLine::deduping_new(pts).ok()
|
||||||
})() {
|
})() {
|
||||||
@ -370,22 +393,17 @@ fn calculate_corner_curbs(i: &Intersection, map: &Map) -> Vec<Polygon> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When a sidewalk and a shoulder meet, use a simpler shape to connect them.
|
// When a sidewalk and a shoulder meet, use a simpler shape to connect them.
|
||||||
let direction = if map.get_config().driving_side == DrivingSide::Right {
|
let l1_line = l1
|
||||||
-1.0
|
.end_line(i.id)
|
||||||
} else {
|
.shift_either_direction(dir * shift(l1.width));
|
||||||
1.0
|
let l2_line = l2
|
||||||
};
|
.end_line(i.id)
|
||||||
let last_line = l1
|
.shift_either_direction(-dir * shift(l2.width));
|
||||||
.last_line()
|
|
||||||
.shift_either_direction(direction * shift(l1.width));
|
|
||||||
let first_line = l2
|
|
||||||
.first_line()
|
|
||||||
.shift_either_direction(direction * shift(l2.width));
|
|
||||||
if let Ok(pl) = PolyLine::deduping_new(vec![
|
if let Ok(pl) = PolyLine::deduping_new(vec![
|
||||||
last_line.reversed().unbounded_dist_along(thickness),
|
l1_line.unbounded_dist_along(l1_line.length() - thickness),
|
||||||
last_line.pt2(),
|
l1_line.pt2(),
|
||||||
first_line.pt1(),
|
l2_line.pt2(),
|
||||||
first_line.unbounded_dist_along(thickness),
|
l2_line.unbounded_dist_along(l2_line.length() - thickness),
|
||||||
]) {
|
]) {
|
||||||
curbs.push(pl.make_polygons(thickness));
|
curbs.push(pl.make_polygons(thickness));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use nbez::{Bez3o, BezCurve, Point2d};
|
use nbez::{Bez3o, BezCurve, Point2d};
|
||||||
@ -219,7 +219,6 @@ fn make_vehicle_turns(i: &Intersection, map: &Map) -> Vec<Turn> {
|
|||||||
dst: dst.id,
|
dst: dst.id,
|
||||||
},
|
},
|
||||||
turn_type,
|
turn_type,
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom,
|
geom,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,172 +1,126 @@
|
|||||||
use std::collections::BTreeSet;
|
use geom::{Distance, PolyLine, Pt2D, Ring};
|
||||||
|
|
||||||
use abstutil::wraparound_get;
|
|
||||||
use geom::{Distance, Line, PolyLine, Pt2D, Ring};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Direction, DrivingSide, Intersection, IntersectionID, Lane, LaneID, LaneType, Map, Road, Turn,
|
Direction, DrivingSide, Intersection, IntersectionID, Lane, LaneID, Map, Turn, TurnID, TurnType,
|
||||||
TurnID, TurnType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generate Crosswalk and SharedSidewalkCorner (places where two sidewalks directly meet) turns.
|
/// Generate Crosswalk and SharedSidewalkCorner (places where two sidewalks directly meet) turns.
|
||||||
/// UnmarkedCrossings are not generated here; another process later "downgrades" crosswalks to
|
/// UnmarkedCrossings are not generated here; another process later "downgrades" crosswalks to
|
||||||
/// unmarked.
|
/// unmarked.
|
||||||
|
/// A complete rewrite of make_walking_turns, which looks at all sidewalks (or lack thereof) in
|
||||||
|
/// counter-clockwise order around an intersection. Based on adjacency, create a
|
||||||
|
/// SharedSidewalkCorner or a Crosswalk.
|
||||||
pub fn make_walking_turns(map: &Map, i: &Intersection) -> Vec<Turn> {
|
pub fn make_walking_turns(map: &Map, i: &Intersection) -> Vec<Turn> {
|
||||||
if i.merged {
|
|
||||||
return make_walking_turns_v2(map, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.is_footway(map) {
|
|
||||||
return make_footway_turns(map, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
let driving_side = map.config.driving_side;
|
let driving_side = map.config.driving_side;
|
||||||
|
|
||||||
let roads: Vec<&Road> = i
|
// Consider all roads in counter-clockwise order. Every road has up to two sidewalks. Gather
|
||||||
.get_roads_sorted_by_incoming_angle(map)
|
// those in order, remembering what roads don't have them.
|
||||||
.into_iter()
|
let mut lanes: Vec<Option<&Lane>> = Vec::new();
|
||||||
.map(|id| map.get_r(id))
|
let mut sorted_roads = i.get_roads_sorted_by_incoming_angle(map);
|
||||||
.collect();
|
// And for left-handed driving, we need to walk around in the opposite order.
|
||||||
|
if driving_side == DrivingSide::Left {
|
||||||
|
sorted_roads.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
for r in sorted_roads {
|
||||||
|
let road = map.get_r(r);
|
||||||
|
let mut fwd = None;
|
||||||
|
let mut back = None;
|
||||||
|
for l in &road.lanes {
|
||||||
|
if l.lane_type.is_walkable() {
|
||||||
|
if l.dir == Direction::Fwd {
|
||||||
|
fwd = Some(l);
|
||||||
|
} else {
|
||||||
|
back = Some(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (in_lane, out_lane) = if road.src_i == i.id {
|
||||||
|
(back, fwd)
|
||||||
|
} else {
|
||||||
|
(fwd, back)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't add None entries for footways even if they only have one lane
|
||||||
|
if map.get_r(r).is_footway() {
|
||||||
|
if in_lane.is_some() {
|
||||||
|
lanes.push(in_lane);
|
||||||
|
}
|
||||||
|
if out_lane.is_some() {
|
||||||
|
lanes.push(out_lane);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lanes.push(in_lane);
|
||||||
|
lanes.push(out_lane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are 0 or 1 sidewalks there are no turns to be made
|
||||||
|
if lanes.iter().filter(|l| l.is_some()).count() <= 1 {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we start with a sidewalk.
|
||||||
|
while lanes[0].is_none() {
|
||||||
|
lanes.rotate_left(1);
|
||||||
|
}
|
||||||
let mut result: Vec<Turn> = Vec::new();
|
let mut result: Vec<Turn> = Vec::new();
|
||||||
|
|
||||||
// I'm a bit confused when to do -1 and +1 honestly, but this works in practice. Angle sorting
|
let mut from: Option<&Lane> = lanes[0];
|
||||||
// may be a little backwards.
|
let first_from = from.unwrap().id;
|
||||||
let idx_offset = if driving_side == DrivingSide::Right {
|
let mut adj = true;
|
||||||
-1
|
for l in lanes.iter().skip(1).chain(lanes.iter()) {
|
||||||
} else {
|
if i.id.0 == 284 {
|
||||||
1
|
debug!(
|
||||||
};
|
"looking at {:?}. from is {:?}, first_from is {}, adj is {}",
|
||||||
|
l.map(|l| l.id),
|
||||||
|
from.map(|l| l.id),
|
||||||
|
first_from,
|
||||||
|
adj
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if i.is_degenerate() {
|
if from.is_none() {
|
||||||
if let Some(turns) = make_degenerate_crosswalks(map, i.id, roads[0], roads[1]) {
|
from = *l;
|
||||||
result.extend(turns);
|
adj = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
// TODO Argh, duplicate logic for SharedSidewalkCorners
|
let l1 = from.unwrap();
|
||||||
for idx1 in 0..roads.len() {
|
|
||||||
if let Some(l1) = get_sidewalk(map, roads[idx1].incoming_lanes(i.id)) {
|
if l.is_none() {
|
||||||
if let Some(l2) = get_sidewalk(
|
adj = false;
|
||||||
map,
|
continue;
|
||||||
wraparound_get(&roads, (idx1 as isize) + idx_offset).outgoing_lanes(i.id),
|
|
||||||
) {
|
|
||||||
if l1.last_pt() != l2.first_pt() {
|
|
||||||
let geom = make_shared_sidewalk_corner(driving_side, i, l1, l2);
|
|
||||||
result.push(Turn {
|
|
||||||
id: turn_id(i.id, l1.id, l2.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom: geom.clone(),
|
|
||||||
});
|
|
||||||
result.push(Turn {
|
|
||||||
id: turn_id(i.id, l2.id, l1.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom: geom.reversed(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
let l2 = l.unwrap();
|
||||||
}
|
|
||||||
if roads.len() == 1 {
|
if adj && l1.id.road != l2.id.road {
|
||||||
if let Some(l1) = get_sidewalk(map, roads[0].incoming_lanes(i.id)) {
|
result.push(Turn {
|
||||||
if let Some(l2) = get_sidewalk(map, roads[0].outgoing_lanes(i.id)) {
|
id: turn_id(i.id, l1.id, l2.id),
|
||||||
let geom = make_shared_sidewalk_corner(driving_side, i, l1, l2);
|
turn_type: TurnType::SharedSidewalkCorner,
|
||||||
|
geom: make_shared_sidewalk_corner(i, l1, l2),
|
||||||
|
});
|
||||||
|
|
||||||
|
from = Some(l2);
|
||||||
|
// adj stays true
|
||||||
|
} else {
|
||||||
|
// Only make one crosswalk for degenerate intersections
|
||||||
|
if !(i.is_degenerate() || i.is_deadend())
|
||||||
|
|| !result.iter().any(|t| t.turn_type == TurnType::Crosswalk)
|
||||||
|
{
|
||||||
result.push(Turn {
|
result.push(Turn {
|
||||||
id: turn_id(i.id, l1.id, l2.id),
|
id: turn_id(i.id, l1.id, l2.id),
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
turn_type: TurnType::Crosswalk,
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
geom: make_crosswalk(i, l1, l2),
|
||||||
geom: geom.clone(),
|
|
||||||
});
|
|
||||||
result.push(Turn {
|
|
||||||
id: turn_id(i.id, l2.id, l1.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom: geom.reversed(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
from = Some(l2);
|
||||||
|
adj = true;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx1 in 0..roads.len() {
|
// Have we made it all the way around?
|
||||||
if let Some(l1) = get_sidewalk(map, roads[idx1].incoming_lanes(i.id)) {
|
if first_from == from.unwrap().id {
|
||||||
// Make the crosswalk to the other side
|
break;
|
||||||
if let Some(l2) = get_sidewalk(map, roads[idx1].outgoing_lanes(i.id)) {
|
|
||||||
result.extend(
|
|
||||||
make_crosswalks(i.id, l1, l2, driving_side)
|
|
||||||
.into_iter()
|
|
||||||
.flatten(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the shared corner
|
|
||||||
if let Some(l2) = get_sidewalk(
|
|
||||||
map,
|
|
||||||
wraparound_get(&roads, (idx1 as isize) + idx_offset).outgoing_lanes(i.id),
|
|
||||||
) {
|
|
||||||
if l1.last_pt() != l2.first_pt() {
|
|
||||||
let geom = make_shared_sidewalk_corner(driving_side, i, l1, l2);
|
|
||||||
result.push(Turn {
|
|
||||||
id: turn_id(i.id, l1.id, l2.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom: geom.clone(),
|
|
||||||
});
|
|
||||||
result.push(Turn {
|
|
||||||
id: turn_id(i.id, l2.id, l1.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom: geom.reversed(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if let Some(l2) = get_sidewalk(
|
|
||||||
map,
|
|
||||||
wraparound_get(&roads, (idx1 as isize) + idx_offset).incoming_lanes(i.id),
|
|
||||||
) {
|
|
||||||
// Adjacent road is missing a sidewalk on the near side, but has one on the far
|
|
||||||
// side
|
|
||||||
result.extend(
|
|
||||||
make_crosswalks(i.id, l1, l2, driving_side)
|
|
||||||
.into_iter()
|
|
||||||
.flatten(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// We may need to add a crosswalk over this intermediate road that has no
|
|
||||||
// sidewalks at all. There might be a few in the way -- think highway onramps.
|
|
||||||
// TODO Refactor and loop until we find something to connect it to?
|
|
||||||
if let Some(l2) = get_sidewalk(
|
|
||||||
map,
|
|
||||||
wraparound_get(&roads, (idx1 as isize) + 2 * idx_offset).outgoing_lanes(i.id),
|
|
||||||
) {
|
|
||||||
result.extend(
|
|
||||||
make_crosswalks(i.id, l1, l2, driving_side)
|
|
||||||
.into_iter()
|
|
||||||
.flatten(),
|
|
||||||
);
|
|
||||||
} else if let Some(l2) = get_sidewalk(
|
|
||||||
map,
|
|
||||||
wraparound_get(&roads, (idx1 as isize) + 2 * idx_offset).incoming_lanes(i.id),
|
|
||||||
) {
|
|
||||||
result.extend(
|
|
||||||
make_crosswalks(i.id, l1, l2, driving_side)
|
|
||||||
.into_iter()
|
|
||||||
.flatten(),
|
|
||||||
);
|
|
||||||
} else if roads.len() > 3 {
|
|
||||||
if let Some(l2) = get_sidewalk(
|
|
||||||
map,
|
|
||||||
wraparound_get(&roads, (idx1 as isize) + 3 * idx_offset)
|
|
||||||
.outgoing_lanes(i.id),
|
|
||||||
) {
|
|
||||||
result.extend(
|
|
||||||
make_crosswalks(i.id, l1, l2, driving_side)
|
|
||||||
.into_iter()
|
|
||||||
.flatten(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,285 +169,62 @@ pub fn filter_turns(mut input: Vec<Turn>, map: &Map, i: &Intersection) -> Vec<Tu
|
|||||||
input
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A complete rewrite of make_walking_turns, which looks at all sidewalks (or lack thereof) in
|
fn make_crosswalk(i: &Intersection, l1: &Lane, l2: &Lane) -> PolyLine {
|
||||||
/// counter-clockwise order around an intersection. Based on adjacency, create a
|
let l1_line = l1.end_line(i.id);
|
||||||
/// SharedSidewalkCorner or a Crosswalk.
|
let l2_line = l2.end_line(i.id);
|
||||||
///
|
|
||||||
/// TODO This is only used for consolidated intersections right now. Cut over to this completely
|
|
||||||
/// after fixing problems like:
|
|
||||||
/// - too many crosswalks at the Boyer roundabout
|
|
||||||
/// - one centered crosswalk for degenerate intersections
|
|
||||||
fn make_walking_turns_v2(map: &Map, i: &Intersection) -> Vec<Turn> {
|
|
||||||
let driving_side = map.config.driving_side;
|
|
||||||
|
|
||||||
// Consider all roads in counter-clockwise order. Every road has up to two sidewalks. Gather
|
// Jut out a bit into the intersection, cross over, then jut back in.
|
||||||
// those in order, remembering what roads don't have them.
|
// Put degenerate intersection crosswalks in the middle (DEGENERATE_HALF_LENGTH).
|
||||||
let mut lanes: Vec<Option<&Lane>> = Vec::new();
|
PolyLine::deduping_new(vec![
|
||||||
let mut num_sidewalks = 0;
|
l1_line.pt2(),
|
||||||
let mut sorted_roads = i.get_roads_sorted_by_incoming_angle(map);
|
l1_line.unbounded_dist_along(
|
||||||
// And for left-handed driving, we need to walk around in the opposite order.
|
l1_line.length()
|
||||||
if driving_side == DrivingSide::Left {
|
+ if i.is_degenerate() {
|
||||||
sorted_roads.reverse();
|
Distance::const_meters(2.5)
|
||||||
}
|
|
||||||
|
|
||||||
for r in sorted_roads {
|
|
||||||
let road = map.get_r(r);
|
|
||||||
let mut fwd = None;
|
|
||||||
let mut back = None;
|
|
||||||
for l in &road.lanes {
|
|
||||||
if l.lane_type.is_walkable() {
|
|
||||||
if l.dir == Direction::Fwd {
|
|
||||||
fwd = Some(l);
|
|
||||||
} else {
|
} else {
|
||||||
back = Some(l);
|
l1.width / 2.0
|
||||||
}
|
},
|
||||||
}
|
),
|
||||||
}
|
l2_line.unbounded_dist_along(
|
||||||
if fwd.is_some() {
|
l2_line.length()
|
||||||
num_sidewalks += 1;
|
+ if i.is_degenerate() {
|
||||||
}
|
Distance::const_meters(2.5)
|
||||||
if back.is_some() {
|
} else {
|
||||||
num_sidewalks += 1;
|
l2.width / 2.0
|
||||||
}
|
},
|
||||||
let (in_lane, out_lane) = if road.src_i == i.id {
|
),
|
||||||
(back, fwd)
|
l2_line.pt2(),
|
||||||
} else {
|
|
||||||
(fwd, back)
|
|
||||||
};
|
|
||||||
lanes.push(in_lane);
|
|
||||||
lanes.push(out_lane);
|
|
||||||
}
|
|
||||||
if num_sidewalks <= 1 {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
// Make sure we start with a sidewalk.
|
|
||||||
while lanes[0].is_none() {
|
|
||||||
lanes.rotate_left(1);
|
|
||||||
}
|
|
||||||
let mut result: Vec<Turn> = Vec::new();
|
|
||||||
|
|
||||||
let mut from: Option<&Lane> = lanes[0];
|
|
||||||
let first_from = from.unwrap().id;
|
|
||||||
let mut adj = true;
|
|
||||||
for l in lanes.iter().skip(1).chain(lanes.iter()) {
|
|
||||||
if i.id.0 == 284 {
|
|
||||||
debug!(
|
|
||||||
"looking at {:?}. from is {:?}, first_from is {}, adj is {}",
|
|
||||||
l.map(|l| l.id),
|
|
||||||
from.map(|l| l.id),
|
|
||||||
first_from,
|
|
||||||
adj
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if from.is_none() {
|
|
||||||
from = *l;
|
|
||||||
adj = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let l1 = from.unwrap();
|
|
||||||
|
|
||||||
if l.is_none() {
|
|
||||||
adj = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let l2 = l.unwrap();
|
|
||||||
|
|
||||||
if adj && l1.id.road != l2.id.road {
|
|
||||||
// Because of the order we go, have to swap l1 and l2 here. l1 is the outgoing, l2 the
|
|
||||||
// incoming.
|
|
||||||
let geom = make_shared_sidewalk_corner(driving_side, i, l2, l1);
|
|
||||||
result.push(Turn {
|
|
||||||
id: turn_id(i.id, l1.id, l2.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom: geom.reversed(),
|
|
||||||
});
|
|
||||||
result.push(Turn {
|
|
||||||
id: turn_id(i.id, l2.id, l1.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom,
|
|
||||||
});
|
|
||||||
|
|
||||||
from = Some(l2);
|
|
||||||
// adj stays true
|
|
||||||
} else {
|
|
||||||
// TODO Just one for degenerate intersections
|
|
||||||
result.extend(
|
|
||||||
make_crosswalks(i.id, l1, l2, driving_side)
|
|
||||||
.into_iter()
|
|
||||||
.flatten(),
|
|
||||||
);
|
|
||||||
from = Some(l2);
|
|
||||||
adj = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Have we made it all the way around?
|
|
||||||
if first_from == from.unwrap().id {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// At an intersection of footpaths only, just generate a turn between every pair of lanes.
|
|
||||||
fn make_footway_turns(map: &Map, i: &Intersection) -> Vec<Turn> {
|
|
||||||
let lanes = i
|
|
||||||
.incoming_lanes
|
|
||||||
.iter()
|
|
||||||
.chain(&i.outgoing_lanes)
|
|
||||||
.filter_map(|l| {
|
|
||||||
let l = map.get_l(*l);
|
|
||||||
if l.is_walkable() {
|
|
||||||
Some(l)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<&Lane>>();
|
|
||||||
let mut results = Vec::new();
|
|
||||||
for l1 in &lanes {
|
|
||||||
for l2 in &lanes {
|
|
||||||
if l1.id == l2.id {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let maybe_geom = PolyLine::new(vec![l1.endpoint(i.id), l2.endpoint(i.id)]);
|
|
||||||
let geom = maybe_geom.unwrap_or_else(|_| {
|
|
||||||
// TODO Gross! After improving intersection geometry where these cases are
|
|
||||||
// happening, if this still happens, maybe it's time to make turn geometry be
|
|
||||||
// optional.
|
|
||||||
PolyLine::must_new(vec![l1.endpoint(i.id), l1.endpoint(i.id).offset(0.1, 0.1)])
|
|
||||||
});
|
|
||||||
results.push(Turn {
|
|
||||||
id: turn_id(i.id, l1.id, l2.id),
|
|
||||||
turn_type: TurnType::SharedSidewalkCorner,
|
|
||||||
other_crosswalk_ids: BTreeSet::new(),
|
|
||||||
geom,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
results
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_crosswalks(
|
|
||||||
i: IntersectionID,
|
|
||||||
l1: &Lane,
|
|
||||||
l2: &Lane,
|
|
||||||
driving_side: DrivingSide,
|
|
||||||
) -> Option<Vec<Turn>> {
|
|
||||||
let l1_pt = l1.endpoint(i);
|
|
||||||
let l2_pt = l2.endpoint(i);
|
|
||||||
// This is one of those uncomfortably "trial-and-error" kind of things.
|
|
||||||
let mut direction = if (l1.dst_i == i) == (l2.dst_i == i) {
|
|
||||||
-1.0
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
};
|
|
||||||
if driving_side == DrivingSide::Left {
|
|
||||||
direction *= -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jut out a bit into the intersection, cross over, then jut back in. Assumes sidewalks are the
|
|
||||||
// same width.
|
|
||||||
let line = Line::new(l1_pt, l2_pt)?.shift_either_direction(direction * l1.width / 2.0);
|
|
||||||
let geom_fwds = PolyLine::deduping_new(vec![l1_pt, line.pt1(), line.pt2(), l2_pt]).ok()?;
|
|
||||||
|
|
||||||
Some(vec![
|
|
||||||
Turn {
|
|
||||||
id: turn_id(i, l1.id, l2.id),
|
|
||||||
turn_type: TurnType::Crosswalk,
|
|
||||||
other_crosswalk_ids: vec![turn_id(i, l2.id, l1.id)].into_iter().collect(),
|
|
||||||
geom: geom_fwds.clone(),
|
|
||||||
},
|
|
||||||
Turn {
|
|
||||||
id: turn_id(i, l2.id, l1.id),
|
|
||||||
turn_type: TurnType::Crosswalk,
|
|
||||||
other_crosswalk_ids: vec![turn_id(i, l1.id, l2.id)].into_iter().collect(),
|
|
||||||
geom: geom_fwds.reversed(),
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
}
|
.unwrap_or_else(|_| PolyLine::unchecked_new(vec![l1.endpoint(i.id), l2.endpoint(i.id)]))
|
||||||
|
|
||||||
// Only one physical crosswalk for degenerate intersections, right in the middle.
|
|
||||||
fn make_degenerate_crosswalks(
|
|
||||||
map: &Map,
|
|
||||||
i: IntersectionID,
|
|
||||||
r1: &Road,
|
|
||||||
r2: &Road,
|
|
||||||
) -> Option<Vec<Turn>> {
|
|
||||||
let l1_in = get_sidewalk(map, r1.incoming_lanes(i))?;
|
|
||||||
let l1_out = get_sidewalk(map, r1.outgoing_lanes(i))?;
|
|
||||||
let l2_in = get_sidewalk(map, r2.incoming_lanes(i))?;
|
|
||||||
let l2_out = get_sidewalk(map, r2.outgoing_lanes(i))?;
|
|
||||||
|
|
||||||
let pt1 = Line::new(l1_in.last_pt(), l2_out.first_pt())?.percent_along(0.5)?;
|
|
||||||
let pt2 = Line::new(l1_out.first_pt(), l2_in.last_pt())?.percent_along(0.5)?;
|
|
||||||
|
|
||||||
if pt1 == pt2 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut all_ids = BTreeSet::new();
|
|
||||||
all_ids.insert(turn_id(i, l1_in.id, l1_out.id));
|
|
||||||
all_ids.insert(turn_id(i, l1_out.id, l1_in.id));
|
|
||||||
all_ids.insert(turn_id(i, l2_in.id, l2_out.id));
|
|
||||||
all_ids.insert(turn_id(i, l2_out.id, l2_in.id));
|
|
||||||
|
|
||||||
Some(
|
|
||||||
vec![
|
|
||||||
Turn {
|
|
||||||
id: turn_id(i, l1_in.id, l1_out.id),
|
|
||||||
turn_type: TurnType::Crosswalk,
|
|
||||||
other_crosswalk_ids: all_ids.clone(),
|
|
||||||
geom: PolyLine::deduping_new(vec![l1_in.last_pt(), pt1, pt2, l1_out.first_pt()])
|
|
||||||
.ok()?,
|
|
||||||
},
|
|
||||||
Turn {
|
|
||||||
id: turn_id(i, l1_out.id, l1_in.id),
|
|
||||||
turn_type: TurnType::Crosswalk,
|
|
||||||
other_crosswalk_ids: all_ids.clone(),
|
|
||||||
geom: PolyLine::deduping_new(vec![l1_out.first_pt(), pt2, pt1, l1_in.last_pt()])
|
|
||||||
.ok()?,
|
|
||||||
},
|
|
||||||
Turn {
|
|
||||||
id: turn_id(i, l2_in.id, l2_out.id),
|
|
||||||
turn_type: TurnType::Crosswalk,
|
|
||||||
other_crosswalk_ids: all_ids.clone(),
|
|
||||||
geom: PolyLine::deduping_new(vec![l2_in.last_pt(), pt2, pt1, l2_out.first_pt()])
|
|
||||||
.ok()?,
|
|
||||||
},
|
|
||||||
Turn {
|
|
||||||
id: turn_id(i, l2_out.id, l2_in.id),
|
|
||||||
turn_type: TurnType::Crosswalk,
|
|
||||||
other_crosswalk_ids: all_ids,
|
|
||||||
geom: PolyLine::deduping_new(vec![l2_out.first_pt(), pt1, pt2, l2_in.last_pt()])
|
|
||||||
.ok()?,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut t| {
|
|
||||||
t.other_crosswalk_ids.remove(&t.id);
|
|
||||||
t
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO This doesn't handle sidewalk/shoulder transitions
|
// TODO This doesn't handle sidewalk/shoulder transitions
|
||||||
fn make_shared_sidewalk_corner(
|
fn make_shared_sidewalk_corner(i: &Intersection, l1: &Lane, l2: &Lane) -> PolyLine {
|
||||||
driving_side: DrivingSide,
|
// This may produce a polyline with two identical points. Nothing better to do here.
|
||||||
i: &Intersection,
|
let baseline = PolyLine::unchecked_new(vec![l1.endpoint(i.id), l2.endpoint(i.id)]);
|
||||||
l1: &Lane,
|
|
||||||
l2: &Lane,
|
|
||||||
) -> PolyLine {
|
|
||||||
let baseline = PolyLine::must_new(vec![l1.last_pt(), l2.first_pt()]);
|
|
||||||
|
|
||||||
|
// Is point2 counter-clockwise of point1?
|
||||||
|
let dir = if i
|
||||||
|
.polygon
|
||||||
|
.center()
|
||||||
|
.angle_to(l1.endpoint(i.id))
|
||||||
|
.simple_shortest_rotation_towards(i.polygon.center().angle_to(l2.endpoint(i.id)))
|
||||||
|
> 0.0
|
||||||
|
{
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
-1.0
|
||||||
|
};
|
||||||
// Find all of the points on the intersection polygon between the two sidewalks. Assumes
|
// Find all of the points on the intersection polygon between the two sidewalks. Assumes
|
||||||
// sidewalks are the same length.
|
// sidewalks are the same length.
|
||||||
let corner1 = l1.last_line().shift_right(l1.width / 2.0).pt2();
|
let corner1 = l1
|
||||||
let corner2 = l2.first_line().shift_right(l2.width / 2.0).pt1();
|
.end_line(i.id)
|
||||||
|
.shift_either_direction(dir * l1.width / 2.0)
|
||||||
|
.pt2();
|
||||||
|
let corner2 = l2
|
||||||
|
.end_line(i.id)
|
||||||
|
.shift_either_direction(-dir * l2.width / 2.0)
|
||||||
|
.pt2();
|
||||||
|
|
||||||
// TODO Something like this will be MUCH simpler and avoid going around the long way sometimes.
|
// TODO Something like this will be MUCH simpler and avoid going around the long way sometimes.
|
||||||
if false {
|
if false {
|
||||||
@ -504,12 +235,15 @@ fn make_shared_sidewalk_corner(
|
|||||||
|
|
||||||
// The order of the points here seems backwards, but it's because we scan from corner2
|
// The order of the points here seems backwards, but it's because we scan from corner2
|
||||||
// to corner1 below.
|
// to corner1 below.
|
||||||
let mut pts_between = vec![l2.first_pt()];
|
|
||||||
|
let mut pts_between = vec![l2.endpoint(i.id)];
|
||||||
// Intersection polygons are constructed in clockwise order, so do corner2 to corner1.
|
// Intersection polygons are constructed in clockwise order, so do corner2 to corner1.
|
||||||
let mut i_pts = i.polygon.points().clone();
|
let mut i_pts = i.polygon.points().clone();
|
||||||
if driving_side == DrivingSide::Left {
|
|
||||||
|
if dir < 0.0 {
|
||||||
i_pts.reverse();
|
i_pts.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pts) = Pt2D::find_pts_between(&i_pts, corner2, corner1, Distance::meters(0.5)) {
|
if let Some(pts) = Pt2D::find_pts_between(&i_pts, corner2, corner1, Distance::meters(0.5)) {
|
||||||
let mut deduped = pts;
|
let mut deduped = pts;
|
||||||
deduped.dedup();
|
deduped.dedup();
|
||||||
@ -528,7 +262,9 @@ fn make_shared_sidewalk_corner(
|
|||||||
return baseline;
|
return baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(pl) = PolyLine::must_new(deduped).shift_right(l1.width.min(l2.width) / 2.0) {
|
if let Ok(pl) = PolyLine::must_new(deduped)
|
||||||
|
.shift_either_direction(dir * l1.width.min(l2.width) / 2.0)
|
||||||
|
{
|
||||||
pts_between.extend(pl.points());
|
pts_between.extend(pl.points());
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
@ -540,7 +276,7 @@ fn make_shared_sidewalk_corner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pts_between.push(l1.last_pt());
|
pts_between.push(l1.endpoint(i.id));
|
||||||
pts_between.reverse();
|
pts_between.reverse();
|
||||||
// Pretty big smoothing; I'm observing funky backtracking about 0.5m long.
|
// Pretty big smoothing; I'm observing funky backtracking about 0.5m long.
|
||||||
let mut final_pts = Pt2D::approx_dedupe(pts_between.clone(), Distance::meters(1.0));
|
let mut final_pts = Pt2D::approx_dedupe(pts_between.clone(), Distance::meters(1.0));
|
||||||
@ -554,16 +290,17 @@ fn make_shared_sidewalk_corner(
|
|||||||
}
|
}
|
||||||
// The last point might be removed as a duplicate, but we want the start/end to exactly match
|
// The last point might be removed as a duplicate, but we want the start/end to exactly match
|
||||||
// up at least.
|
// up at least.
|
||||||
if *final_pts.last().unwrap() != l2.first_pt() {
|
if *final_pts.last().unwrap() != l2.endpoint(i.id) {
|
||||||
final_pts.pop();
|
final_pts.pop();
|
||||||
final_pts.push(l2.first_pt());
|
final_pts.push(l2.endpoint(i.id));
|
||||||
}
|
}
|
||||||
if abstutil::contains_duplicates(
|
if abstutil::contains_duplicates(
|
||||||
&final_pts
|
&final_pts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pt| pt.to_hashable())
|
.map(|pt| pt.to_hashable())
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
) {
|
) || final_pts.len() < 2
|
||||||
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"SharedSidewalkCorner between {} and {} has weird duplicate geometry, so just doing \
|
"SharedSidewalkCorner between {} and {} has weird duplicate geometry, so just doing \
|
||||||
straight line",
|
straight line",
|
||||||
@ -588,12 +325,3 @@ fn make_shared_sidewalk_corner(
|
|||||||
fn turn_id(parent: IntersectionID, src: LaneID, dst: LaneID) -> TurnID {
|
fn turn_id(parent: IntersectionID, src: LaneID, dst: LaneID) -> TurnID {
|
||||||
TurnID { parent, src, dst }
|
TurnID { parent, src, dst }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sidewalk(map: &Map, children: Vec<(LaneID, LaneType)>) -> Option<&Lane> {
|
|
||||||
for (id, lt) in children {
|
|
||||||
if lt.is_walkable() {
|
|
||||||
return Some(map.get_l(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
@ -329,24 +329,15 @@ impl Stage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_movement(&mut self, g: &Movement, pri: TurnPriority) {
|
pub fn edit_movement(&mut self, g: &Movement, pri: TurnPriority) {
|
||||||
let mut ids = vec![g.id];
|
|
||||||
if g.turn_type.pedestrian_crossing() {
|
if g.turn_type.pedestrian_crossing() {
|
||||||
ids.push(MovementID {
|
|
||||||
from: g.id.to,
|
|
||||||
to: g.id.from,
|
|
||||||
parent: g.id.parent,
|
|
||||||
crosswalk: true,
|
|
||||||
});
|
|
||||||
self.enforce_minimum_crosswalk_time(g);
|
self.enforce_minimum_crosswalk_time(g);
|
||||||
}
|
}
|
||||||
for id in ids {
|
self.protected_movements.remove(&g.id);
|
||||||
self.protected_movements.remove(&id);
|
self.yield_movements.remove(&g.id);
|
||||||
self.yield_movements.remove(&id);
|
if pri == TurnPriority::Protected {
|
||||||
if pri == TurnPriority::Protected {
|
self.protected_movements.insert(g.id);
|
||||||
self.protected_movements.insert(id);
|
} else if pri == TurnPriority::Yield {
|
||||||
} else if pri == TurnPriority::Yield {
|
self.yield_movements.insert(g.id);
|
||||||
self.yield_movements.insert(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn enforce_minimum_crosswalk_time(&mut self, movement: &Movement) {
|
pub fn enforce_minimum_crosswalk_time(&mut self, movement: &Movement) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::collections::BTreeSet;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -79,9 +79,6 @@ pub struct Turn {
|
|||||||
// TODO Some turns might not actually have geometry. Currently encoded by two equal points.
|
// TODO Some turns might not actually have geometry. Currently encoded by two equal points.
|
||||||
// Represent more directly?
|
// Represent more directly?
|
||||||
pub geom: PolyLine,
|
pub geom: PolyLine,
|
||||||
/// Empty except for TurnType::Crosswalk and UnmarkedCrossing. Usually just one other ID,
|
|
||||||
/// except for the case of 4 duplicates at a degenerate intersection.
|
|
||||||
pub other_crosswalk_ids: BTreeSet<TurnID>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Turn {
|
impl Turn {
|
||||||
|
Loading…
Reference in New Issue
Block a user