From f0c0d88764c119f70f2cca09e6c9763729d72856 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Thu, 9 Apr 2020 12:28:11 -0700 Subject: [PATCH] the new table makes the population map's weird filter irrelevant. also add sorting by delta time --- game/src/layer/mod.rs | 1 - game/src/layer/population.rs | 68 ++++------------------- game/src/sandbox/trip_results.rs | 95 +++++++++++++++++++++----------- 3 files changed, 72 insertions(+), 92 deletions(-) diff --git a/game/src/layer/mod.rs b/game/src/layer/mod.rs index 5f5345b8db..11a380f4e6 100644 --- a/game/src/layer/mod.rs +++ b/game/src/layer/mod.rs @@ -414,7 +414,6 @@ impl Layers { app, population::Options { heatmap: Some(HeatmapOptions::new()), - with_finished_trip_blocked_pct: None, }, ); Some(Transition::Pop) diff --git a/game/src/layer/population.rs b/game/src/layer/population.rs index f7241c5b2f..e35b740458 100644 --- a/game/src/layer/population.rs +++ b/game/src/layer/population.rs @@ -4,7 +4,7 @@ use crate::layer::Layers; use abstutil::prettyprint_usize; use ezgui::{ hotkey, Btn, Checkbox, Color, Composite, EventCtx, GeomBatch, HorizontalAlignment, Key, Line, - Spinner, TextExt, VerticalAlignment, Widget, + VerticalAlignment, Widget, }; use geom::{Circle, Distance, Pt2D}; use sim::{GetDrawAgents, PersonState}; @@ -13,28 +13,11 @@ use std::collections::HashSet; // TODO Disable drawing unzoomed agents... or alternatively, implement this by asking Sim to // return this kind of data instead! pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers { - let filter = |p| { - if let Some(pct) = opts.with_finished_trip_blocked_pct { - // TODO This is probably inefficient... - app.primary.sim.get_person(p).trips.iter().any(|t| { - if let Some((total, blocked)) = app.primary.sim.finished_trip_time(*t) { - (100.0 * blocked / total) as usize >= pct - } else { - false - } - }) - } else { - true - } - }; - let mut pts = Vec::new(); // Faster to grab all agent positions than individually map trips to agent positions. for a in app.primary.sim.get_unzoomed_agents(&app.primary.map) { - if let Some(p) = a.person { - if filter(p) { - pts.push(a.pos); - } + if a.person.is_some() { + pts.push(a.pos); } } @@ -48,14 +31,12 @@ pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers { // Already covered above PersonState::Trip(_) => {} PersonState::Inside(b) => { - if filter(person.id) { - let pt = app.primary.map.get_b(b).polygon.center(); - if seen_bldgs.contains(&b) { - repeat_pts.push(pt); - } else { - seen_bldgs.insert(b); - pts.push(pt); - } + let pt = app.primary.map.get_b(b).polygon.center(); + if seen_bldgs.contains(&b) { + repeat_pts.push(pt); + } else { + seen_bldgs.insert(b); + pts.push(pt); } } PersonState::OffMap | PersonState::Limbo => {} @@ -87,8 +68,6 @@ pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers { pub struct Options { // If None, just a dot map pub heatmap: Option, - // TODO More filters... Find people with finished/future trips of any/some mode - pub with_finished_trip_blocked_pct: Option, } fn make_controls( @@ -118,21 +97,6 @@ fn make_controls( ]) .centered(), ]; - col.push(Checkbox::text( - ctx, - "Filter by people with a finished trip", - None, - opts.with_finished_trip_blocked_pct.is_some(), - )); - if let Some(pct) = opts.with_finished_trip_blocked_pct { - col.push(Widget::row(vec![ - "% of time spent waiting".draw_text(ctx).margin(5), - Spinner::new(ctx, (0, 100), pct) - .named("blocked ratio") - .align_right() - .centered_vert(), - ])); - } col.push(Checkbox::text( ctx, @@ -155,17 +119,5 @@ pub fn options(c: &mut Composite) -> Options { } else { None }; - Options { - heatmap, - with_finished_trip_blocked_pct: if c.is_checked("Filter by people with a finished trip") { - if c.has_widget("blocked ratio") { - Some(c.spinner("blocked ratio")) - } else { - // Just changed, use default - Some(0) - } - } else { - None - }, - } + Options { heatmap } } diff --git a/game/src/sandbox/trip_results.rs b/game/src/sandbox/trip_results.rs index b0dab83c4c..3aba6599c7 100644 --- a/game/src/sandbox/trip_results.rs +++ b/game/src/sandbox/trip_results.rs @@ -4,8 +4,9 @@ use crate::helpers::cmp_duration_shorter; use crate::info::Tab; use crate::sandbox::SandboxMode; use ezgui::{hotkey, Btn, Composite, EventCtx, GfxCtx, Key, Line, Outcome, Text, Widget}; +use geom::{Duration, Time}; use maplit::btreeset; -use sim::TripID; +use sim::{TripID, TripMode}; // TODO Hover over a trip to preview its route on the map @@ -18,6 +19,7 @@ pub struct TripResults { enum SortBy { Departure, Duration, + RelativeDuration, PercentWaiting, } @@ -42,6 +44,9 @@ impl State for TripResults { "Duration" => { self.composite = make(ctx, app, SortBy::Duration); } + "Comparison with baseline" => { + self.composite = make(ctx, app, SortBy::RelativeDuration); + } "Percent of trip spent waiting" => { self.composite = make(ctx, app, SortBy::PercentWaiting); } @@ -71,6 +76,16 @@ impl State for TripResults { } } +struct Entry { + trip: TripID, + mode: TripMode, + departure: Time, + duration: Duration, + baseline_duration: Duration, + waiting: Duration, + percent_waiting: usize, +} + fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite { let mut data = Vec::new(); let sim = &app.primary.sim; @@ -80,29 +95,30 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite { } else { continue; }; - let (_, blocked) = sim.finished_trip_time(*id).unwrap(); - let (start_time, _, _, _) = sim.trip_info(*id); - let comparison = if app.has_prebaked().is_some() { - cmp_duration_shorter(*duration, app.prebaked().finished_trip_time(*id).unwrap()) + let (_, waiting) = sim.finished_trip_time(*id).unwrap(); + let (departure, _, _, _) = sim.trip_info(*id); + let baseline_duration = if app.has_prebaked().is_some() { + app.prebaked().finished_trip_time(*id).unwrap() } else { - vec![Line("n/a")] + Duration::ZERO }; - data.push(( - *id, + data.push(Entry { + trip: *id, mode, - start_time, - *duration, - comparison, - blocked, - (100.0 * blocked / *duration) as usize, - )); + departure, + duration: *duration, + baseline_duration, + waiting, + percent_waiting: (100.0 * waiting / *duration) as usize, + }); } match sort { - SortBy::Departure => data.sort_by_key(|(_, _, t, _, _, _, _)| *t), - SortBy::Duration => data.sort_by_key(|(_, _, _, dt, _, _, _)| *dt), - SortBy::PercentWaiting => data.sort_by_key(|(_, _, _, _, _, _, pct)| *pct), + SortBy::Departure => data.sort_by_key(|x| x.departure), + SortBy::Duration => data.sort_by_key(|x| x.duration), + SortBy::RelativeDuration => data.sort_by_key(|x| x.duration - x.baseline_duration), + SortBy::PercentWaiting => data.sort_by_key(|x| x.percent_waiting), } // Descending... data.reverse(); @@ -114,20 +130,20 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite { let mut col2 = Text::new(); let mut col3 = Text::new(); let mut col4 = Text::new(); - let mut col5 = Text::new(); + let mut maybe_col5 = Text::new(); let mut col6 = Text::new(); let mut col7 = Text::new(); - for (id, mode, departure, duration, comparison, blocked, pct_blocked) in - data.into_iter().take(30) - { - col1.push(Btn::plaintext(id.0.to_string()).build_def(ctx, None)); - col2.add(Line(mode.ongoing_verb())); - col3.add(Line(departure.ampm_tostring())); - col4.add(Line(duration.to_string())); - col5.add_appended(comparison); - col6.add(Line(blocked.to_string())); - col7.add(Line(format!("{}%", pct_blocked))); + for x in data.into_iter().take(30) { + col1.push(Btn::plaintext(x.trip.0.to_string()).build_def(ctx, None)); + col2.add(Line(x.mode.ongoing_verb())); + col3.add(Line(x.departure.ampm_tostring())); + col4.add(Line(x.duration.to_string())); + if app.has_prebaked().is_some() { + maybe_col5.add_appended(cmp_duration_shorter(x.duration, x.baseline_duration)); + } + col6.add(Line(x.waiting.to_string())); + col7.add(Line(format!("{}%", x.percent_waiting))); } Composite::new( @@ -144,21 +160,30 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite { Line("Trip ID").draw(ctx).margin_right(10), Line("Type").draw(ctx).margin_right(10), if sort == SortBy::Departure { - Btn::text_fg("Departure").inactive(ctx) + Btn::text_bg2("Departure").inactive(ctx) } else { Btn::text_fg("Departure").build_def(ctx, None) } .margin_right(10), if sort == SortBy::Duration { - Btn::text_fg("Duration").inactive(ctx) + Btn::text_bg2("Duration").inactive(ctx) } else { Btn::text_fg("Duration").build_def(ctx, None) } .margin_right(10), - Line("Comparison with baseline").draw(ctx).margin_right(10), + if app.has_prebaked().is_some() { + if sort == SortBy::RelativeDuration { + Btn::text_bg2("Comparison with baseline").inactive(ctx) + } else { + Btn::text_fg("Comparison with baseline").build_def(ctx, None) + } + .margin_right(10) + } else { + Widget::nothing() + }, Line("Time spent waiting").draw(ctx).margin_right(10), if sort == SortBy::PercentWaiting { - Btn::text_fg("Percent of trip spent waiting").inactive(ctx) + Btn::text_bg2("Percent of trip spent waiting").inactive(ctx) } else { Btn::text_fg("Percent of trip spent waiting").build_def(ctx, None) } @@ -169,7 +194,11 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite { col2.draw(ctx).margin_right(10), col3.draw(ctx).margin_right(10), col4.draw(ctx).margin_right(10), - col5.draw(ctx).margin_right(10), + if app.has_prebaked().is_some() { + maybe_col5.draw(ctx).margin_right(10) + } else { + Widget::nothing() + }, col6.draw(ctx).margin_right(10), col7.draw(ctx).margin_right(10), ])