mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
making turns for crosswalks. had to rethink how turns work slightly.
This commit is contained in:
parent
9ca0682108
commit
9c21dae3d9
@ -28,8 +28,6 @@
|
||||
|
||||
## pedestrians
|
||||
|
||||
- calculate turns at crosswalks, see how they conflict
|
||||
- for now, treat same in control layer
|
||||
- pedestrians with different speeds, moving bidirectionally on everything
|
||||
- make them start and end at buildings
|
||||
- trim the sidewalk path to the edge of a building
|
||||
|
@ -186,6 +186,28 @@ https://wiki.openstreetmap.org/wiki/Proposed_features/Street_area
|
||||
- v1: remember other_side for sidewalks too. draw crosswalks at the beginning AND end of every sidewalk lane.
|
||||
- do extra drawing in DrawIntersection for now, figure out modeling later.
|
||||
|
||||
- alright, directional lanes and turns dont fit sidewalks at all. turn icons
|
||||
are drawn at one end. the turns-in-an-intersection invariant is broken, since
|
||||
src and dst dont match up for one side.
|
||||
- could kind of cheat by doubling lanes for sidewalks and making the geometry
|
||||
overlap, but this feels like a worse hack. it's very tempting to ditch lanes
|
||||
and turns for a different way to model sidewalks and crosswalks.
|
||||
- for now, let's make a distinct road and lane abstraction and plumb that all the way through. see what it'd be like to have some more primitives:
|
||||
|
||||
- Intersection
|
||||
- Building
|
||||
- Parcel
|
||||
- Road (undirected bundle)
|
||||
- Driving/biking Lane (directed)
|
||||
- Sidewalk (undirected)
|
||||
- Parking lane (no orientation... or, kind of like a driving lane)
|
||||
- Turn (directed and not)
|
||||
|
||||
but the fact that sidewalks are oriented is actually convenient, it makes it clear that incoming's last pt should be glued to outgoing's first pt.
|
||||
|
||||
what if we just add a bit and make turns bidirectional? still express them in the directional way?
|
||||
if we're looking at turns from a road that's a sidewalk, bake in some extra logic?
|
||||
|
||||
## Stop signs
|
||||
|
||||
How to depict stop signs? Each driving lane has a priority... asap go or full
|
||||
|
@ -120,8 +120,6 @@ impl SelectionState {
|
||||
}
|
||||
}
|
||||
SelectionState::SelectedRoad(id, current_turn_index) => {
|
||||
let all_turns: Vec<&map_model::Turn> =
|
||||
map.get_turns_in_intersection(map.get_destination_intersection(id).id);
|
||||
let relevant_turns = map.get_turns_from_road(id);
|
||||
if !relevant_turns.is_empty() {
|
||||
match current_turn_index {
|
||||
@ -130,10 +128,10 @@ impl SelectionState {
|
||||
let draw_turn =
|
||||
draw_map.get_t(relevant_turns[idx % relevant_turns.len()].id);
|
||||
draw_turn.draw_full(g, cs.get(Colors::Turn));
|
||||
for map_t in all_turns {
|
||||
let t = map.get_t(map_t.id);
|
||||
let draw_t = draw_map.get_t(map_t.id);
|
||||
|
||||
for t in map.get_turns_in_intersection(turn.parent) {
|
||||
if t.conflicts_with(turn) {
|
||||
let draw_t = draw_map.get_t(t.id);
|
||||
// TODO should we instead change color_t?
|
||||
draw_t.draw_icon(g, cs.get(Colors::ConflictingTurn), cs);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use aabb_quadtree::QuadTree;
|
||||
use geom::{LonLat, Pt2D};
|
||||
use map_model::{BuildingID, IntersectionID, Map, ParcelID, RoadID, TurnID};
|
||||
use map_model::{BuildingID, IntersectionID, Map, ParcelID, RoadID, Turn, TurnID};
|
||||
use plugins::selection::Hider;
|
||||
use render::building::DrawBuilding;
|
||||
use render::intersection::DrawIntersection;
|
||||
@ -36,14 +36,25 @@ impl DrawMap {
|
||||
|
||||
let mut turn_to_road_offset: HashMap<TurnID, usize> = HashMap::new();
|
||||
for r in map.all_roads() {
|
||||
let mut turns = map.get_turns_from_road(r.id);
|
||||
// Sort the turn icons by angle.
|
||||
turns.sort_by_key(|t| t.line.angle().normalized_degrees() as i64);
|
||||
// Split into two groups, based on the endpoint
|
||||
let mut pair: (Vec<&Turn>, Vec<&Turn>) = map.get_turns_from_road(r.id)
|
||||
.iter()
|
||||
.partition(|t| t.parent == r.dst_i);
|
||||
|
||||
for (idx, t) in turns.iter().enumerate() {
|
||||
// Sort the turn icons by angle.
|
||||
pair.0
|
||||
.sort_by_key(|t| t.line.angle().normalized_degrees() as i64);
|
||||
pair.1
|
||||
.sort_by_key(|t| t.line.angle().normalized_degrees() as i64);
|
||||
|
||||
for (idx, t) in pair.0.iter().enumerate() {
|
||||
turn_to_road_offset.insert(t.id, idx);
|
||||
}
|
||||
for (idx, t) in pair.1.iter().enumerate() {
|
||||
turn_to_road_offset.insert(t.id, idx);
|
||||
}
|
||||
}
|
||||
assert_eq!(turn_to_road_offset.len(), map.all_turns().len());
|
||||
|
||||
let turns: Vec<DrawTurn> = map.all_turns()
|
||||
.iter()
|
||||
|
@ -31,12 +31,13 @@ impl DrawTurn {
|
||||
let src_pt = turn.line.pt1();
|
||||
let dst_pt = turn.line.pt2();
|
||||
let angle = turn.line.angle();
|
||||
let last_line = map.get_r(turn.src).last_line();
|
||||
|
||||
let end_line = map.get_r(turn.src).end_line(turn.parent);
|
||||
// Start the distance from the intersection
|
||||
let icon_center = last_line
|
||||
let icon_center = end_line
|
||||
.reverse()
|
||||
.unbounded_dist_along((offset_along_road + 0.5) * TURN_ICON_ARROW_LENGTH * si::M);
|
||||
|
||||
let icon_src = icon_center
|
||||
.project_away(TURN_ICON_ARROW_LENGTH / 2.0, angle.opposite())
|
||||
.to_vec();
|
||||
|
@ -6,4 +6,4 @@ mod turns;
|
||||
pub(crate) use self::buildings::make_building;
|
||||
pub(crate) use self::lanes::get_lane_specs;
|
||||
pub(crate) use self::trim_lines::trim_lines;
|
||||
pub(crate) use self::turns::make_turns;
|
||||
pub(crate) use self::turns::{make_crosswalks, make_turns};
|
||||
|
@ -42,8 +42,73 @@ pub(crate) fn make_turns(i: &Intersection, m: &Map, turn_id_start: usize) -> Vec
|
||||
src: *src,
|
||||
dst: *dst,
|
||||
line: Line::new(src_r.last_pt(), dst_r.first_pt()),
|
||||
between_sidewalks: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn make_crosswalks(i: &Intersection, m: &Map, mut turn_id_start: usize) -> Vec<Turn> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
// TODO dedupe some of this logic render/intersection
|
||||
|
||||
// First make all of the crosswalks -- from each incoming and outgoing sidewalk to its other
|
||||
// side
|
||||
for id in i.incoming_roads.iter().chain(i.outgoing_roads.iter()) {
|
||||
let src = m.get_r(*id);
|
||||
if src.lane_type != LaneType::Sidewalk {
|
||||
continue;
|
||||
}
|
||||
let dst = m.get_r(src.other_side.unwrap());
|
||||
|
||||
let id = TurnID(turn_id_start);
|
||||
turn_id_start += 1;
|
||||
result.push(Turn {
|
||||
id,
|
||||
parent: i.id,
|
||||
src: src.id,
|
||||
dst: dst.id,
|
||||
line: Line::new(src.endpoint(i.id), dst.endpoint(i.id)),
|
||||
between_sidewalks: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Then all of the immediate connections onto the shared point
|
||||
for id1 in i.incoming_roads.iter().chain(i.outgoing_roads.iter()) {
|
||||
let src = m.get_r(*id1);
|
||||
if src.lane_type != LaneType::Sidewalk {
|
||||
continue;
|
||||
}
|
||||
let src_pt = src.endpoint(i.id);
|
||||
for id2 in i.incoming_roads.iter().chain(i.outgoing_roads.iter()) {
|
||||
if id1 == id2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let dst = m.get_r(*id2);
|
||||
if dst.lane_type != LaneType::Sidewalk {
|
||||
continue;
|
||||
}
|
||||
let dst_pt = dst.endpoint(i.id);
|
||||
|
||||
if src_pt != dst_pt {
|
||||
continue;
|
||||
}
|
||||
|
||||
let id = TurnID(turn_id_start);
|
||||
turn_id_start += 1;
|
||||
result.push(Turn {
|
||||
id,
|
||||
parent: i.id,
|
||||
src: src.id,
|
||||
dst: dst.id,
|
||||
line: Line::new(src_pt, dst_pt),
|
||||
between_sidewalks: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -128,6 +128,8 @@ impl Map {
|
||||
for i in &m.intersections {
|
||||
let turns = make::make_turns(i, &m, m.turns.len());
|
||||
m.turns.extend(turns);
|
||||
let crosswalks = make::make_crosswalks(i, &m, m.turns.len());
|
||||
m.turns.extend(crosswalks);
|
||||
}
|
||||
for t in &m.turns {
|
||||
m.intersections[t.parent.0].turns.push(t.id);
|
||||
@ -209,14 +211,25 @@ impl Map {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_turns_from_road(&self, id: RoadID) -> Vec<&Turn> {
|
||||
let i = self.get_destination_intersection(id);
|
||||
// TODO can't filter on get_turns_in_intersection... winds up being Vec<&&Turn>
|
||||
i.turns
|
||||
// The turns may belong to two different intersections!
|
||||
pub fn get_turns_from_road(&self, r: RoadID) -> Vec<&Turn> {
|
||||
let road = self.get_r(r);
|
||||
let mut turns: Vec<&Turn> = self.get_i(road.dst_i)
|
||||
.turns
|
||||
.iter()
|
||||
.map(|t| self.get_t(*t))
|
||||
.filter(|t| t.src == id)
|
||||
.collect()
|
||||
.filter(|t| t.src == r)
|
||||
.collect();
|
||||
// Sidewalks are bidirectional
|
||||
if road.lane_type == LaneType::Sidewalk {
|
||||
for t in &self.get_i(road.src_i).turns {
|
||||
let turn = self.get_t(*t);
|
||||
if turn.src == r {
|
||||
turns.push(turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
turns
|
||||
}
|
||||
|
||||
pub fn get_next_roads(&self, from: RoadID) -> Vec<&Road> {
|
||||
|
@ -90,6 +90,27 @@ impl Road {
|
||||
self.lane_center_pts.last_line()
|
||||
}
|
||||
|
||||
pub fn endpoint(&self, i: IntersectionID) -> Pt2D {
|
||||
if i == self.src_i {
|
||||
self.first_pt()
|
||||
} else if i == self.dst_i {
|
||||
self.last_pt()
|
||||
} else {
|
||||
panic!("{} isn't an endpoint of {}", i, self.id);
|
||||
}
|
||||
}
|
||||
|
||||
// pt2 will be endpoint
|
||||
pub fn end_line(&self, i: IntersectionID) -> Line {
|
||||
if i == self.src_i {
|
||||
self.first_line().reverse()
|
||||
} else if i == self.dst_i {
|
||||
self.last_line()
|
||||
} else {
|
||||
panic!("{} isn't an endpoint of {}", i, self.id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dist_along(&self, dist_along: si::Meter<f64>) -> (Pt2D, Angle) {
|
||||
self.lane_center_pts.dist_along(dist_along)
|
||||
}
|
||||
|
@ -20,9 +20,12 @@ impl fmt::Display for TurnID {
|
||||
#[derive(Debug)]
|
||||
pub struct Turn {
|
||||
pub id: TurnID,
|
||||
// src and dst must both belong to parent. No guarantees that src is incoming and dst is
|
||||
// outgoing for turns between sidewalks.
|
||||
pub parent: IntersectionID,
|
||||
pub src: RoadID,
|
||||
pub dst: RoadID,
|
||||
pub(crate) between_sidewalks: bool,
|
||||
|
||||
/// GeomTurn stuff
|
||||
pub line: Line,
|
||||
@ -36,6 +39,10 @@ impl PartialEq for Turn {
|
||||
|
||||
impl Turn {
|
||||
pub fn conflicts_with(&self, other: &Turn) -> bool {
|
||||
if self.between_sidewalks && other.between_sidewalks {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.line.pt1() == other.line.pt1() {
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user