mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +03:00
overhaul ControlTrafficSignal to natively understand TurnGroups. but
it... didn't solve the problem of a left turn yield going first?!
This commit is contained in:
parent
715fc80e04
commit
1145bbc8e6
@ -13,8 +13,7 @@ use ezgui::{
|
||||
};
|
||||
use geom::Duration;
|
||||
use map_model::{
|
||||
ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroupID, TurnID, TurnPriority,
|
||||
TurnType,
|
||||
ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroupID, TurnPriority, TurnType,
|
||||
};
|
||||
use std::collections::BTreeSet;
|
||||
use std::time::Instant;
|
||||
@ -64,9 +63,7 @@ impl TrafficSignalEditor {
|
||||
|
||||
impl State for TrafficSignalEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
|
||||
let mut signal = ui.primary.map.get_traffic_signal(self.diagram.i).clone();
|
||||
// TODO Temporary hack
|
||||
let signal_copy = signal.clone();
|
||||
let orig_signal = ui.primary.map.get_traffic_signal(self.diagram.i);
|
||||
|
||||
self.menu.event(ctx);
|
||||
// TODO This really needs to be shown in the diagram!
|
||||
@ -74,7 +71,7 @@ impl State for TrafficSignalEditor {
|
||||
ctx,
|
||||
Text::from(Line(format!(
|
||||
"Signal offset: {}",
|
||||
signal.offset.minimal_tostring()
|
||||
orig_signal.offset.minimal_tostring()
|
||||
))),
|
||||
);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
@ -93,11 +90,12 @@ impl State for TrafficSignalEditor {
|
||||
}
|
||||
|
||||
if let Some(id) = self.group_selected {
|
||||
let phase = &mut signal.phases[self.diagram.current_phase()];
|
||||
let mut new_signal = orig_signal.clone();
|
||||
let phase = &mut new_signal.phases[self.diagram.current_phase()];
|
||||
// Just one key to toggle between the 3 states
|
||||
let next_priority = match phase.get_priority_group(id, &signal_copy) {
|
||||
let next_priority = match phase.get_priority_of_group(id) {
|
||||
TurnPriority::Banned => {
|
||||
if phase.could_be_protected_group(id, &signal_copy, &ui.primary.map) {
|
||||
if phase.could_be_protected(id, &orig_signal.turn_groups) {
|
||||
Some(TurnPriority::Protected)
|
||||
} else if id.crosswalk.is_some() {
|
||||
None
|
||||
@ -119,34 +117,40 @@ impl State for TrafficSignalEditor {
|
||||
Key::Space,
|
||||
format!(
|
||||
"toggle from {:?} to {:?}",
|
||||
phase.get_priority_group(id, &signal_copy),
|
||||
phase.get_priority_of_group(id),
|
||||
pri
|
||||
),
|
||||
) {
|
||||
phase.edit_group(id, pri, &signal_copy, &ui.primary.map);
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
phase.edit_group(
|
||||
&orig_signal.turn_groups[&id],
|
||||
pri,
|
||||
&orig_signal.turn_groups,
|
||||
&ui.primary.map,
|
||||
);
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
return Transition::Keep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.menu.action("quit") {
|
||||
return check_for_missing_turns(signal, &mut self.diagram, ui, ctx);
|
||||
return check_for_missing_groups(orig_signal.clone(), &mut self.diagram, ui, ctx);
|
||||
}
|
||||
|
||||
if self.menu.action("change phase duration") {
|
||||
return Transition::Push(change_phase_duration(
|
||||
signal.phases[self.diagram.current_phase()].duration,
|
||||
orig_signal.phases[self.diagram.current_phase()].duration,
|
||||
));
|
||||
} else if self.menu.action("change signal offset") {
|
||||
return Transition::Push(change_offset(signal.offset));
|
||||
return Transition::Push(change_offset(orig_signal.offset));
|
||||
} else if self.menu.action("choose a preset signal") {
|
||||
return Transition::Push(change_preset(self.diagram.i));
|
||||
} else if self.menu.action("reset to original") {
|
||||
signal = ControlTrafficSignal::get_possible_policies(&ui.primary.map, self.diagram.i)
|
||||
.remove(0)
|
||||
.1;
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
let new_signal =
|
||||
ControlTrafficSignal::get_possible_policies(&ui.primary.map, self.diagram.i)
|
||||
.remove(0)
|
||||
.1;
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
self.diagram = TrafficSignalDiagram::new(self.diagram.i, 0, ui, ctx);
|
||||
return Transition::Keep;
|
||||
}
|
||||
@ -161,19 +165,22 @@ impl State for TrafficSignalEditor {
|
||||
let current_phase = self.diagram.current_phase();
|
||||
|
||||
if current_phase != 0 && self.menu.action("move current phase up") {
|
||||
signal.phases.swap(current_phase, current_phase - 1);
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
let mut new_signal = orig_signal.clone();
|
||||
new_signal.phases.swap(current_phase, current_phase - 1);
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
self.diagram = TrafficSignalDiagram::new(self.diagram.i, current_phase - 1, ui, ctx);
|
||||
} else if current_phase != signal.phases.len() - 1
|
||||
} else if current_phase != orig_signal.phases.len() - 1
|
||||
&& self.menu.action("move current phase down")
|
||||
{
|
||||
signal.phases.swap(current_phase, current_phase + 1);
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
let mut new_signal = orig_signal.clone();
|
||||
new_signal.phases.swap(current_phase, current_phase + 1);
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
self.diagram = TrafficSignalDiagram::new(self.diagram.i, current_phase + 1, ui, ctx);
|
||||
} else if signal.phases.len() > 1 && self.menu.action("delete current phase") {
|
||||
signal.phases.remove(current_phase);
|
||||
let num_phases = signal.phases.len();
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
} else if orig_signal.phases.len() > 1 && self.menu.action("delete current phase") {
|
||||
let mut new_signal = orig_signal.clone();
|
||||
new_signal.phases.remove(current_phase);
|
||||
let num_phases = new_signal.phases.len();
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
self.diagram = TrafficSignalDiagram::new(
|
||||
self.diagram.i,
|
||||
if current_phase == num_phases {
|
||||
@ -185,32 +192,29 @@ impl State for TrafficSignalEditor {
|
||||
ctx,
|
||||
);
|
||||
} else if self.menu.action("add a new empty phase") {
|
||||
let mut phase = Phase::new();
|
||||
for t in ui.primary.map.get_turns_in_intersection(self.diagram.i) {
|
||||
if t.turn_type == TurnType::SharedSidewalkCorner {
|
||||
phase.protected_turns.insert(t.id);
|
||||
}
|
||||
}
|
||||
signal.phases.insert(current_phase + 1, phase);
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
let mut new_signal = orig_signal.clone();
|
||||
new_signal.phases.insert(current_phase + 1, Phase::new());
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
self.diagram = TrafficSignalDiagram::new(self.diagram.i, current_phase + 1, ui, ctx);
|
||||
} else if has_sidewalks && self.menu.action("add a new pedestrian scramble phase") {
|
||||
let mut phase = Phase::new();
|
||||
for t in ui.primary.map.get_turns_in_intersection(self.diagram.i) {
|
||||
if t.between_sidewalks() {
|
||||
phase.protected_turns.insert(t.id);
|
||||
for g in orig_signal.turn_groups.values() {
|
||||
if g.turn_type == TurnType::Crosswalk {
|
||||
phase.protected_groups.insert(g.id);
|
||||
}
|
||||
}
|
||||
signal.phases.insert(current_phase + 1, phase);
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
let mut new_signal = orig_signal.clone();
|
||||
new_signal.phases.insert(current_phase + 1, phase);
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
self.diagram = TrafficSignalDiagram::new(self.diagram.i, current_phase + 1, ui, ctx);
|
||||
} else if has_sidewalks
|
||||
&& self
|
||||
.menu
|
||||
.action("convert to dedicated pedestrian scramble phase")
|
||||
{
|
||||
signal.convert_to_ped_scramble(&ui.primary.map);
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
let mut new_signal = orig_signal.clone();
|
||||
new_signal.convert_to_ped_scramble(&ui.primary.map);
|
||||
change_traffic_signal(new_signal, ui, ctx);
|
||||
self.diagram = TrafficSignalDiagram::new(self.diagram.i, 0, ui, ctx);
|
||||
}
|
||||
|
||||
@ -259,7 +263,7 @@ impl State for TrafficSignalEditor {
|
||||
g.block.clone(),
|
||||
);
|
||||
}
|
||||
let arrow_color = match phase.get_priority_group(g.id, &signal) {
|
||||
let arrow_color = match phase.get_priority_of_group(g.id) {
|
||||
TurnPriority::Protected => ui.cs.get("turn protected by traffic signal"),
|
||||
TurnPriority::Yield => ui
|
||||
.cs
|
||||
@ -369,43 +373,31 @@ fn change_preset(i: IntersectionID) -> Box<dyn State> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn check_for_missing_turns(
|
||||
fn check_for_missing_groups(
|
||||
mut signal: ControlTrafficSignal,
|
||||
diagram: &mut TrafficSignalDiagram,
|
||||
ui: &mut UI,
|
||||
ctx: &mut EventCtx,
|
||||
) -> Transition {
|
||||
let mut missing_turns: BTreeSet<TurnID> = ui
|
||||
.primary
|
||||
.map
|
||||
.get_i(signal.id)
|
||||
.turns
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let mut missing: BTreeSet<TurnGroupID> = signal.turn_groups.keys().cloned().collect();
|
||||
for phase in &signal.phases {
|
||||
for t in &phase.protected_turns {
|
||||
missing_turns.remove(t);
|
||||
for g in &phase.protected_groups {
|
||||
missing.remove(g);
|
||||
}
|
||||
for t in &phase.yield_turns {
|
||||
missing_turns.remove(t);
|
||||
for g in &phase.yield_groups {
|
||||
missing.remove(g);
|
||||
}
|
||||
}
|
||||
if missing_turns.is_empty() {
|
||||
if missing.is_empty() {
|
||||
let i = signal.id;
|
||||
if let Err(err) = signal.validate(&ui.primary.map) {
|
||||
if let Err(err) = signal.validate() {
|
||||
panic!("Edited traffic signal {} finalized with errors: {}", i, err);
|
||||
}
|
||||
return Transition::Pop;
|
||||
}
|
||||
let num_missing = missing_turns.len();
|
||||
let num_missing = missing.len();
|
||||
let mut phase = Phase::new();
|
||||
phase.yield_turns = missing_turns;
|
||||
for t in ui.primary.map.get_turns_in_intersection(signal.id) {
|
||||
if t.turn_type == TurnType::SharedSidewalkCorner {
|
||||
phase.protected_turns.insert(t.id);
|
||||
}
|
||||
}
|
||||
phase.yield_groups = missing;
|
||||
signal.phases.push(phase);
|
||||
let last_phase = signal.phases.len() - 1;
|
||||
change_traffic_signal(signal, ui, ctx);
|
||||
|
@ -28,52 +28,61 @@ pub fn draw_signal_phase(
|
||||
Color::rgba(255, 105, 180, 0.8),
|
||||
);
|
||||
|
||||
let signal = ctx.map.get_traffic_signal(i);
|
||||
for (id, crosswalk) in &ctx.draw_map.get_i(i).crosswalks {
|
||||
if phase.get_priority(*id) == TurnPriority::Protected {
|
||||
if phase.get_priority_of_turn(*id, signal) == TurnPriority::Protected {
|
||||
batch.append(crosswalk.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if true {
|
||||
let (protected, yielding) = phase.active_turn_groups(ctx.map.get_traffic_signal(i));
|
||||
for g in protected {
|
||||
if g.id.crosswalk.is_none() {
|
||||
for g in &phase.protected_groups {
|
||||
if g.crosswalk.is_none() {
|
||||
batch.push(
|
||||
protected_color,
|
||||
g.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
|
||||
signal.turn_groups[g]
|
||||
.geom
|
||||
.make_arrow(BIG_ARROW_THICKNESS * 2.0)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
for g in yielding {
|
||||
if g.id.crosswalk.is_none() {
|
||||
for g in &phase.yield_groups {
|
||||
if g.crosswalk.is_none() {
|
||||
batch.extend(
|
||||
yield_color,
|
||||
g.geom
|
||||
signal.turn_groups[g]
|
||||
.geom
|
||||
.make_arrow_outline(BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For debugging, can still show individual turns:
|
||||
for t in &phase.protected_turns {
|
||||
let turn = ctx.map.get_t(*t);
|
||||
if !turn.between_sidewalks() {
|
||||
batch.push(
|
||||
protected_color,
|
||||
turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
|
||||
);
|
||||
// For debugging, can still show individual turns
|
||||
for turn in ctx.map.get_turns_in_intersection(i) {
|
||||
if turn.between_sidewalks() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for t in &phase.yield_turns {
|
||||
let turn = ctx.map.get_t(*t);
|
||||
if !turn.between_sidewalks() {
|
||||
batch.extend(
|
||||
yield_color,
|
||||
turn.geom
|
||||
.make_arrow_outline(BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0)
|
||||
.unwrap(),
|
||||
);
|
||||
match phase.get_priority_of_turn(turn.id, signal) {
|
||||
TurnPriority::Protected => {
|
||||
batch.push(
|
||||
protected_color,
|
||||
turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
|
||||
);
|
||||
}
|
||||
TurnPriority::Yield => {
|
||||
batch.extend(
|
||||
yield_color,
|
||||
turn.geom
|
||||
.make_arrow_outline(
|
||||
BIG_ARROW_THICKNESS * 2.0,
|
||||
BIG_ARROW_THICKNESS / 2.0,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
TurnPriority::Banned => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,8 +124,9 @@ fn draw_signal_phase_with_icons(
|
||||
batch: &mut GeomBatch,
|
||||
ctx: &DrawCtx,
|
||||
) {
|
||||
let signal = ctx.map.get_traffic_signal(i);
|
||||
for (id, crosswalk) in &ctx.draw_map.get_i(i).crosswalks {
|
||||
if phase.get_priority(*id) == TurnPriority::Protected {
|
||||
if phase.get_priority_of_turn(*id, signal) == TurnPriority::Protected {
|
||||
batch.append(crosswalk.clone());
|
||||
}
|
||||
}
|
||||
@ -138,7 +148,7 @@ fn draw_signal_phase_with_icons(
|
||||
continue;
|
||||
}
|
||||
|
||||
match phase.get_priority(turn.id) {
|
||||
match phase.get_priority_of_turn(turn.id, signal) {
|
||||
TurnPriority::Protected => {
|
||||
green.push(turn.id);
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::{
|
||||
IntersectionID, Map, RoadID, Turn, TurnGroup, TurnGroupID, TurnID, TurnPriority, TurnType,
|
||||
};
|
||||
use abstutil::{deserialize_btreemap, serialize_btreemap, Timer};
|
||||
use crate::{IntersectionID, Map, RoadID, TurnGroup, TurnGroupID, TurnID, TurnPriority, TurnType};
|
||||
use abstutil::{deserialize_btreemap, retain_btreeset, serialize_btreemap, Timer};
|
||||
use geom::Duration;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
@ -21,8 +19,8 @@ pub struct ControlTrafficSignal {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Phase {
|
||||
pub protected_turns: BTreeSet<TurnID>,
|
||||
pub yield_turns: BTreeSet<TurnID>,
|
||||
pub protected_groups: BTreeSet<TurnGroupID>,
|
||||
pub yield_groups: BTreeSet<TurnGroupID>,
|
||||
pub duration: Duration,
|
||||
}
|
||||
|
||||
@ -86,44 +84,45 @@ impl ControlTrafficSignal {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn validate(self, map: &Map) -> Result<ControlTrafficSignal, String> {
|
||||
// TODO Reuse assertions from edit_turn.
|
||||
|
||||
// Does the assignment cover the correct set of turns?
|
||||
let expected_turns: BTreeSet<TurnID> = map.get_i(self.id).turns.iter().cloned().collect();
|
||||
let mut actual_turns: BTreeSet<TurnID> = BTreeSet::new();
|
||||
pub fn validate(self) -> Result<ControlTrafficSignal, String> {
|
||||
// Does the assignment cover the correct set of groups?
|
||||
let expected_groups: BTreeSet<TurnGroupID> = self.turn_groups.keys().cloned().collect();
|
||||
let mut actual_groups: BTreeSet<TurnGroupID> = BTreeSet::new();
|
||||
for phase in &self.phases {
|
||||
actual_turns.extend(phase.protected_turns.iter());
|
||||
actual_turns.extend(phase.yield_turns.iter());
|
||||
actual_groups.extend(phase.protected_groups.iter());
|
||||
actual_groups.extend(phase.yield_groups.iter());
|
||||
}
|
||||
if expected_turns != actual_turns {
|
||||
return Err(format!("Traffic signal assignment for {} broken. Missing turns {:?}, contains irrelevant turns {:?}", self.id, expected_turns.difference(&actual_turns).cloned().collect::<Vec<TurnID>>(), actual_turns.difference(&expected_turns).cloned().collect::<Vec<TurnID>>()));
|
||||
if expected_groups != actual_groups {
|
||||
return Err(format!(
|
||||
"Traffic signal assignment for {} broken. Missing {:?}, contains irrelevant {:?}",
|
||||
self.id,
|
||||
expected_groups
|
||||
.difference(&actual_groups)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
actual_groups
|
||||
.difference(&expected_groups)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
|
||||
for phase in &self.phases {
|
||||
// Do any of the priority turns in one phase conflict?
|
||||
for t1 in phase.protected_turns.iter().map(|t| map.get_t(*t)) {
|
||||
for t2 in phase.protected_turns.iter().map(|t| map.get_t(*t)) {
|
||||
if t1.conflicts_with(t2) {
|
||||
// Do any of the priority groups in one phase conflict?
|
||||
for g1 in phase.protected_groups.iter().map(|g| &self.turn_groups[g]) {
|
||||
for g2 in phase.protected_groups.iter().map(|g| &self.turn_groups[g]) {
|
||||
if g1.conflicts_with(g2) {
|
||||
return Err(format!(
|
||||
"Traffic signal has conflicting priority turns in one phase:\n{:?}\n\n{:?}",
|
||||
t1, t2
|
||||
"Traffic signal has conflicting protected groups in one phase:\n{:?}\n\n{:?}",
|
||||
g1, g2
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do any of the crosswalks yield? Are all of the SharedSidewalkCorner prioritized?
|
||||
for t in map.get_turns_in_intersection(self.id) {
|
||||
match t.turn_type {
|
||||
TurnType::Crosswalk => {
|
||||
assert!(!phase.yield_turns.contains(&t.id));
|
||||
}
|
||||
TurnType::SharedSidewalkCorner => {
|
||||
assert!(phase.protected_turns.contains(&t.id));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Do any of the crosswalks yield?
|
||||
for g in phase.yield_groups.iter().map(|g| &self.turn_groups[g]) {
|
||||
assert!(g.turn_type != TurnType::Crosswalk);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,49 +130,44 @@ impl ControlTrafficSignal {
|
||||
}
|
||||
|
||||
fn greedy_assignment(map: &Map, intersection: IntersectionID) -> ControlTrafficSignal {
|
||||
if map.get_turns_in_intersection(intersection).is_empty() {
|
||||
panic!("{} has no turns", intersection);
|
||||
}
|
||||
let turn_groups = TurnGroup::for_i(intersection, map);
|
||||
|
||||
let mut phases = Vec::new();
|
||||
|
||||
// Greedily partition turns into phases. More clever things later. No yields.
|
||||
let mut remaining_turns: Vec<TurnID> = map
|
||||
.get_turns_in_intersection(intersection)
|
||||
.iter()
|
||||
.map(|t| t.id)
|
||||
.collect();
|
||||
// Greedily partition groups into phases that only have protected groups.
|
||||
let mut remaining_groups: Vec<TurnGroupID> = turn_groups.keys().cloned().collect();
|
||||
let mut current_phase = Phase::new();
|
||||
loop {
|
||||
let add_turn = remaining_turns
|
||||
let add = remaining_groups
|
||||
.iter()
|
||||
.position(|&t| current_phase.could_be_protected_turn(t, map));
|
||||
match add_turn {
|
||||
.position(|&g| current_phase.could_be_protected(g, &turn_groups));
|
||||
match add {
|
||||
Some(idx) => {
|
||||
current_phase
|
||||
.protected_turns
|
||||
.insert(remaining_turns.remove(idx));
|
||||
.protected_groups
|
||||
.insert(remaining_groups.remove(idx));
|
||||
}
|
||||
None => {
|
||||
assert!(!current_phase.protected_groups.is_empty());
|
||||
phases.push(current_phase);
|
||||
current_phase = Phase::new();
|
||||
if remaining_turns.is_empty() {
|
||||
if remaining_groups.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expand_all_phases(&mut phases, map, intersection);
|
||||
expand_all_phases(&mut phases, &turn_groups);
|
||||
|
||||
let ts = ControlTrafficSignal {
|
||||
id: intersection,
|
||||
phases,
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(intersection, map),
|
||||
turn_groups,
|
||||
};
|
||||
// This must succeed
|
||||
ts.validate(map).unwrap()
|
||||
ts.validate().unwrap()
|
||||
}
|
||||
|
||||
fn degenerate(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
|
||||
@ -187,11 +181,7 @@ impl ControlTrafficSignal {
|
||||
// TODO One-ways downtown should also have crosswalks.
|
||||
let has_crosswalks = !map.get_r(r1).children_backwards.is_empty()
|
||||
|| !map.get_r(r2).children_backwards.is_empty();
|
||||
let mut phases = vec![vec![
|
||||
(vec![r1, r2], TurnType::Straight, PROTECTED),
|
||||
(vec![r1, r2], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![r1, r2], TurnType::LaneChangeRight, YIELD),
|
||||
]];
|
||||
let mut phases = vec![vec![(vec![r1, r2], TurnType::Straight, PROTECTED)]];
|
||||
if has_crosswalks {
|
||||
phases.push(vec![(vec![r1, r2], TurnType::Crosswalk, PROTECTED)]);
|
||||
}
|
||||
@ -204,23 +194,20 @@ impl ControlTrafficSignal {
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(i, map),
|
||||
};
|
||||
ts.validate(map).ok()
|
||||
ts.validate().ok()
|
||||
}
|
||||
|
||||
fn three_way(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
|
||||
if map.get_i(i).roads.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
let turn_groups = TurnGroup::for_i(i, map);
|
||||
|
||||
// Picture a T intersection. Use turn angles to figure out the "main" two roads.
|
||||
let straight_turn = map
|
||||
.get_turns_in_intersection(i)
|
||||
.into_iter()
|
||||
.find(|t| t.turn_type == TurnType::Straight)?;
|
||||
let (north, south) = (
|
||||
map.get_l(straight_turn.id.src).parent,
|
||||
map.get_l(straight_turn.id.dst).parent,
|
||||
);
|
||||
let straight = turn_groups
|
||||
.values()
|
||||
.find(|g| g.turn_type == TurnType::Straight)?;
|
||||
let (north, south) = (straight.id.from, straight.id.to);
|
||||
let mut roads = map.get_i(i).roads.clone();
|
||||
roads.remove(&north);
|
||||
roads.remove(&south);
|
||||
@ -233,8 +220,6 @@ impl ControlTrafficSignal {
|
||||
vec![
|
||||
vec![
|
||||
(vec![north, south], TurnType::Straight, PROTECTED),
|
||||
(vec![north, south], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![north, south], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![north, south], TurnType::Right, YIELD),
|
||||
(vec![north, south], TurnType::Left, YIELD),
|
||||
(vec![east], TurnType::Right, YIELD),
|
||||
@ -242,8 +227,6 @@ impl ControlTrafficSignal {
|
||||
],
|
||||
vec![
|
||||
(vec![east], TurnType::Straight, PROTECTED),
|
||||
(vec![east], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![east], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![east], TurnType::Right, YIELD),
|
||||
(vec![east], TurnType::Left, YIELD),
|
||||
(vec![north, south], TurnType::Right, YIELD),
|
||||
@ -256,9 +239,9 @@ impl ControlTrafficSignal {
|
||||
id: i,
|
||||
phases,
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(i, map),
|
||||
turn_groups,
|
||||
};
|
||||
ts.validate(map).ok()
|
||||
ts.validate().ok()
|
||||
}
|
||||
|
||||
fn four_way_four_phase(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
|
||||
@ -280,8 +263,6 @@ impl ControlTrafficSignal {
|
||||
vec![
|
||||
vec![
|
||||
(vec![north, south], TurnType::Straight, PROTECTED),
|
||||
(vec![north, south], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![north, south], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![north, south], TurnType::Right, YIELD),
|
||||
(vec![east, west], TurnType::Right, YIELD),
|
||||
(vec![east, west], TurnType::Crosswalk, PROTECTED),
|
||||
@ -289,8 +270,6 @@ impl ControlTrafficSignal {
|
||||
vec![(vec![north, south], TurnType::Left, PROTECTED)],
|
||||
vec![
|
||||
(vec![east, west], TurnType::Straight, PROTECTED),
|
||||
(vec![east, west], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![east, west], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![east, west], TurnType::Right, YIELD),
|
||||
(vec![north, south], TurnType::Right, YIELD),
|
||||
(vec![north, south], TurnType::Crosswalk, PROTECTED),
|
||||
@ -305,7 +284,7 @@ impl ControlTrafficSignal {
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(i, map),
|
||||
};
|
||||
ts.validate(map).ok()
|
||||
ts.validate().ok()
|
||||
}
|
||||
|
||||
fn four_way_two_phase(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
|
||||
@ -326,8 +305,6 @@ impl ControlTrafficSignal {
|
||||
vec![
|
||||
vec![
|
||||
(vec![north, south], TurnType::Straight, PROTECTED),
|
||||
(vec![north, south], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![north, south], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![north, south], TurnType::Right, YIELD),
|
||||
(vec![north, south], TurnType::Left, YIELD),
|
||||
(vec![east, west], TurnType::Right, YIELD),
|
||||
@ -335,8 +312,6 @@ impl ControlTrafficSignal {
|
||||
],
|
||||
vec![
|
||||
(vec![east, west], TurnType::Straight, PROTECTED),
|
||||
(vec![east, west], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![east, west], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![east, west], TurnType::Right, YIELD),
|
||||
(vec![east, west], TurnType::Left, YIELD),
|
||||
(vec![north, south], TurnType::Right, YIELD),
|
||||
@ -351,7 +326,7 @@ impl ControlTrafficSignal {
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(i, map),
|
||||
};
|
||||
ts.validate(map).ok()
|
||||
ts.validate().ok()
|
||||
}
|
||||
|
||||
fn four_oneways(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
|
||||
@ -378,8 +353,6 @@ impl ControlTrafficSignal {
|
||||
vec![
|
||||
vec![
|
||||
(vec![r1], TurnType::Straight, PROTECTED),
|
||||
(vec![r1], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![r1], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![r1], TurnType::Crosswalk, PROTECTED),
|
||||
// TODO Technically, upgrade to protected if there's no opposing crosswalk --
|
||||
// even though it doesn't matter much.
|
||||
@ -390,8 +363,6 @@ impl ControlTrafficSignal {
|
||||
],
|
||||
vec![
|
||||
(vec![r2], TurnType::Straight, PROTECTED),
|
||||
(vec![r2], TurnType::LaneChangeLeft, YIELD),
|
||||
(vec![r2], TurnType::LaneChangeRight, YIELD),
|
||||
(vec![r2], TurnType::Crosswalk, PROTECTED),
|
||||
// TODO Technically, upgrade to protected if there's no opposing crosswalk --
|
||||
// even though it doesn't matter much.
|
||||
@ -408,24 +379,22 @@ impl ControlTrafficSignal {
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(i, map),
|
||||
};
|
||||
ts.validate(map).ok()
|
||||
ts.validate().ok()
|
||||
}
|
||||
|
||||
fn all_walk_all_yield(map: &Map, i: IntersectionID) -> ControlTrafficSignal {
|
||||
let turn_groups = TurnGroup::for_i(i, map);
|
||||
|
||||
let mut all_walk = Phase::new();
|
||||
let mut all_yield = Phase::new();
|
||||
|
||||
for turn in map.get_turns_in_intersection(i) {
|
||||
match turn.turn_type {
|
||||
TurnType::SharedSidewalkCorner => {
|
||||
all_walk.protected_turns.insert(turn.id);
|
||||
all_yield.protected_turns.insert(turn.id);
|
||||
}
|
||||
for group in turn_groups.values() {
|
||||
match group.turn_type {
|
||||
TurnType::Crosswalk => {
|
||||
all_walk.protected_turns.insert(turn.id);
|
||||
all_walk.protected_groups.insert(group.id);
|
||||
}
|
||||
_ => {
|
||||
all_yield.yield_turns.insert(turn.id);
|
||||
all_yield.yield_groups.insert(group.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,13 +403,15 @@ impl ControlTrafficSignal {
|
||||
id: i,
|
||||
phases: vec![all_walk, all_yield],
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(i, map),
|
||||
turn_groups,
|
||||
};
|
||||
// This must succeed
|
||||
ts.validate(map).unwrap()
|
||||
ts.validate().unwrap()
|
||||
}
|
||||
|
||||
fn phase_per_road(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
|
||||
let turn_groups = TurnGroup::for_i(i, map);
|
||||
|
||||
let mut phases = Vec::new();
|
||||
let sorted_roads = map
|
||||
.get_i(i)
|
||||
@ -451,20 +422,17 @@ impl ControlTrafficSignal {
|
||||
let adj2 = *abstutil::wraparound_get(&sorted_roads, (idx as isize) + 1);
|
||||
|
||||
let mut phase = Phase::new();
|
||||
for turn in map.get_turns_in_intersection(i) {
|
||||
let parent = map.get_l(turn.id.src).parent;
|
||||
if turn.turn_type == TurnType::SharedSidewalkCorner {
|
||||
phase.protected_turns.insert(turn.id);
|
||||
} else if turn.turn_type == TurnType::Crosswalk {
|
||||
if parent == adj1 || parent == adj2 {
|
||||
phase.protected_turns.insert(turn.id);
|
||||
for group in turn_groups.values() {
|
||||
if group.turn_type == TurnType::Crosswalk {
|
||||
if group.id.from == adj1 || group.id.from == adj2 {
|
||||
phase.protected_groups.insert(group.id);
|
||||
}
|
||||
} else if parent == r {
|
||||
phase.yield_turns.insert(turn.id);
|
||||
} else if group.id.from == r {
|
||||
phase.yield_groups.insert(group.id);
|
||||
}
|
||||
}
|
||||
// Might have a one-way outgoing road. Skip it.
|
||||
if !phase.yield_turns.is_empty() {
|
||||
if !phase.yield_groups.is_empty() {
|
||||
phases.push(phase);
|
||||
}
|
||||
}
|
||||
@ -472,39 +440,38 @@ impl ControlTrafficSignal {
|
||||
id: i,
|
||||
phases,
|
||||
offset: Duration::ZERO,
|
||||
turn_groups: TurnGroup::for_i(i, map),
|
||||
turn_groups,
|
||||
};
|
||||
ts.validate(map).ok()
|
||||
ts.validate().ok()
|
||||
}
|
||||
|
||||
pub fn convert_to_ped_scramble(&mut self, map: &Map) {
|
||||
// Remove Crosswalk turns from existing phases.
|
||||
for phase in self.phases.iter_mut() {
|
||||
// Crosswalks are usually only protected_turns, but also clear out from yield_turns.
|
||||
for t in map.get_turns_in_intersection(self.id) {
|
||||
if t.turn_type == TurnType::Crosswalk {
|
||||
phase.protected_turns.remove(&t.id);
|
||||
phase.yield_turns.remove(&t.id);
|
||||
}
|
||||
}
|
||||
// Remove Crosswalk groups from existing phases.
|
||||
let mut replaced = std::mem::replace(&mut self.phases, Vec::new());
|
||||
for phase in replaced.iter_mut() {
|
||||
// Crosswalks are only in protected_groups.
|
||||
retain_btreeset(&mut phase.protected_groups, |g| {
|
||||
self.turn_groups[g].turn_type != TurnType::Crosswalk
|
||||
});
|
||||
|
||||
// Blindly try to promote yield turns to protected, now that crosswalks are gone.
|
||||
// Blindly try to promote yield groups to protected, now that crosswalks are gone.
|
||||
let mut promoted = Vec::new();
|
||||
for t in &phase.yield_turns {
|
||||
if phase.could_be_protected_turn(*t, map) {
|
||||
phase.protected_turns.insert(*t);
|
||||
promoted.push(*t);
|
||||
for g in &phase.yield_groups {
|
||||
if phase.could_be_protected(*g, &self.turn_groups) {
|
||||
phase.protected_groups.insert(*g);
|
||||
promoted.push(*g);
|
||||
}
|
||||
}
|
||||
for t in promoted {
|
||||
phase.yield_turns.remove(&t);
|
||||
for g in promoted {
|
||||
phase.yield_groups.remove(&g);
|
||||
}
|
||||
}
|
||||
self.phases = replaced;
|
||||
|
||||
let mut phase = Phase::new();
|
||||
for t in map.get_turns_in_intersection(self.id) {
|
||||
if t.between_sidewalks() {
|
||||
phase.edit_turn(t, TurnPriority::Protected);
|
||||
for g in self.turn_groups.values() {
|
||||
if g.turn_type == TurnType::Crosswalk {
|
||||
phase.edit_group(g, TurnPriority::Protected, &self.turn_groups, map);
|
||||
}
|
||||
}
|
||||
self.phases.push(phase);
|
||||
@ -514,129 +481,83 @@ impl ControlTrafficSignal {
|
||||
impl Phase {
|
||||
pub fn new() -> Phase {
|
||||
Phase {
|
||||
protected_turns: BTreeSet::new(),
|
||||
yield_turns: BTreeSet::new(),
|
||||
protected_groups: BTreeSet::new(),
|
||||
yield_groups: BTreeSet::new(),
|
||||
duration: Duration::seconds(30.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn could_be_protected_turn(&self, t1: TurnID, map: &Map) -> bool {
|
||||
let turn1 = map.get_t(t1);
|
||||
for t2 in &self.protected_turns {
|
||||
if t1 == *t2 || turn1.conflicts_with(map.get_t(*t2)) {
|
||||
pub fn could_be_protected(
|
||||
&self,
|
||||
g1: TurnGroupID,
|
||||
turn_groups: &BTreeMap<TurnGroupID, TurnGroup>,
|
||||
) -> bool {
|
||||
let group1 = &turn_groups[&g1];
|
||||
for g2 in &self.protected_groups {
|
||||
if g1 == *g2 || group1.conflicts_with(&turn_groups[g2]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn get_priority(&self, t: TurnID) -> TurnPriority {
|
||||
if self.protected_turns.contains(&t) {
|
||||
pub fn get_priority_of_turn(&self, t: TurnID, parent: &ControlTrafficSignal) -> TurnPriority {
|
||||
// TODO Cache this?
|
||||
let g = parent
|
||||
.turn_groups
|
||||
.values()
|
||||
.find(|g| g.members.contains(&t))
|
||||
.map(|g| g.id)
|
||||
.unwrap();
|
||||
self.get_priority_of_group(g)
|
||||
}
|
||||
|
||||
pub fn get_priority_of_group(&self, g: TurnGroupID) -> TurnPriority {
|
||||
if self.protected_groups.contains(&g) {
|
||||
TurnPriority::Protected
|
||||
} else if self.yield_turns.contains(&t) {
|
||||
} else if self.yield_groups.contains(&g) {
|
||||
TurnPriority::Yield
|
||||
} else {
|
||||
TurnPriority::Banned
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_turn(&mut self, t: &Turn, pri: TurnPriority) {
|
||||
let mut ids = vec![t.id];
|
||||
if t.turn_type == TurnType::Crosswalk {
|
||||
ids.extend(t.other_crosswalk_ids.clone());
|
||||
}
|
||||
for id in ids {
|
||||
self.protected_turns.remove(&id);
|
||||
self.yield_turns.remove(&id);
|
||||
if pri == TurnPriority::Protected {
|
||||
self.protected_turns.insert(id);
|
||||
} else if pri == TurnPriority::Yield {
|
||||
self.yield_turns.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns (protected, permitted)
|
||||
pub fn active_turn_groups<'a>(
|
||||
&self,
|
||||
parent: &'a ControlTrafficSignal,
|
||||
) -> (Vec<&'a TurnGroup>, Vec<&'a TurnGroup>) {
|
||||
let mut protected = Vec::new();
|
||||
let mut permitted = Vec::new();
|
||||
for group in parent.turn_groups.values() {
|
||||
match self.get_priority_group(group.id, parent) {
|
||||
TurnPriority::Protected => {
|
||||
protected.push(group);
|
||||
}
|
||||
TurnPriority::Yield => {
|
||||
permitted.push(group);
|
||||
}
|
||||
TurnPriority::Banned => {}
|
||||
}
|
||||
}
|
||||
(protected, permitted)
|
||||
}
|
||||
|
||||
pub fn get_priority_group(
|
||||
&self,
|
||||
g: TurnGroupID,
|
||||
parent: &ControlTrafficSignal,
|
||||
) -> TurnPriority {
|
||||
// Any, not all.
|
||||
if parent.turn_groups[&g]
|
||||
.members
|
||||
.iter()
|
||||
.any(|t| self.get_priority(*t) == TurnPriority::Protected)
|
||||
{
|
||||
return TurnPriority::Protected;
|
||||
}
|
||||
if parent.turn_groups[&g]
|
||||
.members
|
||||
.iter()
|
||||
.any(|t| self.get_priority(*t) == TurnPriority::Yield)
|
||||
{
|
||||
return TurnPriority::Yield;
|
||||
}
|
||||
TurnPriority::Banned
|
||||
}
|
||||
|
||||
pub fn could_be_protected_group(
|
||||
&self,
|
||||
g: TurnGroupID,
|
||||
parent: &ControlTrafficSignal,
|
||||
map: &Map,
|
||||
) -> bool {
|
||||
// All, not any?
|
||||
parent.turn_groups[&g]
|
||||
.members
|
||||
.iter()
|
||||
.all(|t| self.could_be_protected_turn(*t, map))
|
||||
}
|
||||
|
||||
pub fn edit_group(
|
||||
&mut self,
|
||||
g: TurnGroupID,
|
||||
g: &TurnGroup,
|
||||
pri: TurnPriority,
|
||||
parent: &ControlTrafficSignal,
|
||||
turn_groups: &BTreeMap<TurnGroupID, TurnGroup>,
|
||||
map: &Map,
|
||||
) {
|
||||
for t in &parent.turn_groups[&g].members {
|
||||
self.edit_turn(map.get_t(*t), pri);
|
||||
let mut ids = vec![g.id];
|
||||
if g.turn_type == TurnType::Crosswalk {
|
||||
for t in &map.get_t(g.id.crosswalk.unwrap()).other_crosswalk_ids {
|
||||
ids.push(
|
||||
*turn_groups
|
||||
.keys()
|
||||
.find(|id| id.crosswalk == Some(*t))
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
for id in ids {
|
||||
self.protected_groups.remove(&id);
|
||||
self.yield_groups.remove(&id);
|
||||
if pri == TurnPriority::Protected {
|
||||
self.protected_groups.insert(id);
|
||||
} else if pri == TurnPriority::Yield {
|
||||
self.yield_groups.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all legal protected turns to existing phases.
|
||||
fn expand_all_phases(phases: &mut Vec<Phase>, map: &Map, intersection: IntersectionID) {
|
||||
let all_turns: Vec<TurnID> = map
|
||||
.get_turns_in_intersection(intersection)
|
||||
.iter()
|
||||
.map(|t| t.id)
|
||||
.collect();
|
||||
// Add all possible protected groups to existing phases.
|
||||
fn expand_all_phases(phases: &mut Vec<Phase>, turn_groups: &BTreeMap<TurnGroupID, TurnGroup>) {
|
||||
for phase in phases.iter_mut() {
|
||||
for t in &all_turns {
|
||||
if !phase.protected_turns.contains(t) && phase.could_be_protected_turn(*t, map) {
|
||||
phase.protected_turns.insert(*t);
|
||||
for g in turn_groups.keys() {
|
||||
if phase.could_be_protected(*g, turn_groups) {
|
||||
phase.protected_groups.insert(*g);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -650,38 +571,34 @@ fn make_phases(
|
||||
i: IntersectionID,
|
||||
phase_specs: Vec<Vec<(Vec<RoadID>, TurnType, bool)>>,
|
||||
) -> Vec<Phase> {
|
||||
// TODO Could pass this in instead of recompute...
|
||||
let turn_groups = TurnGroup::for_i(i, map);
|
||||
let mut phases: Vec<Phase> = Vec::new();
|
||||
|
||||
for specs in phase_specs {
|
||||
let mut phase = Phase::new();
|
||||
let mut empty = true;
|
||||
|
||||
for (roads, turn_type, protected) in specs.into_iter() {
|
||||
for turn in map.get_turns_in_intersection(i) {
|
||||
// These never conflict with anything.
|
||||
if turn.turn_type == TurnType::SharedSidewalkCorner {
|
||||
phase.protected_turns.insert(turn.id);
|
||||
for group in turn_groups.values() {
|
||||
if !roads.contains(&group.id.from) || turn_type != group.turn_type {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !roads.contains(&map.get_l(turn.id.src).parent) || turn_type != turn.turn_type {
|
||||
continue;
|
||||
}
|
||||
|
||||
phase.edit_turn(
|
||||
turn,
|
||||
phase.edit_group(
|
||||
group,
|
||||
if protected {
|
||||
TurnPriority::Protected
|
||||
} else {
|
||||
TurnPriority::Yield
|
||||
},
|
||||
&turn_groups,
|
||||
map,
|
||||
);
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out empty phases if they happen.
|
||||
if empty {
|
||||
if phase.protected_groups.is_empty() && phase.yield_groups.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,8 @@ impl TurnType {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This concept may be dated, now that TurnGroups exist. Within a group, the lane-changing
|
||||
// turns should be treated as less important.
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, PartialOrd)]
|
||||
pub enum TurnPriority {
|
||||
// For stop signs: Can't currently specify this!
|
||||
@ -128,6 +130,7 @@ pub struct TurnGroupID {
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct TurnGroup {
|
||||
pub id: TurnGroupID,
|
||||
pub turn_type: TurnType,
|
||||
pub members: Vec<TurnID>,
|
||||
// The "overall" path of movement, aka, an "average" of the turn geometry
|
||||
pub geom: PolyLine,
|
||||
@ -153,6 +156,7 @@ impl TurnGroup {
|
||||
id,
|
||||
TurnGroup {
|
||||
id,
|
||||
turn_type: TurnType::Crosswalk,
|
||||
members: vec![turn.id],
|
||||
geom: turn.geom.clone(),
|
||||
angle: turn.angle(),
|
||||
@ -170,6 +174,23 @@ impl TurnGroup {
|
||||
from,
|
||||
to,
|
||||
);
|
||||
let turn_types: BTreeSet<TurnType> = members
|
||||
.iter()
|
||||
.map(|t| match map.get_t(*t).turn_type {
|
||||
TurnType::Crosswalk | TurnType::SharedSidewalkCorner => unreachable!(),
|
||||
TurnType::Straight | TurnType::LaneChangeLeft | TurnType::LaneChangeRight => {
|
||||
TurnType::Straight
|
||||
}
|
||||
TurnType::Left => TurnType::Left,
|
||||
TurnType::Right => TurnType::Right,
|
||||
})
|
||||
.collect();
|
||||
if turn_types.len() > 1 {
|
||||
println!(
|
||||
"TurnGroup between {} and {} has weird turn types! {:?}",
|
||||
from, to, turn_types
|
||||
);
|
||||
}
|
||||
let members: Vec<TurnID> = members.into_iter().collect();
|
||||
let id = TurnGroupID {
|
||||
from,
|
||||
@ -180,12 +201,16 @@ impl TurnGroup {
|
||||
id,
|
||||
TurnGroup {
|
||||
id,
|
||||
turn_type: *turn_types.iter().next().unwrap(),
|
||||
angle: map.get_t(members[0]).angle(),
|
||||
members,
|
||||
geom,
|
||||
},
|
||||
);
|
||||
}
|
||||
if results.is_empty() {
|
||||
panic!("{} has no TurnGroups!", i);
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
@ -229,6 +254,23 @@ impl TurnGroup {
|
||||
let width = LANE_THICKNESS * ((*offsets.last().unwrap() - offsets[0] + 1) as f64);
|
||||
(pl, width)
|
||||
}
|
||||
|
||||
pub fn conflicts_with(&self, other: &TurnGroup) -> bool {
|
||||
if self.id == other.id {
|
||||
return false;
|
||||
}
|
||||
if self.turn_type == TurnType::Crosswalk && other.turn_type == TurnType::Crosswalk {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.id.from == other.id.from {
|
||||
return false;
|
||||
}
|
||||
if self.id.to == other.id.to {
|
||||
return true;
|
||||
}
|
||||
self.geom.intersection(&other.geom).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
fn turn_group_geom(polylines: Vec<&PolyLine>, from: RoadID, to: RoadID) -> PolyLine {
|
||||
|
@ -135,7 +135,7 @@ impl IntersectionSimState {
|
||||
} else if let Some(ref signal) = map.maybe_get_traffic_signal(i) {
|
||||
let (_, phase, _) = signal.current_phase_and_remaining_time(now);
|
||||
for (req, _) in all {
|
||||
match phase.get_priority(req.turn) {
|
||||
match phase.get_priority_of_turn(req.turn, signal) {
|
||||
TurnPriority::Protected => {
|
||||
protected.push(req);
|
||||
}
|
||||
@ -344,7 +344,7 @@ impl State {
|
||||
let (_, phase, remaining_phase_time) = signal.current_phase_and_remaining_time(now);
|
||||
|
||||
// Can't go at all this phase.
|
||||
if phase.get_priority(new_req.turn) == TurnPriority::Banned {
|
||||
if phase.get_priority_of_turn(new_req.turn, signal) == TurnPriority::Banned {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user