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:
Dustin Carlino 2018-10-31 09:59:06 -07:00
parent 0ac633905b
commit dc12555b03
3 changed files with 73 additions and 99 deletions

View File

@ -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 {

View File

@ -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(), &center_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

View File

@ -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;