diff --git a/game/src/ltn/mod.rs b/game/src/ltn/mod.rs index 9546081693..93c769a47a 100644 --- a/game/src/ltn/mod.rs +++ b/game/src/ltn/mod.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, BTreeSet}; use geom::{Circle, Distance, Line, Polygon}; use map_gui::tools::DrawRoadLabels; -use map_model::{IntersectionID, Map, Perimeter, RoadID}; +use map_model::{IntersectionID, Map, Perimeter, RoadID, RoutingParams, TurnID}; use widgetry::mapspace::ToggleZoomed; use widgetry::{Color, Drawable, EventCtx, GeomBatch}; @@ -41,6 +41,25 @@ pub struct ModalFilters { pub intersections: BTreeMap, } +impl ModalFilters { + /// Modify RoutingParams to respect these modal filters + pub fn update_routing_params(&self, params: &mut RoutingParams) { + params.avoid_roads.extend(self.roads.keys().cloned()); + for filter in self.intersections.values() { + params + .avoid_movements_between + .extend(filter.avoid_movements_between_roads()); + } + } + + pub fn allows_turn(&self, t: TurnID) -> bool { + if let Some(filter) = self.intersections.get(&t.parent) { + return filter.allows_turn(t.src.road, t.dst.road); + } + true + } +} + /// A diagonal filter exists in an intersection. It's defined by two roads (the order is /// arbitrary). When all of the intersection's roads are sorted in clockwise order, this pair of /// roads splits the ordering into two groups. Turns in each group are still possible, but not diff --git a/game/src/ltn/rat_runs.rs b/game/src/ltn/rat_runs.rs index 63dd277b5c..f3a0c3f8bb 100644 --- a/game/src/ltn/rat_runs.rs +++ b/game/src/ltn/rat_runs.rs @@ -38,16 +38,30 @@ pub fn find_rat_runs( } } + let mut params = map.routing_params().clone(); + modal_filters.update_routing_params(&mut params); + let cache_custom = true; let mut paths: Vec = timer .parallelize( "calculate paths between entrances and exits", requests, - |req| map.pathfind(req), + |req| map.pathfind_with_params(req, ¶ms, cache_custom), ) .into_iter() .flatten() .collect(); + // update_routing_params heavily penalizes crossing modal filters, but it doesn't prevent it + // completely! So strip out paths that were forced to cross a filter. + paths.retain(|path| { + !path.get_steps().iter().any(|step| match step { + PathStep::Lane(l) => modal_filters.roads.contains_key(&l.road), + PathStep::Turn(t) => !modal_filters.allows_turn(*t), + // Car paths don't make contraflow movements + _ => unreachable!(), + }) + }); + // Some paths wind up partly using perimeter roads (or even things outside the neighborhood // entirely). Sort by "worse" paths that spend more time inside. paths.sort_by_key(|path| { diff --git a/game/src/ltn/route.rs b/game/src/ltn/route.rs index 20fa87b245..b674e9ead4 100644 --- a/game/src/ltn/route.rs +++ b/game/src/ltn/route.rs @@ -87,14 +87,7 @@ impl RoutePlanner { // First the route respecting the filters let (total_time_after, total_dist_after) = { let mut params = map.routing_params().clone(); - params - .avoid_roads - .extend(app.session.modal_filters.roads.keys().cloned()); - for filter in app.session.modal_filters.intersections.values() { - params - .avoid_movements_between - .extend(filter.avoid_movements_between_roads()); - } + app.session.modal_filters.update_routing_params(&mut params); params.main_road_penalty = self.panel.spinner::("main road penalty").0; let cache_custom = true;