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:
Dustin Carlino 2019-11-22 18:42:44 -08:00
parent 715fc80e04
commit 1145bbc8e6
5 changed files with 293 additions and 332 deletions

View File

@ -13,8 +13,7 @@ use ezgui::{
}; };
use geom::Duration; use geom::Duration;
use map_model::{ use map_model::{
ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroupID, TurnID, TurnPriority, ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroupID, TurnPriority, TurnType,
TurnType,
}; };
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::time::Instant; use std::time::Instant;
@ -64,9 +63,7 @@ impl TrafficSignalEditor {
impl State for TrafficSignalEditor { impl State for TrafficSignalEditor {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition { fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
let mut signal = ui.primary.map.get_traffic_signal(self.diagram.i).clone(); let orig_signal = ui.primary.map.get_traffic_signal(self.diagram.i);
// TODO Temporary hack
let signal_copy = signal.clone();
self.menu.event(ctx); self.menu.event(ctx);
// TODO This really needs to be shown in the diagram! // TODO This really needs to be shown in the diagram!
@ -74,7 +71,7 @@ impl State for TrafficSignalEditor {
ctx, ctx,
Text::from(Line(format!( Text::from(Line(format!(
"Signal offset: {}", "Signal offset: {}",
signal.offset.minimal_tostring() orig_signal.offset.minimal_tostring()
))), ))),
); );
ctx.canvas.handle_event(ctx.input); ctx.canvas.handle_event(ctx.input);
@ -93,11 +90,12 @@ impl State for TrafficSignalEditor {
} }
if let Some(id) = self.group_selected { 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 // 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 => { 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) Some(TurnPriority::Protected)
} else if id.crosswalk.is_some() { } else if id.crosswalk.is_some() {
None None
@ -119,34 +117,40 @@ impl State for TrafficSignalEditor {
Key::Space, Key::Space,
format!( format!(
"toggle from {:?} to {:?}", "toggle from {:?} to {:?}",
phase.get_priority_group(id, &signal_copy), phase.get_priority_of_group(id),
pri pri
), ),
) { ) {
phase.edit_group(id, pri, &signal_copy, &ui.primary.map); phase.edit_group(
change_traffic_signal(signal, ui, ctx); &orig_signal.turn_groups[&id],
pri,
&orig_signal.turn_groups,
&ui.primary.map,
);
change_traffic_signal(new_signal, ui, ctx);
return Transition::Keep; return Transition::Keep;
} }
} }
} }
if self.menu.action("quit") { 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") { if self.menu.action("change phase duration") {
return Transition::Push(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") { } 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") { } else if self.menu.action("choose a preset signal") {
return Transition::Push(change_preset(self.diagram.i)); return Transition::Push(change_preset(self.diagram.i));
} else if self.menu.action("reset to original") { } else if self.menu.action("reset to original") {
signal = ControlTrafficSignal::get_possible_policies(&ui.primary.map, self.diagram.i) let new_signal =
ControlTrafficSignal::get_possible_policies(&ui.primary.map, self.diagram.i)
.remove(0) .remove(0)
.1; .1;
change_traffic_signal(signal, ui, ctx); change_traffic_signal(new_signal, ui, ctx);
self.diagram = TrafficSignalDiagram::new(self.diagram.i, 0, ui, ctx); self.diagram = TrafficSignalDiagram::new(self.diagram.i, 0, ui, ctx);
return Transition::Keep; return Transition::Keep;
} }
@ -161,19 +165,22 @@ impl State for TrafficSignalEditor {
let current_phase = self.diagram.current_phase(); let current_phase = self.diagram.current_phase();
if current_phase != 0 && self.menu.action("move current phase up") { if current_phase != 0 && self.menu.action("move current phase up") {
signal.phases.swap(current_phase, current_phase - 1); let mut new_signal = orig_signal.clone();
change_traffic_signal(signal, ui, ctx); 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); 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") && self.menu.action("move current phase down")
{ {
signal.phases.swap(current_phase, current_phase + 1); let mut new_signal = orig_signal.clone();
change_traffic_signal(signal, ui, ctx); 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); self.diagram = TrafficSignalDiagram::new(self.diagram.i, current_phase + 1, ui, ctx);
} else if signal.phases.len() > 1 && self.menu.action("delete current phase") { } else if orig_signal.phases.len() > 1 && self.menu.action("delete current phase") {
signal.phases.remove(current_phase); let mut new_signal = orig_signal.clone();
let num_phases = signal.phases.len(); new_signal.phases.remove(current_phase);
change_traffic_signal(signal, ui, ctx); let num_phases = new_signal.phases.len();
change_traffic_signal(new_signal, ui, ctx);
self.diagram = TrafficSignalDiagram::new( self.diagram = TrafficSignalDiagram::new(
self.diagram.i, self.diagram.i,
if current_phase == num_phases { if current_phase == num_phases {
@ -185,32 +192,29 @@ impl State for TrafficSignalEditor {
ctx, ctx,
); );
} else if self.menu.action("add a new empty phase") { } else if self.menu.action("add a new empty phase") {
let mut phase = Phase::new(); let mut new_signal = orig_signal.clone();
for t in ui.primary.map.get_turns_in_intersection(self.diagram.i) { new_signal.phases.insert(current_phase + 1, Phase::new());
if t.turn_type == TurnType::SharedSidewalkCorner { change_traffic_signal(new_signal, ui, ctx);
phase.protected_turns.insert(t.id);
}
}
signal.phases.insert(current_phase + 1, phase);
change_traffic_signal(signal, ui, ctx);
self.diagram = TrafficSignalDiagram::new(self.diagram.i, current_phase + 1, 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") { } else if has_sidewalks && self.menu.action("add a new pedestrian scramble phase") {
let mut phase = Phase::new(); let mut phase = Phase::new();
for t in ui.primary.map.get_turns_in_intersection(self.diagram.i) { for g in orig_signal.turn_groups.values() {
if t.between_sidewalks() { if g.turn_type == TurnType::Crosswalk {
phase.protected_turns.insert(t.id); phase.protected_groups.insert(g.id);
} }
} }
signal.phases.insert(current_phase + 1, phase); let mut new_signal = orig_signal.clone();
change_traffic_signal(signal, ui, ctx); 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); self.diagram = TrafficSignalDiagram::new(self.diagram.i, current_phase + 1, ui, ctx);
} else if has_sidewalks } else if has_sidewalks
&& self && self
.menu .menu
.action("convert to dedicated pedestrian scramble phase") .action("convert to dedicated pedestrian scramble phase")
{ {
signal.convert_to_ped_scramble(&ui.primary.map); let mut new_signal = orig_signal.clone();
change_traffic_signal(signal, ui, ctx); 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); self.diagram = TrafficSignalDiagram::new(self.diagram.i, 0, ui, ctx);
} }
@ -259,7 +263,7 @@ impl State for TrafficSignalEditor {
g.block.clone(), 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::Protected => ui.cs.get("turn protected by traffic signal"),
TurnPriority::Yield => ui TurnPriority::Yield => ui
.cs .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, mut signal: ControlTrafficSignal,
diagram: &mut TrafficSignalDiagram, diagram: &mut TrafficSignalDiagram,
ui: &mut UI, ui: &mut UI,
ctx: &mut EventCtx, ctx: &mut EventCtx,
) -> Transition { ) -> Transition {
let mut missing_turns: BTreeSet<TurnID> = ui let mut missing: BTreeSet<TurnGroupID> = signal.turn_groups.keys().cloned().collect();
.primary
.map
.get_i(signal.id)
.turns
.iter()
.cloned()
.collect();
for phase in &signal.phases { for phase in &signal.phases {
for t in &phase.protected_turns { for g in &phase.protected_groups {
missing_turns.remove(t); missing.remove(g);
} }
for t in &phase.yield_turns { for g in &phase.yield_groups {
missing_turns.remove(t); missing.remove(g);
} }
} }
if missing_turns.is_empty() { if missing.is_empty() {
let i = signal.id; 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); panic!("Edited traffic signal {} finalized with errors: {}", i, err);
} }
return Transition::Pop; return Transition::Pop;
} }
let num_missing = missing_turns.len(); let num_missing = missing.len();
let mut phase = Phase::new(); let mut phase = Phase::new();
phase.yield_turns = missing_turns; phase.yield_groups = missing;
for t in ui.primary.map.get_turns_in_intersection(signal.id) {
if t.turn_type == TurnType::SharedSidewalkCorner {
phase.protected_turns.insert(t.id);
}
}
signal.phases.push(phase); signal.phases.push(phase);
let last_phase = signal.phases.len() - 1; let last_phase = signal.phases.len() - 1;
change_traffic_signal(signal, ui, ctx); change_traffic_signal(signal, ui, ctx);

View File

@ -28,53 +28,62 @@ pub fn draw_signal_phase(
Color::rgba(255, 105, 180, 0.8), 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 { 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()); batch.append(crosswalk.clone());
} }
} }
if true { if true {
let (protected, yielding) = phase.active_turn_groups(ctx.map.get_traffic_signal(i)); for g in &phase.protected_groups {
for g in protected { if g.crosswalk.is_none() {
if g.id.crosswalk.is_none() {
batch.push( batch.push(
protected_color, 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 { for g in &phase.yield_groups {
if g.id.crosswalk.is_none() { if g.crosswalk.is_none() {
batch.extend( batch.extend(
yield_color, yield_color,
g.geom signal.turn_groups[g]
.geom
.make_arrow_outline(BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0) .make_arrow_outline(BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0)
.unwrap(), .unwrap(),
); );
} }
} }
} else { } else {
// For debugging, can still show individual turns: // For debugging, can still show individual turns
for t in &phase.protected_turns { for turn in ctx.map.get_turns_in_intersection(i) {
let turn = ctx.map.get_t(*t); if turn.between_sidewalks() {
if !turn.between_sidewalks() { continue;
}
match phase.get_priority_of_turn(turn.id, signal) {
TurnPriority::Protected => {
batch.push( batch.push(
protected_color, protected_color,
turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(), turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
); );
} }
} TurnPriority::Yield => {
for t in &phase.yield_turns {
let turn = ctx.map.get_t(*t);
if !turn.between_sidewalks() {
batch.extend( batch.extend(
yield_color, yield_color,
turn.geom turn.geom
.make_arrow_outline(BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0) .make_arrow_outline(
BIG_ARROW_THICKNESS * 2.0,
BIG_ARROW_THICKNESS / 2.0,
)
.unwrap(), .unwrap(),
); );
} }
TurnPriority::Banned => {}
}
} }
} }
@ -115,8 +124,9 @@ fn draw_signal_phase_with_icons(
batch: &mut GeomBatch, batch: &mut GeomBatch,
ctx: &DrawCtx, ctx: &DrawCtx,
) { ) {
let signal = ctx.map.get_traffic_signal(i);
for (id, crosswalk) in &ctx.draw_map.get_i(i).crosswalks { 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()); batch.append(crosswalk.clone());
} }
} }
@ -138,7 +148,7 @@ fn draw_signal_phase_with_icons(
continue; continue;
} }
match phase.get_priority(turn.id) { match phase.get_priority_of_turn(turn.id, signal) {
TurnPriority::Protected => { TurnPriority::Protected => {
green.push(turn.id); green.push(turn.id);
} }

View File

@ -1,7 +1,5 @@
use crate::{ use crate::{IntersectionID, Map, RoadID, TurnGroup, TurnGroupID, TurnID, TurnPriority, TurnType};
IntersectionID, Map, RoadID, Turn, TurnGroup, TurnGroupID, TurnID, TurnPriority, TurnType, use abstutil::{deserialize_btreemap, retain_btreeset, serialize_btreemap, Timer};
};
use abstutil::{deserialize_btreemap, serialize_btreemap, Timer};
use geom::Duration; use geom::Duration;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
@ -21,8 +19,8 @@ pub struct ControlTrafficSignal {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Phase { pub struct Phase {
pub protected_turns: BTreeSet<TurnID>, pub protected_groups: BTreeSet<TurnGroupID>,
pub yield_turns: BTreeSet<TurnID>, pub yield_groups: BTreeSet<TurnGroupID>,
pub duration: Duration, pub duration: Duration,
} }
@ -86,44 +84,45 @@ impl ControlTrafficSignal {
unreachable!() unreachable!()
} }
pub fn validate(self, map: &Map) -> Result<ControlTrafficSignal, String> { pub fn validate(self) -> Result<ControlTrafficSignal, String> {
// TODO Reuse assertions from edit_turn. // Does the assignment cover the correct set of groups?
let expected_groups: BTreeSet<TurnGroupID> = self.turn_groups.keys().cloned().collect();
// Does the assignment cover the correct set of turns? let mut actual_groups: BTreeSet<TurnGroupID> = BTreeSet::new();
let expected_turns: BTreeSet<TurnID> = map.get_i(self.id).turns.iter().cloned().collect();
let mut actual_turns: BTreeSet<TurnID> = BTreeSet::new();
for phase in &self.phases { for phase in &self.phases {
actual_turns.extend(phase.protected_turns.iter()); actual_groups.extend(phase.protected_groups.iter());
actual_turns.extend(phase.yield_turns.iter()); actual_groups.extend(phase.yield_groups.iter());
} }
if expected_turns != actual_turns { if expected_groups != actual_groups {
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>>()));
}
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) {
return Err(format!( return Err(format!(
"Traffic signal has conflicting priority turns in one phase:\n{:?}\n\n{:?}", "Traffic signal assignment for {} broken. Missing {:?}, contains irrelevant {:?}",
t1, t2 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 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 protected groups in one phase:\n{:?}\n\n{:?}",
g1, g2
)); ));
} }
} }
} }
// Do any of the crosswalks yield? Are all of the SharedSidewalkCorner prioritized? // Do any of the crosswalks yield?
for t in map.get_turns_in_intersection(self.id) { for g in phase.yield_groups.iter().map(|g| &self.turn_groups[g]) {
match t.turn_type { assert!(g.turn_type != TurnType::Crosswalk);
TurnType::Crosswalk => {
assert!(!phase.yield_turns.contains(&t.id));
}
TurnType::SharedSidewalkCorner => {
assert!(phase.protected_turns.contains(&t.id));
}
_ => {}
}
} }
} }
@ -131,49 +130,44 @@ impl ControlTrafficSignal {
} }
fn greedy_assignment(map: &Map, intersection: IntersectionID) -> ControlTrafficSignal { fn greedy_assignment(map: &Map, intersection: IntersectionID) -> ControlTrafficSignal {
if map.get_turns_in_intersection(intersection).is_empty() { let turn_groups = TurnGroup::for_i(intersection, map);
panic!("{} has no turns", intersection);
}
let mut phases = Vec::new(); let mut phases = Vec::new();
// Greedily partition turns into phases. More clever things later. No yields. // Greedily partition groups into phases that only have protected groups.
let mut remaining_turns: Vec<TurnID> = map let mut remaining_groups: Vec<TurnGroupID> = turn_groups.keys().cloned().collect();
.get_turns_in_intersection(intersection)
.iter()
.map(|t| t.id)
.collect();
let mut current_phase = Phase::new(); let mut current_phase = Phase::new();
loop { loop {
let add_turn = remaining_turns let add = remaining_groups
.iter() .iter()
.position(|&t| current_phase.could_be_protected_turn(t, map)); .position(|&g| current_phase.could_be_protected(g, &turn_groups));
match add_turn { match add {
Some(idx) => { Some(idx) => {
current_phase current_phase
.protected_turns .protected_groups
.insert(remaining_turns.remove(idx)); .insert(remaining_groups.remove(idx));
} }
None => { None => {
assert!(!current_phase.protected_groups.is_empty());
phases.push(current_phase); phases.push(current_phase);
current_phase = Phase::new(); current_phase = Phase::new();
if remaining_turns.is_empty() { if remaining_groups.is_empty() {
break; break;
} }
} }
} }
} }
expand_all_phases(&mut phases, map, intersection); expand_all_phases(&mut phases, &turn_groups);
let ts = ControlTrafficSignal { let ts = ControlTrafficSignal {
id: intersection, id: intersection,
phases, phases,
offset: Duration::ZERO, offset: Duration::ZERO,
turn_groups: TurnGroup::for_i(intersection, map), turn_groups,
}; };
// This must succeed // This must succeed
ts.validate(map).unwrap() ts.validate().unwrap()
} }
fn degenerate(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> { fn degenerate(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
@ -187,11 +181,7 @@ impl ControlTrafficSignal {
// TODO One-ways downtown should also have crosswalks. // TODO One-ways downtown should also have crosswalks.
let has_crosswalks = !map.get_r(r1).children_backwards.is_empty() let has_crosswalks = !map.get_r(r1).children_backwards.is_empty()
|| !map.get_r(r2).children_backwards.is_empty(); || !map.get_r(r2).children_backwards.is_empty();
let mut phases = vec![vec![ let mut phases = vec![vec![(vec![r1, r2], TurnType::Straight, PROTECTED)]];
(vec![r1, r2], TurnType::Straight, PROTECTED),
(vec![r1, r2], TurnType::LaneChangeLeft, YIELD),
(vec![r1, r2], TurnType::LaneChangeRight, YIELD),
]];
if has_crosswalks { if has_crosswalks {
phases.push(vec![(vec![r1, r2], TurnType::Crosswalk, PROTECTED)]); phases.push(vec![(vec![r1, r2], TurnType::Crosswalk, PROTECTED)]);
} }
@ -204,23 +194,20 @@ impl ControlTrafficSignal {
offset: Duration::ZERO, offset: Duration::ZERO,
turn_groups: TurnGroup::for_i(i, map), turn_groups: TurnGroup::for_i(i, map),
}; };
ts.validate(map).ok() ts.validate().ok()
} }
fn three_way(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> { fn three_way(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
if map.get_i(i).roads.len() != 3 { if map.get_i(i).roads.len() != 3 {
return None; return None;
} }
let turn_groups = TurnGroup::for_i(i, map);
// Picture a T intersection. Use turn angles to figure out the "main" two roads. // Picture a T intersection. Use turn angles to figure out the "main" two roads.
let straight_turn = map let straight = turn_groups
.get_turns_in_intersection(i) .values()
.into_iter() .find(|g| g.turn_type == TurnType::Straight)?;
.find(|t| t.turn_type == TurnType::Straight)?; let (north, south) = (straight.id.from, straight.id.to);
let (north, south) = (
map.get_l(straight_turn.id.src).parent,
map.get_l(straight_turn.id.dst).parent,
);
let mut roads = map.get_i(i).roads.clone(); let mut roads = map.get_i(i).roads.clone();
roads.remove(&north); roads.remove(&north);
roads.remove(&south); roads.remove(&south);
@ -233,8 +220,6 @@ impl ControlTrafficSignal {
vec![ vec![
vec![ vec![
(vec![north, south], TurnType::Straight, PROTECTED), (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::Right, YIELD),
(vec![north, south], TurnType::Left, YIELD), (vec![north, south], TurnType::Left, YIELD),
(vec![east], TurnType::Right, YIELD), (vec![east], TurnType::Right, YIELD),
@ -242,8 +227,6 @@ impl ControlTrafficSignal {
], ],
vec![ vec![
(vec![east], TurnType::Straight, PROTECTED), (vec![east], TurnType::Straight, PROTECTED),
(vec![east], TurnType::LaneChangeLeft, YIELD),
(vec![east], TurnType::LaneChangeRight, YIELD),
(vec![east], TurnType::Right, YIELD), (vec![east], TurnType::Right, YIELD),
(vec![east], TurnType::Left, YIELD), (vec![east], TurnType::Left, YIELD),
(vec![north, south], TurnType::Right, YIELD), (vec![north, south], TurnType::Right, YIELD),
@ -256,9 +239,9 @@ impl ControlTrafficSignal {
id: i, id: i,
phases, phases,
offset: Duration::ZERO, 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> { fn four_way_four_phase(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
@ -280,8 +263,6 @@ impl ControlTrafficSignal {
vec![ vec![
vec![ vec![
(vec![north, south], TurnType::Straight, PROTECTED), (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::Right, YIELD),
(vec![east, west], TurnType::Right, YIELD), (vec![east, west], TurnType::Right, YIELD),
(vec![east, west], TurnType::Crosswalk, PROTECTED), (vec![east, west], TurnType::Crosswalk, PROTECTED),
@ -289,8 +270,6 @@ impl ControlTrafficSignal {
vec![(vec![north, south], TurnType::Left, PROTECTED)], vec![(vec![north, south], TurnType::Left, PROTECTED)],
vec![ vec![
(vec![east, west], TurnType::Straight, PROTECTED), (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::Right, YIELD),
(vec![north, south], TurnType::Right, YIELD), (vec![north, south], TurnType::Right, YIELD),
(vec![north, south], TurnType::Crosswalk, PROTECTED), (vec![north, south], TurnType::Crosswalk, PROTECTED),
@ -305,7 +284,7 @@ impl ControlTrafficSignal {
offset: Duration::ZERO, offset: Duration::ZERO,
turn_groups: TurnGroup::for_i(i, map), 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> { fn four_way_two_phase(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
@ -326,8 +305,6 @@ impl ControlTrafficSignal {
vec![ vec![
vec![ vec![
(vec![north, south], TurnType::Straight, PROTECTED), (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::Right, YIELD),
(vec![north, south], TurnType::Left, YIELD), (vec![north, south], TurnType::Left, YIELD),
(vec![east, west], TurnType::Right, YIELD), (vec![east, west], TurnType::Right, YIELD),
@ -335,8 +312,6 @@ impl ControlTrafficSignal {
], ],
vec![ vec![
(vec![east, west], TurnType::Straight, PROTECTED), (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::Right, YIELD),
(vec![east, west], TurnType::Left, YIELD), (vec![east, west], TurnType::Left, YIELD),
(vec![north, south], TurnType::Right, YIELD), (vec![north, south], TurnType::Right, YIELD),
@ -351,7 +326,7 @@ impl ControlTrafficSignal {
offset: Duration::ZERO, offset: Duration::ZERO,
turn_groups: TurnGroup::for_i(i, map), turn_groups: TurnGroup::for_i(i, map),
}; };
ts.validate(map).ok() ts.validate().ok()
} }
fn four_oneways(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> { fn four_oneways(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
@ -378,8 +353,6 @@ impl ControlTrafficSignal {
vec![ vec![
vec![ vec![
(vec![r1], TurnType::Straight, PROTECTED), (vec![r1], TurnType::Straight, PROTECTED),
(vec![r1], TurnType::LaneChangeLeft, YIELD),
(vec![r1], TurnType::LaneChangeRight, YIELD),
(vec![r1], TurnType::Crosswalk, PROTECTED), (vec![r1], TurnType::Crosswalk, PROTECTED),
// TODO Technically, upgrade to protected if there's no opposing crosswalk -- // TODO Technically, upgrade to protected if there's no opposing crosswalk --
// even though it doesn't matter much. // even though it doesn't matter much.
@ -390,8 +363,6 @@ impl ControlTrafficSignal {
], ],
vec![ vec![
(vec![r2], TurnType::Straight, PROTECTED), (vec![r2], TurnType::Straight, PROTECTED),
(vec![r2], TurnType::LaneChangeLeft, YIELD),
(vec![r2], TurnType::LaneChangeRight, YIELD),
(vec![r2], TurnType::Crosswalk, PROTECTED), (vec![r2], TurnType::Crosswalk, PROTECTED),
// TODO Technically, upgrade to protected if there's no opposing crosswalk -- // TODO Technically, upgrade to protected if there's no opposing crosswalk --
// even though it doesn't matter much. // even though it doesn't matter much.
@ -408,24 +379,22 @@ impl ControlTrafficSignal {
offset: Duration::ZERO, offset: Duration::ZERO,
turn_groups: TurnGroup::for_i(i, map), turn_groups: TurnGroup::for_i(i, map),
}; };
ts.validate(map).ok() ts.validate().ok()
} }
fn all_walk_all_yield(map: &Map, i: IntersectionID) -> ControlTrafficSignal { 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_walk = Phase::new();
let mut all_yield = Phase::new(); let mut all_yield = Phase::new();
for turn in map.get_turns_in_intersection(i) { for group in turn_groups.values() {
match turn.turn_type { match group.turn_type {
TurnType::SharedSidewalkCorner => {
all_walk.protected_turns.insert(turn.id);
all_yield.protected_turns.insert(turn.id);
}
TurnType::Crosswalk => { 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, id: i,
phases: vec![all_walk, all_yield], phases: vec![all_walk, all_yield],
offset: Duration::ZERO, offset: Duration::ZERO,
turn_groups: TurnGroup::for_i(i, map), turn_groups,
}; };
// This must succeed // This must succeed
ts.validate(map).unwrap() ts.validate().unwrap()
} }
fn phase_per_road(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> { fn phase_per_road(map: &Map, i: IntersectionID) -> Option<ControlTrafficSignal> {
let turn_groups = TurnGroup::for_i(i, map);
let mut phases = Vec::new(); let mut phases = Vec::new();
let sorted_roads = map let sorted_roads = map
.get_i(i) .get_i(i)
@ -451,20 +422,17 @@ impl ControlTrafficSignal {
let adj2 = *abstutil::wraparound_get(&sorted_roads, (idx as isize) + 1); let adj2 = *abstutil::wraparound_get(&sorted_roads, (idx as isize) + 1);
let mut phase = Phase::new(); let mut phase = Phase::new();
for turn in map.get_turns_in_intersection(i) { for group in turn_groups.values() {
let parent = map.get_l(turn.id.src).parent; if group.turn_type == TurnType::Crosswalk {
if turn.turn_type == TurnType::SharedSidewalkCorner { if group.id.from == adj1 || group.id.from == adj2 {
phase.protected_turns.insert(turn.id); phase.protected_groups.insert(group.id);
} else if turn.turn_type == TurnType::Crosswalk {
if parent == adj1 || parent == adj2 {
phase.protected_turns.insert(turn.id);
} }
} else if parent == r { } else if group.id.from == r {
phase.yield_turns.insert(turn.id); phase.yield_groups.insert(group.id);
} }
} }
// Might have a one-way outgoing road. Skip it. // Might have a one-way outgoing road. Skip it.
if !phase.yield_turns.is_empty() { if !phase.yield_groups.is_empty() {
phases.push(phase); phases.push(phase);
} }
} }
@ -472,39 +440,38 @@ impl ControlTrafficSignal {
id: i, id: i,
phases, phases,
offset: Duration::ZERO, 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) { pub fn convert_to_ped_scramble(&mut self, map: &Map) {
// Remove Crosswalk turns from existing phases. // Remove Crosswalk groups from existing phases.
for phase in self.phases.iter_mut() { let mut replaced = std::mem::replace(&mut self.phases, Vec::new());
// Crosswalks are usually only protected_turns, but also clear out from yield_turns. for phase in replaced.iter_mut() {
for t in map.get_turns_in_intersection(self.id) { // Crosswalks are only in protected_groups.
if t.turn_type == TurnType::Crosswalk { retain_btreeset(&mut phase.protected_groups, |g| {
phase.protected_turns.remove(&t.id); self.turn_groups[g].turn_type != TurnType::Crosswalk
phase.yield_turns.remove(&t.id); });
}
}
// 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(); let mut promoted = Vec::new();
for t in &phase.yield_turns { for g in &phase.yield_groups {
if phase.could_be_protected_turn(*t, map) { if phase.could_be_protected(*g, &self.turn_groups) {
phase.protected_turns.insert(*t); phase.protected_groups.insert(*g);
promoted.push(*t); promoted.push(*g);
} }
} }
for t in promoted { for g in promoted {
phase.yield_turns.remove(&t); phase.yield_groups.remove(&g);
} }
} }
self.phases = replaced;
let mut phase = Phase::new(); let mut phase = Phase::new();
for t in map.get_turns_in_intersection(self.id) { for g in self.turn_groups.values() {
if t.between_sidewalks() { if g.turn_type == TurnType::Crosswalk {
phase.edit_turn(t, TurnPriority::Protected); phase.edit_group(g, TurnPriority::Protected, &self.turn_groups, map);
} }
} }
self.phases.push(phase); self.phases.push(phase);
@ -514,129 +481,83 @@ impl ControlTrafficSignal {
impl Phase { impl Phase {
pub fn new() -> Phase { pub fn new() -> Phase {
Phase { Phase {
protected_turns: BTreeSet::new(), protected_groups: BTreeSet::new(),
yield_turns: BTreeSet::new(), yield_groups: BTreeSet::new(),
duration: Duration::seconds(30.0), duration: Duration::seconds(30.0),
} }
} }
fn could_be_protected_turn(&self, t1: TurnID, map: &Map) -> bool { pub fn could_be_protected(
let turn1 = map.get_t(t1); &self,
for t2 in &self.protected_turns { g1: TurnGroupID,
if t1 == *t2 || turn1.conflicts_with(map.get_t(*t2)) { 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; return false;
} }
} }
true true
} }
pub fn get_priority(&self, t: TurnID) -> TurnPriority { pub fn get_priority_of_turn(&self, t: TurnID, parent: &ControlTrafficSignal) -> TurnPriority {
if self.protected_turns.contains(&t) { // 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 TurnPriority::Protected
} else if self.yield_turns.contains(&t) { } else if self.yield_groups.contains(&g) {
TurnPriority::Yield TurnPriority::Yield
} else { } else {
TurnPriority::Banned 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( pub fn edit_group(
&mut self, &mut self,
g: TurnGroupID, g: &TurnGroup,
pri: TurnPriority, pri: TurnPriority,
parent: &ControlTrafficSignal, turn_groups: &BTreeMap<TurnGroupID, TurnGroup>,
map: &Map, map: &Map,
) { ) {
for t in &parent.turn_groups[&g].members { let mut ids = vec![g.id];
self.edit_turn(map.get_t(*t), pri); 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. // Add all possible protected groups to existing phases.
fn expand_all_phases(phases: &mut Vec<Phase>, map: &Map, intersection: IntersectionID) { fn expand_all_phases(phases: &mut Vec<Phase>, turn_groups: &BTreeMap<TurnGroupID, TurnGroup>) {
let all_turns: Vec<TurnID> = map
.get_turns_in_intersection(intersection)
.iter()
.map(|t| t.id)
.collect();
for phase in phases.iter_mut() { for phase in phases.iter_mut() {
for t in &all_turns { for g in turn_groups.keys() {
if !phase.protected_turns.contains(t) && phase.could_be_protected_turn(*t, map) { if phase.could_be_protected(*g, turn_groups) {
phase.protected_turns.insert(*t); phase.protected_groups.insert(*g);
} }
} }
} }
@ -650,38 +571,34 @@ fn make_phases(
i: IntersectionID, i: IntersectionID,
phase_specs: Vec<Vec<(Vec<RoadID>, TurnType, bool)>>, phase_specs: Vec<Vec<(Vec<RoadID>, TurnType, bool)>>,
) -> Vec<Phase> { ) -> 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(); let mut phases: Vec<Phase> = Vec::new();
for specs in phase_specs { for specs in phase_specs {
let mut phase = Phase::new(); let mut phase = Phase::new();
let mut empty = true;
for (roads, turn_type, protected) in specs.into_iter() { for (roads, turn_type, protected) in specs.into_iter() {
for turn in map.get_turns_in_intersection(i) { for group in turn_groups.values() {
// These never conflict with anything. if !roads.contains(&group.id.from) || turn_type != group.turn_type {
if turn.turn_type == TurnType::SharedSidewalkCorner {
phase.protected_turns.insert(turn.id);
continue; continue;
} }
if !roads.contains(&map.get_l(turn.id.src).parent) || turn_type != turn.turn_type { phase.edit_group(
continue; group,
}
phase.edit_turn(
turn,
if protected { if protected {
TurnPriority::Protected TurnPriority::Protected
} else { } else {
TurnPriority::Yield TurnPriority::Yield
}, },
&turn_groups,
map,
); );
empty = false;
} }
} }
// Filter out empty phases if they happen. // Filter out empty phases if they happen.
if empty { if phase.protected_groups.is_empty() && phase.yield_groups.is_empty() {
continue; continue;
} }

View File

@ -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)] #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, PartialOrd)]
pub enum TurnPriority { pub enum TurnPriority {
// For stop signs: Can't currently specify this! // For stop signs: Can't currently specify this!
@ -128,6 +130,7 @@ pub struct TurnGroupID {
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct TurnGroup { pub struct TurnGroup {
pub id: TurnGroupID, pub id: TurnGroupID,
pub turn_type: TurnType,
pub members: Vec<TurnID>, pub members: Vec<TurnID>,
// The "overall" path of movement, aka, an "average" of the turn geometry // The "overall" path of movement, aka, an "average" of the turn geometry
pub geom: PolyLine, pub geom: PolyLine,
@ -153,6 +156,7 @@ impl TurnGroup {
id, id,
TurnGroup { TurnGroup {
id, id,
turn_type: TurnType::Crosswalk,
members: vec![turn.id], members: vec![turn.id],
geom: turn.geom.clone(), geom: turn.geom.clone(),
angle: turn.angle(), angle: turn.angle(),
@ -170,6 +174,23 @@ impl TurnGroup {
from, from,
to, 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 members: Vec<TurnID> = members.into_iter().collect();
let id = TurnGroupID { let id = TurnGroupID {
from, from,
@ -180,12 +201,16 @@ impl TurnGroup {
id, id,
TurnGroup { TurnGroup {
id, id,
turn_type: *turn_types.iter().next().unwrap(),
angle: map.get_t(members[0]).angle(), angle: map.get_t(members[0]).angle(),
members, members,
geom, geom,
}, },
); );
} }
if results.is_empty() {
panic!("{} has no TurnGroups!", i);
}
results results
} }
@ -229,6 +254,23 @@ impl TurnGroup {
let width = LANE_THICKNESS * ((*offsets.last().unwrap() - offsets[0] + 1) as f64); let width = LANE_THICKNESS * ((*offsets.last().unwrap() - offsets[0] + 1) as f64);
(pl, width) (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 { fn turn_group_geom(polylines: Vec<&PolyLine>, from: RoadID, to: RoadID) -> PolyLine {

View File

@ -135,7 +135,7 @@ impl IntersectionSimState {
} else if let Some(ref signal) = map.maybe_get_traffic_signal(i) { } else if let Some(ref signal) = map.maybe_get_traffic_signal(i) {
let (_, phase, _) = signal.current_phase_and_remaining_time(now); let (_, phase, _) = signal.current_phase_and_remaining_time(now);
for (req, _) in all { for (req, _) in all {
match phase.get_priority(req.turn) { match phase.get_priority_of_turn(req.turn, signal) {
TurnPriority::Protected => { TurnPriority::Protected => {
protected.push(req); protected.push(req);
} }
@ -344,7 +344,7 @@ impl State {
let (_, phase, remaining_phase_time) = signal.current_phase_and_remaining_time(now); let (_, phase, remaining_phase_time) = signal.current_phase_and_remaining_time(now);
// Can't go at all this phase. // 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; return false;
} }