mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
simplified the intersection polygon algorithm to just look at angle-adjacent pairs of lines. same results, simpler code. almost ready to fix the triangle intersections.
This commit is contained in:
parent
0ac633905b
commit
dc12555b03
@ -144,18 +144,25 @@ impl Renderable for DrawLane {
|
||||
fn tooltip_lines(&self, map: &map_model::Map) -> Vec<String> {
|
||||
let l = map.get_l(self.id);
|
||||
let r = map.get_r(l.parent);
|
||||
let i1 = map.get_source_intersection(self.id);
|
||||
let i2 = map.get_destination_intersection(self.id);
|
||||
let r_endpt = if r.center_pts.last_pt() == i1.point {
|
||||
i1.id
|
||||
} else if r.center_pts.last_pt() == i2.point {
|
||||
i2.id
|
||||
} else {
|
||||
panic!("{} doesn't end at {} or {}", r.id, i1.id, i2.id);
|
||||
};
|
||||
|
||||
let mut lines = vec![
|
||||
format!(
|
||||
"{} is {}",
|
||||
l.id,
|
||||
r.osm_tags.get("name").unwrap_or(&"???".to_string())
|
||||
),
|
||||
format!("From OSM way {}, parent is {}", r.osm_way_id, r.id,),
|
||||
format!(
|
||||
"Lane goes from {} to {}",
|
||||
map.get_source_intersection(self.id).elevation,
|
||||
map.get_destination_intersection(self.id).elevation,
|
||||
),
|
||||
format!("From OSM way {}", r.osm_way_id),
|
||||
format!("Parent {} points to {}", r.id, r_endpt),
|
||||
format!("Lane goes from {} to {}", i1.elevation, i2.elevation),
|
||||
format!("Lane is {} long", l.length()),
|
||||
];
|
||||
for (k, v) in &r.osm_tags {
|
||||
|
@ -1,98 +1,69 @@
|
||||
use dimensioned::si;
|
||||
use geom::{PolyLine, Pt2D};
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use {Road, RoadID, LANE_THICKNESS};
|
||||
use std::collections::BTreeSet;
|
||||
use std::iter;
|
||||
use {Intersection, Road, RoadID, LANE_THICKNESS};
|
||||
|
||||
pub fn intersection_polygon(pt: Pt2D, road_ids: BTreeSet<RoadID>, roads: &Vec<Road>) -> Vec<Pt2D> {
|
||||
// Turn each incident road into two PolyLines, forming the border of the entire road.
|
||||
let mut lines: Vec<PolyLine> = Vec::new();
|
||||
for id in road_ids.into_iter() {
|
||||
let r = &roads[id.0];
|
||||
let fwd_width = LANE_THICKNESS * (r.children_forwards.len() as f64);
|
||||
let back_width = LANE_THICKNESS * (r.children_backwards.len() as f64);
|
||||
pub fn intersection_polygon(
|
||||
i: &Intersection,
|
||||
road_ids: BTreeSet<RoadID>,
|
||||
roads: &Vec<Road>,
|
||||
) -> Vec<Pt2D> {
|
||||
// Turn all of the incident roads into the center PolyLine, always pointing at the intersection
|
||||
// (endpoint is pt). The f64's are the width to shift without transforming the points, and then
|
||||
// the width to shift when reversing the points.
|
||||
let mut center_lines: Vec<(PolyLine, RoadID, f64, f64)> = road_ids
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
let r = &roads[id.0];
|
||||
let line = &r.center_pts;
|
||||
let fwd_width = LANE_THICKNESS * (r.children_forwards.len() as f64);
|
||||
let back_width = LANE_THICKNESS * (r.children_backwards.len() as f64);
|
||||
|
||||
// All of the lines are "incoming" to the intersection pt, meaning their last point is at
|
||||
// the intersection.
|
||||
let line = &r.center_pts;
|
||||
// TODO shift(...).unwrap() should maybe fall back to shift_blindly or something
|
||||
if line.first_pt() == pt {
|
||||
lines.push(line.shift(fwd_width).unwrap().reversed());
|
||||
lines.push(line.reversed().shift(back_width).unwrap());
|
||||
} else if line.last_pt() == pt {
|
||||
lines.push(line.shift(fwd_width).unwrap());
|
||||
lines.push(line.reversed().shift(back_width).unwrap().reversed());
|
||||
if line.first_pt() == i.point {
|
||||
(line.reversed(), id, back_width, fwd_width)
|
||||
} else if line.last_pt() == i.point {
|
||||
(line.clone(), id, fwd_width, back_width)
|
||||
} else {
|
||||
panic!("Incident road {} doesn't have an endpoint at {}", id, i.id);
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// Sort the polylines by the angle of their last segment.
|
||||
// TODO This might break weirdly for polylines with very short last lines!
|
||||
center_lines.sort_by_key(|(pl, _, _, _)| pl.last_line().angle().normalized_degrees() as i64);
|
||||
|
||||
// Now look at adjacent pairs of these polylines...
|
||||
let mut endpoints: Vec<Pt2D> = Vec::new();
|
||||
for ((center1, id1, _, width1_reverse), (center2, id2, width2_normal, _)) in center_lines
|
||||
.iter()
|
||||
.zip(center_lines.iter().skip(1))
|
||||
.chain(iter::once((center_lines.last().unwrap(), ¢er_lines[0])))
|
||||
{
|
||||
// Turn the center polylines into one of the road's border polylines. Every road should
|
||||
// have a chance to be shifted in both directions.
|
||||
let pl1 = center1
|
||||
.reversed()
|
||||
.shift(*width1_reverse)
|
||||
.unwrap()
|
||||
.reversed();
|
||||
let pl2 = center2.shift(*width2_normal).unwrap();
|
||||
|
||||
if let Some(hit) = pl1.intersection(&pl2) {
|
||||
endpoints.push(hit);
|
||||
} else {
|
||||
panic!("Incident road {} doesn't have an endpoint at {}", id, pt);
|
||||
warn!(
|
||||
"No hit btwn {} and {}, for {} with {} incident roads",
|
||||
id1,
|
||||
id2,
|
||||
i.id,
|
||||
center_lines.len()
|
||||
);
|
||||
endpoints.push(pl1.last_pt());
|
||||
endpoints.push(pl2.last_pt());
|
||||
}
|
||||
}
|
||||
|
||||
// Now trim all of the lines against all others.
|
||||
// TODO The next step is to just consider adjacent (in the angle sense) pairs and handle ~180
|
||||
// deg offsets between pairs.
|
||||
// usize indexes into lines
|
||||
let mut shortest_line: BTreeMap<usize, (PolyLine, si::Meter<f64>)> = BTreeMap::new();
|
||||
|
||||
fn update_shortest(
|
||||
m: &mut BTreeMap<usize, (PolyLine, si::Meter<f64>)>,
|
||||
idx: usize,
|
||||
pl: PolyLine,
|
||||
) {
|
||||
let new_len = pl.length();
|
||||
|
||||
match m.entry(idx) {
|
||||
Entry::Occupied(mut o) => {
|
||||
if new_len < o.get().1 {
|
||||
o.insert((pl, new_len));
|
||||
}
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert((pl, new_len));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This matches by polyline, so short first/last lines should be fine
|
||||
for idx1 in 0..lines.len() {
|
||||
for idx2 in 0..lines.len() {
|
||||
if idx1 == idx2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pl1 = &lines[idx1];
|
||||
let pl2 = &lines[idx2];
|
||||
if pl1 == pl2 {
|
||||
panic!("Both {} and {} have same pts?! {}", idx1, idx2, pl1);
|
||||
}
|
||||
|
||||
if let Some(hit) = pl1.intersection(&pl2) {
|
||||
let mut new_pl1 = pl1.clone();
|
||||
if new_pl1.trim_to_pt(hit) {
|
||||
update_shortest(&mut shortest_line, idx1, new_pl1);
|
||||
}
|
||||
|
||||
let mut new_pl2 = pl2.clone();
|
||||
if new_pl2.trim_to_pt(hit) {
|
||||
update_shortest(&mut shortest_line, idx2, new_pl2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the updates
|
||||
for (idx, (pl, _)) in shortest_line.into_iter() {
|
||||
lines[idx] = pl;
|
||||
}
|
||||
|
||||
// Now finally use all of the endpoints of the trimmed lines to make a polygon!
|
||||
let mut endpoints: Vec<Pt2D> = lines.into_iter().map(|l| l.last_pt()).collect();
|
||||
// TODO Safer to not use original intersection pt?
|
||||
let center = Pt2D::center(&endpoints);
|
||||
// Sort points by angle from the center
|
||||
endpoints.sort_by_key(|pt| center.angle_to(*pt).normalized_degrees() as i64);
|
||||
// Both lines get trimmed to the same endpoint, so we wind up with dupe points.
|
||||
// TODO This entire algorithm could be later simplified
|
||||
endpoints.dedup();
|
||||
// Close off the polygon
|
||||
let first_pt = endpoints[0].clone();
|
||||
endpoints.push(first_pt);
|
||||
endpoints
|
||||
|
@ -176,11 +176,7 @@ impl Map {
|
||||
for i in &m.intersections {
|
||||
timer.next();
|
||||
let incident_roads = i.get_roads(&m);
|
||||
intersection_polygons.push(make::intersection_polygon(
|
||||
i.point,
|
||||
incident_roads,
|
||||
&m.roads,
|
||||
));
|
||||
intersection_polygons.push(make::intersection_polygon(i, incident_roads, &m.roads));
|
||||
}
|
||||
for (idx, p) in intersection_polygons.into_iter().enumerate() {
|
||||
m.intersections[idx].polygon = p;
|
||||
|
Loading…
Reference in New Issue
Block a user