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 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 =
|
||||||
.remove(0)
|
ControlTrafficSignal::get_possible_policies(&ui.primary.map, self.diagram.i)
|
||||||
.1;
|
.remove(0)
|
||||||
change_traffic_signal(signal, ui, ctx);
|
.1;
|
||||||
|
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);
|
||||||
|
@ -28,52 +28,61 @@ 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;
|
||||||
batch.push(
|
|
||||||
protected_color,
|
|
||||||
turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
match phase.get_priority_of_turn(turn.id, signal) {
|
||||||
for t in &phase.yield_turns {
|
TurnPriority::Protected => {
|
||||||
let turn = ctx.map.get_t(*t);
|
batch.push(
|
||||||
if !turn.between_sidewalks() {
|
protected_color,
|
||||||
batch.extend(
|
turn.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
|
||||||
yield_color,
|
);
|
||||||
turn.geom
|
}
|
||||||
.make_arrow_outline(BIG_ARROW_THICKNESS * 2.0, BIG_ARROW_THICKNESS / 2.0)
|
TurnPriority::Yield => {
|
||||||
.unwrap(),
|
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,
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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>>()));
|
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 {
|
for phase in &self.phases {
|
||||||
// Do any of the priority turns in one phase conflict?
|
// Do any of the priority groups in one phase conflict?
|
||||||
for t1 in phase.protected_turns.iter().map(|t| map.get_t(*t)) {
|
for g1 in phase.protected_groups.iter().map(|g| &self.turn_groups[g]) {
|
||||||
for t2 in phase.protected_turns.iter().map(|t| map.get_t(*t)) {
|
for g2 in phase.protected_groups.iter().map(|g| &self.turn_groups[g]) {
|
||||||
if t1.conflicts_with(t2) {
|
if g1.conflicts_with(g2) {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Traffic signal has conflicting priority turns in one phase:\n{:?}\n\n{:?}",
|
"Traffic signal has conflicting protected groups in one phase:\n{:?}\n\n{:?}",
|
||||||
t1, t2
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user