From 0f5ab2f410b35d0b04c9204d471749d53ce76243 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Tue, 3 Mar 2020 17:20:56 -0800 Subject: [PATCH] change turn group representation to prepare for cross-map edits --- data/MANIFEST.txt | 60 ++++++++++++------------ data/input/screenshots/montlake/MANIFEST | 34 +++++++------- game/src/edit/traffic_signals.rs | 23 ++++----- game/src/render/traffic_signal.rs | 17 ++++--- map_model/src/intersection.rs | 1 + map_model/src/traffic_signals.rs | 36 +++++--------- map_model/src/turn.rs | 42 +++++++++-------- 7 files changed, 104 insertions(+), 109 deletions(-) diff --git a/data/MANIFEST.txt b/data/MANIFEST.txt index bcfc719fe5..23d7a4d187 100644 --- a/data/MANIFEST.txt +++ b/data/MANIFEST.txt @@ -103,26 +103,26 @@ d7e630552af64d49ff58c92a55e09356 data/input/raw_maps/23rd.bin 5d48031ff3b288ecb22bd2a96809a0ab data/input/polygons/ballard.poly 0304847f270859fcfc19c8ca122ed793 data/input/polygons/23rd.poly 54f5f16e5ec925cb29b4d1405a89ef94 data/input/polygons/downtown.poly -ecfe0b11fe0641a2c092a42ffd488ef6 data/input/screenshots/montlake/02x05_i124.png -37321854140b6b4d19d29e0dcc7b4ed3 data/input/screenshots/montlake/01x06_i26.png -e105cbb1ee418bfbe469bd8e9afd6da8 data/input/screenshots/montlake/01x01_i19.png -64ab71bd2defd98dc82874d7dd3df937 data/input/screenshots/montlake/MANIFEST -39a82b3b5162e86c6bebeb6a7b92dc38 data/input/screenshots/montlake/03x06_i27.png -54fb9ae739cd4e30a71f478c688987e2 data/input/screenshots/montlake/02x03_i1.png -82a93b7ac0e086358944b7b148f0dd2e data/input/screenshots/montlake/02x04_i25.png +e510730c57bcc1ed6671cc95ea257585 data/input/screenshots/montlake/02x05_i124.png +681c82d0767ea71a55ce183c14e816c0 data/input/screenshots/montlake/01x06_i26.png +1717bfb53aa375bd3359de6e7db860ff data/input/screenshots/montlake/01x01_i19.png +63a920add8f7d4da115ea56e52677d9b data/input/screenshots/montlake/MANIFEST +0ece3a6bc65266c96c10ecb11f6738f9 data/input/screenshots/montlake/03x06_i27.png +19394b3b1c0208244cfdcac805414325 data/input/screenshots/montlake/02x03_i1.png +612d5c710d77ae2fcd639fc8c1716dac data/input/screenshots/montlake/02x04_i25.png 6154d0d36f9c6ef7bb492f6f3d040a87 data/input/screenshots/montlake/02x02_i289.png -9fd973825f6c4a31081bd116d5f758e8 data/input/screenshots/montlake/03x02_i8.png -7cb13279a40e88500bf56a27649a6ac9 data/input/screenshots/montlake/01x04_i31.png +88423d807e30c66547d7d0bf7dffd788 data/input/screenshots/montlake/03x02_i8.png +920eaf9c480d89e68234b399fecce33a data/input/screenshots/montlake/01x04_i31.png dc10dd940c4f3b41086f7b40983ba25f data/input/screenshots/montlake/combine.sh -3f22b63ebc3736c1ac08da2a128fcba5 data/input/screenshots/montlake/03x01_i0.png -b4d83fc0ee525b9961c210ba0d945c31 data/input/screenshots/montlake/01x03_i4.png -568880dceb44433f02fa038d337ea491 data/input/screenshots/montlake/01x02_i20.png -8061eddd26929189861c6dffbdb92f06 data/input/screenshots/montlake/03x04_i112.png -44557d69a3e330ac11087de204385c1c data/input/screenshots/montlake/03x03_i59.png -df80ffb7a65491cd2e674facce0ab565 data/input/screenshots/montlake/02x01_i24.png -f1f167f53f21d6ba4406e51a9e97b262 data/input/screenshots/montlake/03x05_i2.png -4125185f97bcf0b6c2e9a3392db45023 data/input/screenshots/montlake/02x06_i85.png -2e4d121b40cc023d1ccc0222a6e5dd39 data/input/screenshots/montlake/01x05_i40.png +d6db692ef068337e5758de7be568f885 data/input/screenshots/montlake/03x01_i0.png +3ab7c23b54d9bfeac190fc3d5f0c75b1 data/input/screenshots/montlake/01x03_i4.png +924811cfa5520f1fd3da1ef06b1a5c0c data/input/screenshots/montlake/01x02_i20.png +b1929464274d34c3bce5040821f7869d data/input/screenshots/montlake/03x04_i112.png +94acbdf117cd18491f8360754089da2b data/input/screenshots/montlake/03x03_i59.png +8b5c174350141b6411fa2f0dcb802b34 data/input/screenshots/montlake/02x01_i24.png +0cfba587f63ae66e59dc6961e4aab07b data/input/screenshots/montlake/03x05_i2.png +c9958a511d116460d48ff0a4093a7623 data/input/screenshots/montlake/02x06_i85.png +6c894ae6c793e96056493524254ea28d data/input/screenshots/montlake/01x05_i40.png 129b460f56f5eb41cab3bfd70fb5fde9 data/input/sidewalks.bin 5a7e5b1d68b6e4d1637731d1e1c54ccb data/input/google_transit_2018_18_08/stop_times.txt 3fce7b5925679b421e5fb85966dbb5cd data/input/google_transit_2018_18_08/routes.txt @@ -204,13 +204,13 @@ a5e849fa8883569519976ebfef3ae269 data/system/night_colors.json 517a360397bbb5a7573c2558e78fa4ea data/system/fonts/DejaVuSans.ttf e07df86cef2e721115583d61d1fb68a6 data/system/fonts/Roboto-Bold.ttf 11eabca2251325cfc5589c9c6fb57b46 data/system/fonts/Roboto-Regular.ttf -543ccb8d97828ad8080f8e25c399dcaf data/system/maps/huge_seattle.bin -80fc7213d29f9425c80d1e2f22113aa6 data/system/maps/ballard.bin -15c7082403bee91a2431811f00bde464 data/system/maps/downtown.bin -9e6e6d2d1b7aa17b8a13cd074f4aeaf4 data/system/maps/caphill.bin -e1f6f6c288395293378b65f7cd995d1b data/system/maps/montlake.bin -d5f488eeea910f2c43c6829e98df4430 data/system/maps/intl_district.bin -0b11365a2af8ebdda5a5d4a3f4d20daf data/system/maps/23rd.bin +bc26d5fd443d669cfd0456a8a0968b1a data/system/maps/huge_seattle.bin +2fddde4d3516302952844cdb54a99377 data/system/maps/ballard.bin +6954ccbec2c8ccaef6f97795b6970437 data/system/maps/downtown.bin +6e4f5d242991f28d88e6b9b48dd926c1 data/system/maps/caphill.bin +b0317f387d3c6f5e6459a920bd7d8db9 data/system/maps/montlake.bin +94883e1fac1efa4d87672468e58b157a data/system/maps/intl_district.bin +4293cd436373faecc82bf1ae652bcb58 data/system/maps/23rd.bin d71169fc1789707c403e70e6f2cc27d4 data/system/synthetic_maps/signal_double.json 407cf814c6b5c73b5e1857529b94a204 data/system/synthetic_maps/signal_single.json 7e3b93d87b5c6cf4788122e7f65ecac1 data/system/synthetic_maps/signal_fan_in.json @@ -221,9 +221,9 @@ f9f0a109966db097617a13122ecaa6f3 data/system/scenarios/downtown/weekday.bin dd54763cfda7fce4c401ae122c6daf57 data/system/scenarios/huge_seattle/weekday.bin 47dea892a1c4331da02494d15ce02a02 data/system/scenarios/caphill/weekday.bin 7d6a8e8f5d716ce8674ed63d27f2da51 data/system/scenarios/montlake/weekday.bin -b50d41d25ba40504df5a56945769bb27 data/system/prebaked_results/23rd/weekday.bin -c83401e693ea9bb2c437ecdcc33fadbf data/system/prebaked_results/signal_single/tutorial lvl1.bin -e8cdc329b7c10914df88ea3d4df6abd0 data/system/prebaked_results/signal_single/tutorial lvl2.bin +5f78d73efbabf06aed28c0e80c6b6d5b data/system/prebaked_results/23rd/weekday.bin +39dc5028982cc7ef40da44ec00ab1020 data/system/prebaked_results/signal_single/tutorial lvl1.bin +c4f5661e5c62c0c0ccccc7c2b9f2d365 data/system/prebaked_results/signal_single/tutorial lvl2.bin b8137879ec00add4cd2a39e8c985420f data/system/prebaked_results/montlake/car vs bike contention.bin -029227cdea45492954e3b1666b0dc5b9 data/system/prebaked_results/montlake/weekday.bin -994677ade10b0d021f3a0eb91696265c data/system/prebaked_results/montlake/car vs bus contention.bin +3cb2adbb509617b15007b92b373a8b97 data/system/prebaked_results/montlake/weekday.bin +7f9aec66506cc24bcf7b2e74dbcf38f4 data/system/prebaked_results/montlake/car vs bus contention.bin diff --git a/data/input/screenshots/montlake/MANIFEST b/data/input/screenshots/montlake/MANIFEST index 5a90c9ec1c..f7647684e7 100644 --- a/data/input/screenshots/montlake/MANIFEST +++ b/data/input/screenshots/montlake/MANIFEST @@ -1,18 +1,18 @@ -e105cbb1ee418bfbe469bd8e9afd6da8 ../data/input/screenshots/pending_montlake/01x01_i19.png -df80ffb7a65491cd2e674facce0ab565 ../data/input/screenshots/pending_montlake/02x01_i24.png -3f22b63ebc3736c1ac08da2a128fcba5 ../data/input/screenshots/pending_montlake/03x01_i0.png -568880dceb44433f02fa038d337ea491 ../data/input/screenshots/pending_montlake/01x02_i20.png +1717bfb53aa375bd3359de6e7db860ff ../data/input/screenshots/pending_montlake/01x01_i19.png +8b5c174350141b6411fa2f0dcb802b34 ../data/input/screenshots/pending_montlake/02x01_i24.png +d6db692ef068337e5758de7be568f885 ../data/input/screenshots/pending_montlake/03x01_i0.png +924811cfa5520f1fd3da1ef06b1a5c0c ../data/input/screenshots/pending_montlake/01x02_i20.png 6154d0d36f9c6ef7bb492f6f3d040a87 ../data/input/screenshots/pending_montlake/02x02_i289.png -9fd973825f6c4a31081bd116d5f758e8 ../data/input/screenshots/pending_montlake/03x02_i8.png -b4d83fc0ee525b9961c210ba0d945c31 ../data/input/screenshots/pending_montlake/01x03_i4.png -54fb9ae739cd4e30a71f478c688987e2 ../data/input/screenshots/pending_montlake/02x03_i1.png -44557d69a3e330ac11087de204385c1c ../data/input/screenshots/pending_montlake/03x03_i59.png -7cb13279a40e88500bf56a27649a6ac9 ../data/input/screenshots/pending_montlake/01x04_i31.png -82a93b7ac0e086358944b7b148f0dd2e ../data/input/screenshots/pending_montlake/02x04_i25.png -8061eddd26929189861c6dffbdb92f06 ../data/input/screenshots/pending_montlake/03x04_i112.png -2e4d121b40cc023d1ccc0222a6e5dd39 ../data/input/screenshots/pending_montlake/01x05_i40.png -ecfe0b11fe0641a2c092a42ffd488ef6 ../data/input/screenshots/pending_montlake/02x05_i124.png -f1f167f53f21d6ba4406e51a9e97b262 ../data/input/screenshots/pending_montlake/03x05_i2.png -37321854140b6b4d19d29e0dcc7b4ed3 ../data/input/screenshots/pending_montlake/01x06_i26.png -4125185f97bcf0b6c2e9a3392db45023 ../data/input/screenshots/pending_montlake/02x06_i85.png -39a82b3b5162e86c6bebeb6a7b92dc38 ../data/input/screenshots/pending_montlake/03x06_i27.png +88423d807e30c66547d7d0bf7dffd788 ../data/input/screenshots/pending_montlake/03x02_i8.png +3ab7c23b54d9bfeac190fc3d5f0c75b1 ../data/input/screenshots/pending_montlake/01x03_i4.png +19394b3b1c0208244cfdcac805414325 ../data/input/screenshots/pending_montlake/02x03_i1.png +94acbdf117cd18491f8360754089da2b ../data/input/screenshots/pending_montlake/03x03_i59.png +920eaf9c480d89e68234b399fecce33a ../data/input/screenshots/pending_montlake/01x04_i31.png +612d5c710d77ae2fcd639fc8c1716dac ../data/input/screenshots/pending_montlake/02x04_i25.png +b1929464274d34c3bce5040821f7869d ../data/input/screenshots/pending_montlake/03x04_i112.png +6c894ae6c793e96056493524254ea28d ../data/input/screenshots/pending_montlake/01x05_i40.png +e510730c57bcc1ed6671cc95ea257585 ../data/input/screenshots/pending_montlake/02x05_i124.png +0cfba587f63ae66e59dc6961e4aab07b ../data/input/screenshots/pending_montlake/03x05_i2.png +681c82d0767ea71a55ce183c14e816c0 ../data/input/screenshots/pending_montlake/01x06_i26.png +c9958a511d116460d48ff0a4093a7623 ../data/input/screenshots/pending_montlake/02x06_i85.png +0ece3a6bc65266c96c10ecb11f6738f9 ../data/input/screenshots/pending_montlake/03x06_i27.png diff --git a/game/src/edit/traffic_signals.rs b/game/src/edit/traffic_signals.rs index d1735298f7..cc0ef7b7a5 100644 --- a/game/src/edit/traffic_signals.rs +++ b/game/src/edit/traffic_signals.rs @@ -131,7 +131,7 @@ impl State for TrafficSignalEditor { TurnPriority::Banned => { if phase.could_be_protected(id, &orig_signal.turn_groups) { Some(TurnPriority::Protected) - } else if id.crosswalk.is_some() { + } else if id.crosswalk { None } else { Some(TurnPriority::Yield) @@ -139,7 +139,7 @@ impl State for TrafficSignalEditor { } TurnPriority::Yield => Some(TurnPriority::Banned), TurnPriority::Protected => { - if id.crosswalk.is_some() { + if id.crosswalk { Some(TurnPriority::Banned) } else { Some(TurnPriority::Yield) @@ -155,12 +155,7 @@ impl State for TrafficSignalEditor { pri ), ) { - phase.edit_group( - &orig_signal.turn_groups[&id], - pri, - &orig_signal.turn_groups, - &app.primary.map, - ); + phase.edit_group(&orig_signal.turn_groups[&id], pri); self.command_stack.push(orig_signal.clone()); self.redo_stack.clear(); self.top_panel = make_top_panel(true, false, ctx); @@ -275,16 +270,16 @@ impl State for TrafficSignalEditor { self.composite.draw(g); self.top_panel.draw(g); if let Some(id) = self.group_selected { - let osd = if id.crosswalk.is_some() { + let osd = if id.crosswalk { Text::from(Line(format!( "Crosswalk across {}", - app.primary.map.get_r(id.from).get_name() + app.primary.map.get_r(id.from.id).get_name() ))) } else { Text::from(Line(format!( "Turn from {} to {}", - app.primary.map.get_r(id.from).get_name(), - app.primary.map.get_r(id.to).get_name() + app.primary.map.get_r(id.from.id).get_name(), + app.primary.map.get_r(id.to.id).get_name() ))) }; CommonState::draw_custom_osd(g, app, osd); @@ -536,7 +531,7 @@ fn edit_entire_signal(app: &App, i: IntersectionID, suspended_sim: Sim) -> Box().unwrap(); let orig_signal = app.primary.map.get_traffic_signal(editor.i); let mut new_signal = orig_signal.clone(); - if new_signal.convert_to_ped_scramble(&app.primary.map) { + if new_signal.convert_to_ped_scramble() { editor.command_stack.push(orig_signal.clone()); editor.redo_stack.clear(); editor.top_panel = make_top_panel(true, false, ctx); @@ -750,7 +745,7 @@ fn check_for_missing_groups( let num_missing = missing.len(); let mut phase = Phase::new(); for g in missing { - if g.crosswalk.is_some() { + if g.crosswalk { phase.protected_groups.insert(g); } else { phase.yield_groups.insert(g); diff --git a/game/src/render/traffic_signal.rs b/game/src/render/traffic_signal.rs index 4125c3e165..fc368a5e07 100644 --- a/game/src/render/traffic_signal.rs +++ b/game/src/render/traffic_signal.rs @@ -31,7 +31,7 @@ pub fn draw_signal_phase( match signal_style { TrafficSignalStyle::GroupArrows => { for g in &phase.yield_groups { - assert!(g.crosswalk.is_none()); + assert!(!g.crosswalk); batch.push( yield_bg_color, signal.turn_groups[g] @@ -49,12 +49,12 @@ pub fn draw_signal_phase( } let mut dont_walk = BTreeSet::new(); for g in signal.turn_groups.keys() { - if g.crosswalk.is_some() { + if g.crosswalk { dont_walk.insert(g); } } for g in &phase.protected_groups { - if g.crosswalk.is_none() { + if !g.crosswalk { batch.push( protected_color, signal.turn_groups[g] @@ -87,7 +87,7 @@ pub fn draw_signal_phase( } TrafficSignalStyle::Sidewalks => { for g in &phase.yield_groups { - assert!(g.crosswalk.is_none()); + assert!(!g.crosswalk); batch.push( yield_bg_color, signal.turn_groups[g] @@ -104,8 +104,13 @@ pub fn draw_signal_phase( ); } for g in &phase.protected_groups { - if let Some(t) = g.crosswalk { - make_crosswalk(batch, app.primary.map.get_t(t), &app.primary.map, &app.cs); + if g.crosswalk { + make_crosswalk( + batch, + app.primary.map.get_t(signal.turn_groups[g].members[0]), + &app.primary.map, + &app.cs, + ); } else { batch.push( protected_color, diff --git a/map_model/src/intersection.rs b/map_model/src/intersection.rs index 6392d1f5f5..aa7cfcc222 100644 --- a/map_model/src/intersection.rs +++ b/map_model/src/intersection.rs @@ -38,6 +38,7 @@ pub struct Intersection { pub incoming_lanes: Vec, pub outgoing_lanes: Vec, + // TODO Maybe DirectedRoadIDs pub roads: BTreeSet, } diff --git a/map_model/src/traffic_signals.rs b/map_model/src/traffic_signals.rs index a38c86f742..7cf34ad4d2 100644 --- a/map_model/src/traffic_signals.rs +++ b/map_model/src/traffic_signals.rs @@ -213,7 +213,7 @@ impl ControlTrafficSignal { let straight = turn_groups .values() .find(|g| g.turn_type == TurnType::Straight)?; - let (north, south) = (straight.id.from, straight.id.to); + let (north, south) = (straight.id.from.id, straight.id.to.id); let mut roads = map.get_i(i).roads.clone(); roads.remove(&north); roads.remove(&south); @@ -430,10 +430,10 @@ impl ControlTrafficSignal { let mut phase = Phase::new(); for group in turn_groups.values() { if group.turn_type == TurnType::Crosswalk { - if group.id.from == adj1 || group.id.from == adj2 { + if group.id.from.id == adj1 || group.id.from.id == adj2 { phase.protected_groups.insert(group.id); } - } else if group.id.from == r { + } else if group.id.from.id == r { phase.yield_groups.insert(group.id); } } @@ -452,13 +452,13 @@ impl ControlTrafficSignal { } // Returns true if this did anything - pub fn convert_to_ped_scramble(&mut self, map: &Map) -> bool { + pub fn convert_to_ped_scramble(&mut self) -> bool { let orig = self.clone(); let mut all_walk_phase = Phase::new(); for g in self.turn_groups.values() { if g.turn_type == TurnType::Crosswalk { - all_walk_phase.edit_group(g, TurnPriority::Protected, &self.turn_groups, map); + all_walk_phase.edit_group(g, TurnPriority::Protected); } } @@ -541,23 +541,15 @@ impl Phase { } } - pub fn edit_group( - &mut self, - g: &TurnGroup, - pri: TurnPriority, - turn_groups: &BTreeMap, - map: &Map, - ) { + pub fn edit_group(&mut self, g: &TurnGroup, pri: TurnPriority) { let mut ids = vec![g.id]; if g.turn_type == TurnType::Crosswalk { - for t in &map.get_t(g.id.crosswalk.unwrap()).other_crosswalk_ids { - ids.push( - *turn_groups - .keys() - .find(|id| id.crosswalk == Some(*t)) - .unwrap(), - ); - } + ids.push(TurnGroupID { + from: g.id.to, + to: g.id.from, + parent: g.id.parent, + crosswalk: true, + }); } for id in ids { self.protected_groups.remove(&id); @@ -599,7 +591,7 @@ fn make_phases( for (roads, turn_type, protected) in specs.into_iter() { for group in turn_groups.values() { - if !roads.contains(&group.id.from) || turn_type != group.turn_type { + if !roads.contains(&group.id.from.id) || turn_type != group.turn_type { continue; } @@ -610,8 +602,6 @@ fn make_phases( } else { TurnPriority::Yield }, - &turn_groups, - map, ); } } diff --git a/map_model/src/turn.rs b/map_model/src/turn.rs index 574018401c..9c7869679d 100644 --- a/map_model/src/turn.rs +++ b/map_model/src/turn.rs @@ -1,4 +1,4 @@ -use crate::{IntersectionID, LaneID, Map, RoadID}; +use crate::{DirectedRoadID, IntersectionID, LaneID, Map}; use abstutil::MultiMap; use geom::{Angle, Distance, PolyLine, Pt2D}; use serde_derive::{Deserialize, Serialize}; @@ -114,13 +114,14 @@ impl Turn { } } +// One road usually has 4 crosswalks, each a singleton TurnGroup. We need all of the information +// here to keep each crosswalk separate. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct TurnGroupID { - pub from: RoadID, - pub to: RoadID, - // If this is true, there's only one member. There are separate TurnGroups for each side of a - // crosswalk! The TurnID is embedded here so the two crosswalks have different IDs. - pub crosswalk: Option, + pub from: DirectedRoadID, + pub to: DirectedRoadID, + pub parent: IntersectionID, + pub crosswalk: bool, } // TODO Unclear how this plays with different lane types @@ -138,17 +139,18 @@ pub struct TurnGroup { impl TurnGroup { pub(crate) fn for_i(i: IntersectionID, map: &Map) -> BTreeMap { let mut results = BTreeMap::new(); - let mut groups: MultiMap<(RoadID, RoadID), TurnID> = MultiMap::new(); + let mut groups: MultiMap<(DirectedRoadID, DirectedRoadID), TurnID> = MultiMap::new(); for turn in map.get_turns_in_intersection(i) { - let from = map.get_l(turn.id.src).parent; - let to = map.get_l(turn.id.dst).parent; + let from = map.get_l(turn.id.src).get_directed_parent(map); + let to = map.get_l(turn.id.dst).get_directed_parent(map); match turn.turn_type { TurnType::SharedSidewalkCorner => {} TurnType::Crosswalk => { let id = TurnGroupID { from, to, - crosswalk: Some(turn.id), + parent: i, + crosswalk: true, }; results.insert( id, @@ -193,7 +195,8 @@ impl TurnGroup { let id = TurnGroupID { from, to, - crosswalk: None, + parent: i, + crosswalk: false, }; results.insert( id, @@ -214,8 +217,8 @@ impl TurnGroup { // Polyline points FROM intersection pub fn src_center_and_width(&self, map: &Map) -> (PolyLine, Distance) { - let r = map.get_r(self.id.from); - let dir = r.dir_and_offset(self.members[0].src).0; + let r = map.get_r(self.id.from.id); + let dir = self.id.from.forwards; // Points towards the intersection let pl = if dir { r.get_current_center(map) @@ -242,11 +245,8 @@ impl TurnGroup { let pl = pl.shift_right((leftmost + rightmost) / 2.0).unwrap(); // Flip direction, so we point away from the intersection - let pl = if self - .id - .crosswalk - .map(|t| map.get_l(t.src).src_i == t.parent) - .unwrap_or(false) + let pl = if self.id.crosswalk + && map.get_l(self.members[0].src).src_i == self.members[0].parent { pl } else { @@ -276,7 +276,11 @@ impl TurnGroup { } } -fn turn_group_geom(polylines: Vec<&PolyLine>, from: RoadID, to: RoadID) -> PolyLine { +fn turn_group_geom( + polylines: Vec<&PolyLine>, + from: DirectedRoadID, + to: DirectedRoadID, +) -> PolyLine { let num_pts = polylines[0].points().len(); for pl in &polylines { if num_pts != pl.points().len() {