mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-26 07:52:05 +03:00
First tentative experiments in automatic filter placement -- the greedy
approach
This commit is contained in:
parent
27452f0e08
commit
65899f3a79
66
game/src/ltn/auto.rs
Normal file
66
game/src/ltn/auto.rs
Normal file
@ -0,0 +1,66 @@
|
||||
//! Experiments to make a neighborhood be low-traffic by automatically placing filters to prevent all rat runs.
|
||||
|
||||
use abstutil::Timer;
|
||||
use widgetry::EventCtx;
|
||||
|
||||
use super::rat_runs::find_rat_runs;
|
||||
use super::Neighborhood;
|
||||
use crate::app::App;
|
||||
|
||||
/// Find the road with the most rat-runs that can be closed without creating a disconnected cell,
|
||||
/// and filter it. There's a vague intuition or observation that the "bottleneck" will have the
|
||||
/// most rat-runs going through it, so tackle the worst problem first.
|
||||
pub fn greedy_heuristic(
|
||||
ctx: &EventCtx,
|
||||
app: &mut App,
|
||||
neighborhood: &Neighborhood,
|
||||
timer: &mut Timer,
|
||||
) {
|
||||
if neighborhood
|
||||
.cells
|
||||
.iter()
|
||||
.filter(|c| c.is_disconnected())
|
||||
.count()
|
||||
!= 0
|
||||
{
|
||||
warn!("Not applying the greedy heuristic to a neighborhood; it already has a disconnected cell");
|
||||
return;
|
||||
}
|
||||
|
||||
let rat_runs = find_rat_runs(
|
||||
&app.primary.map,
|
||||
&neighborhood,
|
||||
&app.session.modal_filters,
|
||||
timer,
|
||||
);
|
||||
// TODO How should we break ties? Some rat-runs are worse than others; use that weight?
|
||||
// TODO Should this operation be per cell instead? We could hover on a road belonging to that
|
||||
// cell to select it
|
||||
if let Some((r, _)) = rat_runs
|
||||
.count_per_road
|
||||
.borrow()
|
||||
.iter()
|
||||
.max_by_key(|pair| pair.1)
|
||||
{
|
||||
let road = app.primary.map.get_r(*r);
|
||||
app.session
|
||||
.modal_filters
|
||||
.roads
|
||||
.insert(road.id, road.length() / 2.0);
|
||||
let new_neighborhood = Neighborhood::new(ctx, app, neighborhood.orig_perimeter.clone());
|
||||
if new_neighborhood
|
||||
.cells
|
||||
.iter()
|
||||
.filter(|c| c.is_disconnected())
|
||||
.count()
|
||||
!= 0
|
||||
{
|
||||
warn!("Filtering {} disconnects a cell, never mind", road.id);
|
||||
app.session.modal_filters.roads.remove(&road.id).unwrap();
|
||||
// TODO Try the next choice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO The brute force approach: try to filter every possible road, find the one with the least
|
||||
// rat-runs by the end
|
@ -36,6 +36,12 @@ impl Viewer {
|
||||
Toggle::choice(ctx, "draw cells", "areas", "streets", Key::D, true),
|
||||
]),
|
||||
Text::new().into_widget(ctx).named("warnings"),
|
||||
ctx.style()
|
||||
.btn_outline
|
||||
.text("Automatically stop rat-runs")
|
||||
.hotkey(Key::A)
|
||||
.tooltip("Warning: uses experimental heuristics to place filters")
|
||||
.build_def(ctx),
|
||||
]),
|
||||
)
|
||||
.build(ctx);
|
||||
@ -60,7 +66,7 @@ impl Viewer {
|
||||
.neighborhood
|
||||
.cells
|
||||
.iter()
|
||||
.filter(|c| c.borders.is_empty() && !c.car_free)
|
||||
.filter(|c| c.is_disconnected())
|
||||
.count();
|
||||
// TODO Also add a red outline to them or something
|
||||
let warning = if disconnected_cells == 0 {
|
||||
@ -77,6 +83,16 @@ impl State<App> for Viewer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => {
|
||||
if x == "Automatically stop rat-runs" {
|
||||
ctx.loading_screen("automatically filter a neighborhood", |ctx, timer| {
|
||||
crate::ltn::auto::greedy_heuristic(ctx, app, &self.neighborhood, timer);
|
||||
});
|
||||
self.neighborhood =
|
||||
Neighborhood::new(ctx, app, self.neighborhood.orig_perimeter.clone());
|
||||
self.neighborhood_changed(ctx, app);
|
||||
return Transition::Keep;
|
||||
}
|
||||
|
||||
return Tab::Connectivity
|
||||
.handle_action::<Viewer>(ctx, app, x.as_ref())
|
||||
.unwrap();
|
||||
|
@ -13,6 +13,7 @@ use crate::app::App;
|
||||
pub use browse::BrowseNeighborhoods;
|
||||
pub use partition::Partitioning;
|
||||
|
||||
mod auto;
|
||||
mod browse;
|
||||
mod connectivity;
|
||||
mod draw_cells;
|
||||
@ -94,6 +95,14 @@ pub struct Cell {
|
||||
pub car_free: bool,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
/// A cell is disconnected if it's not connected to a perimeter road. (The exception is cells
|
||||
/// containing roads that by their OSM classification already ban cars.)
|
||||
pub fn is_disconnected(&self) -> bool {
|
||||
self.borders.is_empty() && !self.car_free
|
||||
}
|
||||
}
|
||||
|
||||
/// An interval along a road's length, with start < end.
|
||||
pub struct DistanceInterval {
|
||||
pub start: Distance,
|
||||
|
Loading…
Reference in New Issue
Block a user