1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
use geom::Distance;
use crate::{Direction, DrivingSide, LaneSpec, LaneType};
impl LaneSpec {
/// Convert the driving lanes of a road between one-way forwards, one-way backwards, and
/// bidirectional. It should be idempotent to apply this 3 times in a row. Even when an
/// existing one-way street is narrow, it can be made bidirectional by splitting into two very
/// narrow lanes.
pub fn toggle_road_direction(lanes_ltr: &mut Vec<LaneSpec>, driving_side: DrivingSide) {
let leftmost_dir = if driving_side == DrivingSide::Right {
Direction::Back
} else {
Direction::Fwd
};
let oneway_dir = LaneSpec::oneway_for_driving(lanes_ltr);
let mut num_driving_lanes = lanes_ltr
.iter()
.filter(|lane| lane.lt == LaneType::Driving)
.count();
// Pre-processing: if it's one-way backwards and there's only one driving lane,
// split it into two narrow pieces
if oneway_dir == Some(Direction::Back) && num_driving_lanes == 1 {
// TODO If there's parking, use that
let idx = lanes_ltr
.iter()
.position(|x| x.lt == LaneType::Driving)
.unwrap();
lanes_ltr[idx].width *= 0.5;
lanes_ltr.insert(idx, lanes_ltr[idx].clone());
num_driving_lanes = 2;
}
// And undo the above
if oneway_dir == None && num_driving_lanes == 2 {
let idx = lanes_ltr
.iter()
.position(|x| x.lt == LaneType::Driving)
.unwrap();
// Is it super narrow?
// TODO Potentially brittle. SERVICE_ROAD_LANE_THICKNESS is 1.5,
// NORMAL_LANE_THICKNESS is 2.5. Half of either one is less than 1.5.
if lanes_ltr[idx].width < Distance::meters(1.5) {
lanes_ltr.remove(idx);
lanes_ltr[idx].width *= 2.0;
}
}
let mut driving_lanes_so_far = 0;
for lane in lanes_ltr {
if lane.lt == LaneType::Driving {
driving_lanes_so_far += 1;
match oneway_dir {
Some(Direction::Fwd) => {
// If it's one-way forwards, flip the direction
lane.dir = Direction::Back;
}
Some(Direction::Back) => {
// If it's one-way backwards, make it bidirectional. Split the
// directions down the middle
if (driving_lanes_so_far as f64) / (num_driving_lanes as f64) <= 0.5 {
lane.dir = leftmost_dir;
} else {
lane.dir = leftmost_dir.opposite();
}
}
None => {
// TODO If it's narrow...
// If it's bidirectional, make it one-way
lane.dir = Direction::Fwd;
}
}
}
}
}
}