mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 17:37:22 +03:00
simplify intersection polygon code a bit, then handle the degenerate cases
This commit is contained in:
parent
c255f3aa28
commit
161081da6d
@ -22,6 +22,8 @@ ControlMap, DrawMap, etc.
|
||||
Need to figure out how to handle reaping old IDs for transient objects like
|
||||
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
|
||||
|
||||
Driving and walking layer are both kind of broken, since they know about
|
||||
|
@ -55,3 +55,5 @@ Small complication with two directional sidewalks
|
||||
original direction always? How to start/end walking?
|
||||
- do they belong to children {forwards, backwards}? They'd no longer be
|
||||
in order.
|
||||
|
||||
And what about modeling shared left-turn lanes?
|
||||
|
@ -1,106 +1,145 @@
|
||||
use geom::{PolyLine, Pt2D};
|
||||
use dimensioned::si;
|
||||
use geom::{PolyLine, Angle, Pt2D};
|
||||
use std::collections::BTreeSet;
|
||||
use std::marker;
|
||||
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(
|
||||
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.
|
||||
// TODO Simplify code by just stashing the two transformed PolyLines here. ;)
|
||||
let mut center_lines: Vec<(PolyLine, RoadID, f64, f64)> = road_ids
|
||||
// Turn all of the incident roads into two PolyLines (the "forwards" and "backwards" borders of
|
||||
// the road), both with an endpoint at i.point, and the angle of the last segment of the center
|
||||
// line.
|
||||
let mut lines: Vec<(RoadID, Angle, PolyLine, PolyLine)> = road_ids
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
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 back_width = LANE_THICKNESS * (r.children_backwards.len() as f64);
|
||||
|
||||
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)
|
||||
let (line, width_normal, width_reverse) = if center_line.first_pt() == i.point {
|
||||
(center_line.reversed(), back_width, fwd_width)
|
||||
} else if center_line.last_pt() == i.point {
|
||||
(center_line.clone(), fwd_width, back_width)
|
||||
} else {
|
||||
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();
|
||||
|
||||
// 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);
|
||||
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();
|
||||
for idx1 in 0..center_lines.len() as isize {
|
||||
let idx2 = idx1 + 1;
|
||||
|
||||
let (center1, id1, _, width1_reverse) = wraparound_get(¢er_lines, idx1);
|
||||
let (center2, id2, width2_normal, _) = wraparound_get(¢er_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
|
||||
if lines.len() == 1 {
|
||||
// Dead-ends!
|
||||
let (id, _, pl_a, pl_b) = &lines[0];
|
||||
let pt1 = pl_a
|
||||
.reversed()
|
||||
.shift(*width1_reverse)
|
||||
.unwrap()
|
||||
.reversed();
|
||||
let pl2 = center2.shift(*width2_normal).unwrap();
|
||||
.safe_dist_along(DEGENERATE_INTERSECTION_HALF_LENGTH * 2.0)
|
||||
.map(|(pt, _)| pt);
|
||||
let pt2 = pl_b
|
||||
.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
|
||||
// won't be right.
|
||||
let angle_diff = (pl1.last_line().angle().opposite().normalized_degrees()
|
||||
- pl2.last_line().angle().normalized_degrees()).abs();
|
||||
let (id1, _, _, pl1) = wraparound_get(&lines, idx1);
|
||||
let (id2, _, pl2, _) = wraparound_get(&lines, idx2);
|
||||
|
||||
// TODO A tuning challenge. :)
|
||||
if angle_diff > 15.0 {
|
||||
// The easy case!
|
||||
if let Some(hit) = pl1.intersection(&pl2) {
|
||||
endpoints.push(hit);
|
||||
continue;
|
||||
// If the two lines are too close in angle, they'll either not hit or even if they do, it
|
||||
// won't be right.
|
||||
let angle_diff = (pl1.last_line().angle().opposite().normalized_degrees()
|
||||
- pl2.last_line().angle().normalized_degrees()).abs();
|
||||
|
||||
// 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.
|
||||
let inf_line1 = {
|
||||
let (center, _, _, width_reverse) = wraparound_get(¢er_lines, idx1 - 1);
|
||||
center
|
||||
.reversed()
|
||||
.shift(*width_reverse)
|
||||
.unwrap()
|
||||
.reversed()
|
||||
.last_line()
|
||||
};
|
||||
if let Some(hit) = pl1.intersection_infinite_line(inf_line1) {
|
||||
endpoints.push(hit);
|
||||
} else {
|
||||
endpoints.push(pl1.last_pt());
|
||||
ok = false;
|
||||
}
|
||||
// Use the next adjacent road, doing line to line segment intersection instead.
|
||||
let inf_line1 = wraparound_get(&lines, idx1 - 1).3.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 (center, _, width_normal, _) = wraparound_get(¢er_lines, idx2 + 1);
|
||||
center.shift(*width_normal).unwrap().last_line()
|
||||
};
|
||||
if let Some(hit) = pl2.intersection_infinite_line(inf_line2) {
|
||||
endpoints.push(hit);
|
||||
} else {
|
||||
endpoints.push(pl2.last_pt());
|
||||
ok = false;
|
||||
}
|
||||
let inf_line2 = wraparound_get(&lines, idx2 + 1).2.last_line();
|
||||
if let Some(hit) = pl2.intersection_infinite_line(inf_line2) {
|
||||
endpoints.push(hit);
|
||||
} else {
|
||||
endpoints.push(pl2.last_pt());
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if !ok {
|
||||
warn!(
|
||||
"No hit btwn {} and {}, for {} with {} incident roads",
|
||||
id1,
|
||||
id2,
|
||||
i.id,
|
||||
center_lines.len()
|
||||
);
|
||||
if !ok {
|
||||
warn!(
|
||||
"No hit btwn {} and {}, for {} with {} incident roads",
|
||||
id1,
|
||||
id2,
|
||||
i.id,
|
||||
lines.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user