diff --git a/game/src/debug/mod.rs b/game/src/debug/mod.rs index 0737c6d360..71acb79ec5 100644 --- a/game/src/debug/mod.rs +++ b/game/src/debug/mod.rs @@ -729,13 +729,12 @@ impl ContextualActions for Actions { } fn find_bad_signals(app: &App) { - println!("Bad traffic signals:"); + error!("Bad traffic signals:"); for i in app.primary.map.all_intersections() { if i.is_traffic_signal() { let first = &ControlTrafficSignal::get_possible_policies(&app.primary.map, i.id)[0].0; if first == "stage per road" || first == "arbitrary assignment" { - println!("- {}", i.id); - ControlTrafficSignal::brute_force(&app.primary.map, i.id); + error!("- {}", i.id); } } } diff --git a/geom/src/duration.rs b/geom/src/duration.rs index ae7e883dce..8de4da4794 100644 --- a/geom/src/duration.rs +++ b/geom/src/duration.rs @@ -222,6 +222,24 @@ impl Duration { } s } + + /// Returns the largest of the two inputs. + pub fn max(self, other: Duration) -> Duration { + if self >= other { + self + } else { + other + } + } + + /// Returns the smallest of the two inputs. + pub fn min(self, other: Duration) -> Duration { + if self <= other { + self + } else { + other + } + } } impl std::fmt::Display for Duration { diff --git a/map_model/src/make/traffic_signals/brute_force.rs b/map_model/src/make/traffic_signals/brute_force.rs deleted file mode 100644 index f63a55225d..0000000000 --- a/map_model/src/make/traffic_signals/brute_force.rs +++ /dev/null @@ -1,39 +0,0 @@ -use super::*; - -/// Temporary experiment to group all movements into the smallest number of stages. -pub fn make_traffic_signal(map: &Map, i: IntersectionID) { - let movements: Vec = Movement::for_i(i, map) - .unwrap() - .into_iter() - .filter_map(|(id, m)| if id.crosswalk { None } else { Some(m) }) - .collect(); - let indices: Vec = (0..movements.len()).collect(); - for num_stages in 1..=movements.len() { - println!( - "For {} turn movements, looking for solution with {} stages", - movements.len(), - num_stages - ); - for partition in helper(&indices, num_stages) { - if okay_partition(movements.iter().collect(), partition) { - return; - } - } - } - unreachable!() -} - -fn okay_partition(movements: Vec<&Movement>, partition: Partition) -> bool { - for stage in partition.0 { - let mut protected: Vec<&Movement> = Vec::new(); - for idx in stage { - let m = movements[idx]; - if protected.iter().any(|other| m.conflicts_with(other)) { - return false; - } - protected.push(m); - } - } - println!("found one that works! :O"); - true -} diff --git a/map_model/src/make/traffic_signals/lagging_green.rs b/map_model/src/make/traffic_signals/lagging_green.rs index 52b7aa56e2..478ab76f39 100644 --- a/map_model/src/make/traffic_signals/lagging_green.rs +++ b/map_model/src/make/traffic_signals/lagging_green.rs @@ -1,6 +1,5 @@ use super::*; -/// /// Create a traffic signal which has a stage that is: protected straight, protected right, /// unprotected left, unprotected right on red. Followed by a variable stage that has protected /// left, unprotected right on red. With a last stage that is all-walk and variable. @@ -57,15 +56,11 @@ fn optimize(mut ts: ControlTrafficSignal) -> Option { fn make_crosswalk_variable(ts: &mut ControlTrafficSignal) { const MIN_CROSSWALK_TIME: Duration = Duration::const_seconds(15.0); for mut s in ts.stages.iter_mut() { - if let Some(mut duration) = s.max_crosswalk_time(&ts.movements) { + if let Some(duration) = s.max_crosswalk_time(&ts.movements) { match s.stage_type { StageType::Fixed(_) => { - // get the minimum duration, but never less than 15 seconds - if duration < MIN_CROSSWALK_TIME { - duration = MIN_CROSSWALK_TIME; - } s.stage_type = StageType::Variable( - duration, + duration.max(MIN_CROSSWALK_TIME), Duration::const_seconds(1.0), Duration::const_seconds(1.0), ) @@ -157,20 +152,20 @@ fn multi_way_stages(map: &Map, id: IntersectionID) -> Option Option>); - -// Extremely hasty port of https://stackoverflow.com/a/30903689 -fn helper(items: &[usize], max_size: usize) -> Vec { - if items.len() < max_size || max_size == 0 { - return Vec::new(); - } - - if max_size == 1 { - return vec![Partition(vec![items.to_vec()])]; - } - - let mut results = Vec::new(); - let prev1 = helper(&items[0..items.len() - 1], max_size); - for i in 0..prev1.len() { - for j in 0..prev1[i].0.len() { - let mut partition: Vec> = Vec::new(); - for inner in &prev1[i].0 { - partition.push(inner.clone()); - } - partition[j].push(*items.last().unwrap()); - results.push(Partition(partition)); - } - } - - let set = vec![*items.last().unwrap()]; - for mut partition in helper(&items[0..items.len() - 1], max_size - 1) { - partition.0.push(set.clone()); - results.push(partition); - } - results -} - /// Simple second-pass after generating all signals. Find pairs of traffic signals very close to /// each other with 2 stages each, see if the primary movement of the first stages lead to each /// other, and flip the order of stages if not. This is often wrong when the most common movement is diff --git a/map_model/src/objects/traffic_signals.rs b/map_model/src/objects/traffic_signals.rs index 21583b9647..40ac3a373c 100644 --- a/map_model/src/objects/traffic_signals.rs +++ b/map_model/src/objects/traffic_signals.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use abstutil::{deserialize_btreemap, retain_btreeset, serialize_btreemap}; use geom::{Distance, Duration, Speed}; -use crate::make::traffic_signals::{brute_force, get_possible_policies}; +use crate::make::traffic_signals::get_possible_policies; use crate::raw::OriginalRoad; use crate::{ osm, CompressedMovementID, DirectedRoadID, Direction, IntersectionID, Map, Movement, @@ -89,10 +89,6 @@ impl ControlTrafficSignal { // signal config. get_possible_policies(map, id, false) } - // TODO tmp - pub fn brute_force(map: &Map, id: IntersectionID) { - brute_force::make_traffic_signal(map, id) - } pub fn get_min_crossing_time(&self, idx: usize) -> Duration { let mut max_distance = Distance::meters(0.0); diff --git a/sim/src/mechanics/intersection.rs b/sim/src/mechanics/intersection.rs index 685e3005fa..7cbace547d 100644 --- a/sim/src/mechanics/intersection.rs +++ b/sim/src/mechanics/intersection.rs @@ -839,16 +839,10 @@ impl IntersectionSimState { // it wrong, that's fine -- block the box a bit. let time_to_cross = turn.geom.length() / speed; if time_to_cross > remaining_stage_time { - // Actually, we might have bigger problems... - if time_to_cross > full_stage_duration { - self.events.push(Event::Alert( - AlertLocation::Intersection(req.turn.parent), - format!( - "{:?} is impossible to fit into stage duration of {}", - req, full_stage_duration - ), - )); - } else { + // Signals enforce a minimum crosswalk time, but some pedestrians are configured to + // walk very slowly. In that case, allow them to go anyway and wind up in the crosswalk + // during a red. This matches reality reasonably. + if time_to_cross <= full_stage_duration { return false; } }