mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +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
|
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
|
||||||
|
@ -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?
|
||||||
|
@ -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(¢er_lines, idx1);
|
let pt1 = pl_a
|
||||||
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
|
|
||||||
.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(¢er_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(¢er_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()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user