Recalculate LTN impact when something changes; don't make the user figure it out

This commit is contained in:
Dustin Carlino 2022-01-20 14:07:19 +00:00
parent ba02ead72b
commit fc2817670a
3 changed files with 59 additions and 22 deletions

View File

@ -122,11 +122,7 @@ impl State<App> for BrowseNeighborhoods {
}
});
}
"Calculate" => {
return Transition::Push(super::impact::ShowResults::new_state(ctx, app));
}
"Force recalculation" => {
app.session.impact = None;
"Calculate" | "Show impact" => {
return Transition::Push(super::impact::ShowResults::new_state(ctx, app));
}
_ => unreachable!(),
@ -259,6 +255,33 @@ pub enum Style {
fn impact_widget(ctx: &EventCtx, app: &App) -> Widget {
let map_name = app.map.get_name();
if let Some(ref results) = app.session.impact {
if &results.map == map_name {
if results.change_key == app.session.modal_filters.change_key {
// Nothing to calculate!
return ctx
.style()
.btn_solid_primary
.text("Show impact")
.build_def(ctx);
}
// We'll need to do some pathfinding
return Widget::col(vec![
Text::from_multiline(vec![
Line("Predicting impact of your proposal may take a moment."),
Line("The application may freeze up during that time."),
])
.into_widget(ctx),
ctx.style()
.btn_solid_primary
.text("Calculate")
.build_def(ctx),
]);
}
}
// Starting from scratch
let scenario_name = Scenario::default_scenario_for_map(&map_name);
if scenario_name == "home_to_work" {
return "This city doesn't have travel demand model data available".text_widget(ctx);
@ -274,16 +297,9 @@ fn impact_widget(ctx: &EventCtx, app: &App) -> Widget {
Line(format!("We need to load a {} file", size)),
])
.into_widget(ctx),
Widget::row(vec![
ctx.style()
.btn_solid_primary
.text("Calculate")
.build_def(ctx),
// TODO Bad UI! Detect edits and do this. I'm being lazy.
ctx.style()
.btn_solid_primary
.text("Force recalculation")
.build_def(ctx),
]),
ctx.style()
.btn_solid_primary
.text("Calculate")
.build_def(ctx),
])
}

View File

@ -17,6 +17,8 @@ pub struct ModalFilters {
/// Edit history is preserved recursively
pub previous_version: Box<Option<ModalFilters>>,
/// This changes every time an edit occurs
pub change_key: usize,
}
/// A diagonal filter exists in an intersection. It's defined by two roads (the order is
@ -41,6 +43,7 @@ impl ModalFilters {
pub fn before_edit(&mut self) {
let copy = self.clone();
self.previous_version = Box::new(Some(copy));
self.change_key += 1;
}
/// If it's possible no edits were made, undo the previous call to `before_edit` and collapse
@ -49,6 +52,7 @@ impl ModalFilters {
if let Some(prev) = self.previous_version.take() {
if self.roads == prev.roads && self.intersections == prev.intersections {
self.previous_version = prev.previous_version;
// Leave change_key alone for simplicity
} else {
// There was a real difference, keep
self.previous_version = Box::new(Some(prev));

View File

@ -10,7 +10,7 @@ use widgetry::{
TextExt, VerticalAlignment, Widget,
};
use crate::{App, NeighborhoodID, Transition};
use crate::{App, BrowseNeighborhoods, NeighborhoodID, Transition};
// TODO Configurable main road penalty, like in the pathfinding tool
// TODO Don't allow crossing filters at all -- don't just disincentivize
@ -18,9 +18,13 @@ use crate::{App, NeighborhoodID, Transition};
// ... can't we just produce data of a certain shape, and have a UI pretty tuned for that?
pub struct Results {
map: MapName,
pub map: MapName,
// This changes per map
all_driving_trips: Vec<PathRequest>,
// The rest need updating when this changes
pub change_key: usize,
before_world: World<Obj>,
before_road_counts: Counter<RoadID>,
before_intersection_counts: Counter<IntersectionID>,
@ -63,6 +67,8 @@ impl Results {
map: app.map.get_name().clone(),
all_driving_trips,
change_key: 0,
before_world: World::unbounded(),
before_road_counts: Counter::new(),
before_intersection_counts: Counter::new(),
@ -76,6 +82,7 @@ impl Results {
}
fn recalculate_impact(&mut self, ctx: &mut EventCtx, app: &App, timer: &mut Timer) {
self.change_key = app.session.modal_filters.change_key;
let map = &app.map;
// Before the filters
@ -297,9 +304,8 @@ pub struct ShowResults {
}
impl ShowResults {
pub fn new_state(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
pub fn new_state(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
let map_name = app.map.get_name().clone();
// TODO Handle changes in the filters / partitioning too
if app
.session
.impact
@ -320,6 +326,15 @@ impl ShowResults {
);
}
if app.session.impact.as_ref().unwrap().change_key != app.session.modal_filters.change_key {
ctx.loading_screen("recalculate impact", |ctx, timer| {
// Avoid a double borrow
let mut results = app.session.impact.take().unwrap();
results.recalculate_impact(ctx, app, timer);
app.session.impact = Some(results);
});
}
let layer = Layer::Relative;
let panel = Panel::new_builder(Widget::col(vec![
map_gui::tools::app_header(ctx, app, "Low traffic neighborhoods"),
@ -374,9 +389,11 @@ impl ShowResults {
}
impl SimpleState<App> for ShowResults {
fn on_click(&mut self, _: &mut EventCtx, _: &mut App, x: &str, _: &Panel) -> Transition {
fn on_click(&mut self, ctx: &mut EventCtx, app: &mut App, x: &str, _: &Panel) -> Transition {
if x == "close" {
return Transition::Pop;
// Don't just Pop; if we updated the results, the UI won't warn the user about a slow
// loading
return Transition::Replace(BrowseNeighborhoods::new_state(ctx, app));
}
unreachable!()
}