simplify intersection polygon code a bit, then handle the degenerate cases

This commit is contained in:
Dustin Carlino 2018-10-31 20:31:12 -07:00
parent c255f3aa28
commit 161081da6d
3 changed files with 116 additions and 73 deletions

View File

@ -22,6 +22,8 @@ ControlMap, DrawMap, etc.
Need to figure out how to handle reaping old IDs for transient objects like Need to figure out how to handle reaping old IDs for transient objects like
cars, but also things like modified roads. Slot maps? cars, but also things like modified roads. Slot maps?
Sort of related -- http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/
## Everything as FSMs ## Everything as FSMs
Driving and walking layer are both kind of broken, since they know about Driving and walking layer are both kind of broken, since they know about

View File

@ -55,3 +55,5 @@ Small complication with two directional sidewalks
original direction always? How to start/end walking? original direction always? How to start/end walking?
- do they belong to children {forwards, backwards}? They'd no longer be - do they belong to children {forwards, backwards}? They'd no longer be
in order. in order.
And what about modeling shared left-turn lanes?

View File

@ -1,106 +1,145 @@
use geom::{PolyLine, Pt2D}; use dimensioned::si;
use geom::{PolyLine, Angle, Pt2D};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::marker;
use {Intersection, Road, RoadID, LANE_THICKNESS}; use {Intersection, Road, RoadID, LANE_THICKNESS};
const DEGENERATE_INTERSECTION_HALF_LENGTH: si::Meter<f64> = si::Meter {
value_unsafe: 5.0,
_marker: marker::PhantomData,
};
// The polygon should exist entirely within the thick bands around all original roads -- it just
// carves up part of that space, doesn't reach past it.
pub fn intersection_polygon( pub fn intersection_polygon(
i: &Intersection, i: &Intersection,
road_ids: BTreeSet<RoadID>, road_ids: BTreeSet<RoadID>,
roads: &Vec<Road>, roads: &Vec<Road>,
) -> Vec<Pt2D> { ) -> Vec<Pt2D> {
// Turn all of the incident roads into the center PolyLine, always pointing at the intersection // Turn all of the incident roads into two PolyLines (the "forwards" and "backwards" borders of
// (endpoint is pt). The f64's are the width to shift without transforming the points, and then // the road), both with an endpoint at i.point, and the angle of the last segment of the center
// the width to shift when reversing the points. // line.
// TODO Simplify code by just stashing the two transformed PolyLines here. ;) let mut lines: Vec<(RoadID, Angle, PolyLine, PolyLine)> = road_ids
let mut center_lines: Vec<(PolyLine, RoadID, f64, f64)> = road_ids
.into_iter() .into_iter()
.map(|id| { .map(|id| {
let r = &roads[id.0]; let r = &roads[id.0];
let line = &r.center_pts; let center_line = &r.center_pts;
let fwd_width = LANE_THICKNESS * (r.children_forwards.len() as f64); let fwd_width = LANE_THICKNESS * (r.children_forwards.len() as f64);
let back_width = LANE_THICKNESS * (r.children_backwards.len() as f64); let back_width = LANE_THICKNESS * (r.children_backwards.len() as f64);
if line.first_pt() == i.point { let (line, width_normal, width_reverse) = if center_line.first_pt() == i.point {
(line.reversed(), id, back_width, fwd_width) (center_line.reversed(), back_width, fwd_width)
} else if line.last_pt() == i.point { } else if center_line.last_pt() == i.point {
(line.clone(), id, fwd_width, back_width) (center_line.clone(), fwd_width, back_width)
} else { } else {
panic!("Incident road {} doesn't have an endpoint at {}", id, i.id); panic!("Incident road {} doesn't have an endpoint at {}", id, i.id);
} };
let pl_normal = line.shift(width_normal).unwrap();
let pl_reverse = line.reversed().shift(width_reverse).unwrap().reversed();
(id, line.last_line().angle(), pl_normal, pl_reverse)
}).collect(); }).collect();
// Sort the polylines by the angle of their last segment. // Sort the polylines by the angle of their last segment.
// TODO This might break weirdly for polylines with very short last lines! // 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); lines.sort_by_key(|(_, angle, _, _)| angle.normalized_degrees() as i64);
// Now look at adjacent pairs of these polylines... // Special cases for degenerate intersections.
let mut endpoints: Vec<Pt2D> = Vec::new(); let mut endpoints: Vec<Pt2D> = Vec::new();
for idx1 in 0..center_lines.len() as isize { if lines.len() == 1 {
let idx2 = idx1 + 1; // Dead-ends!
let (id, _, pl_a, pl_b) = &lines[0];
let (center1, id1, _, width1_reverse) = wraparound_get(&center_lines, idx1); let pt1 = pl_a
let (center2, id2, width2_normal, _) = wraparound_get(&center_lines, idx2);
// 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() .reversed()
.shift(*width1_reverse) .safe_dist_along(DEGENERATE_INTERSECTION_HALF_LENGTH * 2.0)
.unwrap() .map(|(pt, _)| pt);
.reversed(); let pt2 = pl_b
let pl2 = center2.shift(*width2_normal).unwrap(); .reversed()
.safe_dist_along(DEGENERATE_INTERSECTION_HALF_LENGTH * 2.0)
.map(|(pt, _)| pt);
if pt1.is_some() && pt2.is_some() {
endpoints.extend(vec![
pt1.unwrap(),
pt2.unwrap(),
pl_b.last_pt(),
pl_a.last_pt(),
]);
} else {
error!("{} is a dead-end for {}, which is too short to make degenerate intersection geometry", i.id, id);
endpoints.extend(vec![pl_a.last_pt(), pl_b.last_pt()]);
}
} else if lines.len() == 2 {
let (id1, _, pl1_a, pl1_b) = &lines[0];
let (id2, _, pl2_a, pl2_b) = &lines[1];
endpoints.extend(
vec![pl1_a, pl1_b, pl2_a, pl2_b]
.into_iter()
.filter_map(|l| {
l.reversed()
.safe_dist_along(DEGENERATE_INTERSECTION_HALF_LENGTH)
.map(|(pt, _)| pt)
}).collect::<Vec<Pt2D>>(),
);
if endpoints.len() != 4 {
error!("{} has only {} and {}, some of which are too short to make degenerate intersection geometry", i.id, id1, id2);
endpoints.clear();
endpoints.extend(vec![
pl1_a.last_pt(),
pl1_b.last_pt(),
pl2_a.last_pt(),
pl2_b.last_pt(),
]);
}
} else {
// Look at adjacent pairs of these polylines...
for idx1 in 0..lines.len() as isize {
let idx2 = idx1 + 1;
// If the two lines are too close in angle, they'll either not hit or even if they do, it let (id1, _, _, pl1) = wraparound_get(&lines, idx1);
// won't be right. let (id2, _, pl2, _) = wraparound_get(&lines, idx2);
let angle_diff = (pl1.last_line().angle().opposite().normalized_degrees()
- pl2.last_line().angle().normalized_degrees()).abs();
// TODO A tuning challenge. :) // If the two lines are too close in angle, they'll either not hit or even if they do, it
if angle_diff > 15.0 { // won't be right.
// The easy case! let angle_diff = (pl1.last_line().angle().opposite().normalized_degrees()
if let Some(hit) = pl1.intersection(&pl2) { - pl2.last_line().angle().normalized_degrees()).abs();
endpoints.push(hit);
continue; // TODO A tuning challenge. :)
if angle_diff > 15.0 {
// The easy case!
if let Some(hit) = pl1.intersection(&pl2) {
endpoints.push(hit);
continue;
}
} }
}
let mut ok = true; let mut ok = true;
// Use the next adjacent road, doing line to line segment intersection instead. // Use the next adjacent road, doing line to line segment intersection instead.
let inf_line1 = { let inf_line1 = wraparound_get(&lines, idx1 - 1).3.last_line();
let (center, _, _, width_reverse) = wraparound_get(&center_lines, idx1 - 1); if let Some(hit) = pl1.intersection_infinite_line(inf_line1) {
center endpoints.push(hit);
.reversed() } else {
.shift(*width_reverse) endpoints.push(pl1.last_pt());
.unwrap() ok = false;
.reversed() }
.last_line()
};
if let Some(hit) = pl1.intersection_infinite_line(inf_line1) {
endpoints.push(hit);
} else {
endpoints.push(pl1.last_pt());
ok = false;
}
let inf_line2 = { let inf_line2 = wraparound_get(&lines, idx2 + 1).2.last_line();
let (center, _, width_normal, _) = wraparound_get(&center_lines, idx2 + 1); if let Some(hit) = pl2.intersection_infinite_line(inf_line2) {
center.shift(*width_normal).unwrap().last_line() endpoints.push(hit);
}; } else {
if let Some(hit) = pl2.intersection_infinite_line(inf_line2) { endpoints.push(pl2.last_pt());
endpoints.push(hit); ok = false;
} else { }
endpoints.push(pl2.last_pt());
ok = false;
}
if !ok { if !ok {
warn!( warn!(
"No hit btwn {} and {}, for {} with {} incident roads", "No hit btwn {} and {}, for {} with {} incident roads",
id1, id1,
id2, id2,
i.id, i.id,
center_lines.len() lines.len()
); );
}
} }
} }