mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-28 08:53:26 +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),
|
Toggle::choice(ctx, "draw cells", "areas", "streets", Key::D, true),
|
||||||
]),
|
]),
|
||||||
Text::new().into_widget(ctx).named("warnings"),
|
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);
|
.build(ctx);
|
||||||
@ -60,7 +66,7 @@ impl Viewer {
|
|||||||
.neighborhood
|
.neighborhood
|
||||||
.cells
|
.cells
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|c| c.borders.is_empty() && !c.car_free)
|
.filter(|c| c.is_disconnected())
|
||||||
.count();
|
.count();
|
||||||
// TODO Also add a red outline to them or something
|
// TODO Also add a red outline to them or something
|
||||||
let warning = if disconnected_cells == 0 {
|
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 {
|
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||||
match self.panel.event(ctx) {
|
match self.panel.event(ctx) {
|
||||||
Outcome::Clicked(x) => {
|
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
|
return Tab::Connectivity
|
||||||
.handle_action::<Viewer>(ctx, app, x.as_ref())
|
.handle_action::<Viewer>(ctx, app, x.as_ref())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -13,6 +13,7 @@ use crate::app::App;
|
|||||||
pub use browse::BrowseNeighborhoods;
|
pub use browse::BrowseNeighborhoods;
|
||||||
pub use partition::Partitioning;
|
pub use partition::Partitioning;
|
||||||
|
|
||||||
|
mod auto;
|
||||||
mod browse;
|
mod browse;
|
||||||
mod connectivity;
|
mod connectivity;
|
||||||
mod draw_cells;
|
mod draw_cells;
|
||||||
@ -94,6 +95,14 @@ pub struct Cell {
|
|||||||
pub car_free: bool,
|
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.
|
/// An interval along a road's length, with start < end.
|
||||||
pub struct DistanceInterval {
|
pub struct DistanceInterval {
|
||||||
pub start: Distance,
|
pub start: Distance,
|
||||||
|
Loading…
Reference in New Issue
Block a user