see modified trips in the table and info panels

This commit is contained in:
Dustin Carlino 2020-07-14 16:10:50 -07:00
parent e691545b63
commit 614354271a
15 changed files with 166 additions and 45 deletions

View File

@ -794,6 +794,13 @@ impl Composite {
pub fn is_checked(&self, name: &str) -> bool {
self.find::<Checkbox>(name).enabled
}
pub fn maybe_is_checked(&self, name: &str) -> Option<bool> {
if self.has_widget(name) {
Some(self.find::<Checkbox>(name).enabled)
} else {
None
}
}
pub fn text_box(&self, name: &str) -> String {
self.find::<TextBox>(name).get_line()

View File

@ -522,6 +522,8 @@ pub struct PerMap {
pub zorder_range: (isize, isize),
// If we ever left edit mode and resumed without restarting from midnight, this is true.
pub dirty_from_edits: bool,
// Any ScenarioModifiers in effect?
pub has_modified_trips: bool,
}
impl PerMap {
@ -553,6 +555,7 @@ impl PerMap {
zorder_range: (low_z, high_z),
show_zorder: high_z,
dirty_from_edits: false,
has_modified_trips: false,
}
}

View File

@ -103,7 +103,7 @@ pub fn people(ctx: &mut EventCtx, app: &App, details: &mut Details, id: Building
for t in &person.trips {
match app.primary.sim.trip_to_agent(*t) {
TripResult::TripNotStarted => {
let (start_time, _, _, mode) = app.primary.sim.trip_info(*t);
let (start_time, _, _, mode, _) = app.primary.sim.trip_info(*t);
next_trip = Some((start_time, mode));
break;
}

View File

@ -721,20 +721,17 @@ impl DataOptions {
}
pub fn from_controls(c: &Composite) -> DataOptions {
let show_before =
c.has_widget("Show before changes") && c.is_checked("Show before changes");
let show_before = c.maybe_is_checked("Show before changes").unwrap_or(false);
let mut disabled_modes = BTreeSet::new();
for m in TripMode::all() {
let label = m.noun();
if c.has_widget(label) && !c.is_checked(label) {
if !c.maybe_is_checked(label).unwrap_or(true) {
disabled_modes.insert(m);
}
}
DataOptions {
show_before,
show_end_of_day: show_before
&& c.has_widget("Show full day")
&& c.is_checked("Show full day"),
show_end_of_day: show_before && c.maybe_is_checked("Show full day").unwrap_or(false),
disabled_modes,
}
}

View File

@ -117,12 +117,15 @@ pub fn trips(
}
TripResult::TripDoesntExist => unreachable!(),
};
let (_, _, _, trip_mode) = sim.trip_info(*t);
let (_, _, _, trip_mode, modified) = sim.trip_info(*t);
// TODO Style wrong. Button should be the entire row.
rows.push(
Widget::custom_row(vec![
format!("Trip {} ", idx + 1).draw_text(ctx).margin_right(21),
format!("Trip {} ", idx + 1)
.draw_text(ctx)
.centered_vert()
.margin_right(21),
Widget::row(vec![
Widget::draw_svg_transform(
ctx,
@ -134,20 +137,31 @@ pub fn trips(
},
RewriteColor::ChangeAll(color),
),
Line(trip_status).small().fg(color).draw(ctx),
Line(trip_status)
.small()
.fg(color)
.draw(ctx)
.centered_vert(),
])
.fully_rounded()
.outline(1.0, color)
.bg(color.alpha(0.2))
.padding(5)
.padding(10)
.margin_right(21),
if modified {
Line("modified").draw(ctx).centered_vert().margin_right(15)
} else {
Widget::nothing()
},
if trip_status == "finished" {
if let Some(before) = app
.has_prebaked()
.and_then(|_| app.prebaked().finished_trip_time(*t))
{
let (after, _) = app.primary.sim.finished_trip_time(*t).unwrap();
Text::from(cmp_duration_shorter(after, before)).draw(ctx)
Text::from(cmp_duration_shorter(after, before))
.draw(ctx)
.centered_vert()
} else {
Widget::nothing()
}
@ -172,6 +186,7 @@ pub fn trips(
),
None,
)
.centered_vert()
.align_right(),
])
.outline(2.0, Color::WHITE)
@ -314,7 +329,7 @@ pub fn schedule(
// TODO Proportional 24-hour timeline would be easier to understand
let mut last_t = Time::START_OF_DAY;
for t in &person.trips {
let (start_time, from, _, _) = app.primary.sim.trip_info(*t);
let (start_time, from, _, _, _) = app.primary.sim.trip_info(*t);
let at = match from {
TripEndpoint::Bldg(b) => {
let b = app.primary.map.get_b(b);
@ -334,7 +349,7 @@ pub fn schedule(
last_t = start_time;
}
// Where do they spend the night?
let (start_time, _, to, _) = app.primary.sim.trip_info(*person.trips.last().unwrap());
let (start_time, _, to, _, _) = app.primary.sim.trip_info(*person.trips.last().unwrap());
let at = match to {
TripEndpoint::Bldg(b) => {
let b = app.primary.map.get_b(b);

View File

@ -50,7 +50,7 @@ pub fn ongoing(
.sim
.get_analytics()
.get_trip_phases(trip, &app.primary.map);
let (start_time, _, _, _) = app.primary.sim.trip_info(trip);
let (start_time, _, _, _, _) = app.primary.sim.trip_info(trip);
let col_width = 7;
let props = app.primary.sim.agent_properties(agent);
@ -143,7 +143,7 @@ pub fn future(
open_trip: &mut OpenTrip,
details: &mut Details,
) -> Widget {
let (start_time, trip_start, trip_end, _) = app.primary.sim.trip_info(trip);
let (start_time, trip_start, trip_end, _, _) = app.primary.sim.trip_info(trip);
let mut col = Vec::new();
@ -206,7 +206,7 @@ pub fn finished(
trip: TripID,
details: &mut Details,
) -> Widget {
let (start_time, _, _, _) = app.primary.sim.trip_info(trip);
let (start_time, _, _, _, _) = app.primary.sim.trip_info(trip);
let phases = if open_trips[&trip].show_after {
app.primary
.sim
@ -300,7 +300,7 @@ pub fn finished(
}
pub fn aborted(ctx: &mut EventCtx, app: &App, trip: TripID) -> Widget {
let (start_time, trip_start, trip_end, _) = app.primary.sim.trip_info(trip);
let (start_time, trip_start, trip_end, _, _) = app.primary.sim.trip_info(trip);
let mut col = vec![Text::from_multiline(vec![
Line("A glitch in the simulation happened."),
@ -336,7 +336,7 @@ fn make_timeline(
let map = &app.primary.map;
let sim = &app.primary.sim;
// TODO Repeating stuff
let (start_time, trip_start, trip_end, _) = sim.trip_info(trip);
let (start_time, trip_start, trip_end, _, _) = sim.trip_info(trip);
let end_time = phases.last().as_ref().and_then(|p| p.end_time);
let start_btn = {

View File

@ -133,8 +133,10 @@ impl Layer for Throughput {
_ => unreachable!(),
},
None => {
let new_compare = self.composite.has_widget("Compare before edits")
&& self.composite.is_checked("Compare before edits");
let new_compare = self
.composite
.maybe_is_checked("Compare before edits")
.unwrap_or(false);
if new_compare != self.compare {
*self = Throughput::new(ctx, app, new_compare);
self.composite.align_above(ctx, minimap);
@ -312,8 +314,10 @@ impl Layer for Delay {
_ => unreachable!(),
},
None => {
let new_compare = self.composite.has_widget("Compare before edits")
&& self.composite.is_checked("Compare before edits");
let new_compare = self
.composite
.maybe_is_checked("Compare before edits")
.unwrap_or(false);
if new_compare != self.compare {
*self = Delay::new(ctx, app, new_compare);
self.composite.align_above(ctx, minimap);

View File

@ -165,7 +165,7 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
// Gather raw data
let mut data = Vec::new();
for (id, phases) in app.primary.sim.get_analytics().get_all_trip_phases() {
let (_, start, end, _) = app.primary.sim.trip_info(id);
let (_, start, end, _, _) = app.primary.sim.trip_info(id);
if !opts.off_map_starts {
if let TripEndpoint::Border(_, _) = start {
continue;

View File

@ -28,6 +28,8 @@ struct Options {
modes: BTreeSet<TripMode>,
off_map_starts: bool,
off_map_ends: bool,
unmodified_trips: bool,
modified_trips: bool,
skip: usize,
}
@ -62,6 +64,8 @@ impl TripTable {
modes: TripMode::all().into_iter().collect(),
off_map_starts: true,
off_map_ends: true,
unmodified_trips: true,
modified_trips: true,
skip: 0,
};
Box::new(TripTable {
@ -145,11 +149,23 @@ impl State for TripTable {
}
let off_map_starts = self.composite.is_checked("starting off-map");
let off_map_ends = self.composite.is_checked("ending off-map");
let unmodified_trips = self
.composite
.maybe_is_checked("trips unmodified by experiment")
.unwrap_or(true);
let modified_trips = self
.composite
.maybe_is_checked("trips modified by experiment")
.unwrap_or(true);
if self.opts.off_map_starts != off_map_starts
|| self.opts.off_map_ends != off_map_ends
|| self.opts.unmodified_trips != unmodified_trips
|| self.opts.modified_trips != modified_trips
{
self.opts.off_map_starts = off_map_starts;
self.opts.off_map_ends = off_map_ends;
self.opts.unmodified_trips = unmodified_trips;
self.opts.modified_trips = modified_trips;
self.opts.skip = 0;
self.recalc(ctx, app);
}
@ -173,6 +189,7 @@ impl State for TripTable {
struct Entry {
trip: TripID,
mode: TripMode,
modified: bool,
departure: Time,
duration_after: Duration,
duration_before: Duration,
@ -208,7 +225,7 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
aborted += 1;
continue;
};
let (departure, start, end, _) = sim.trip_info(*id);
let (departure, start, end, _, modified) = sim.trip_info(*id);
if !opts.off_map_starts {
if let TripEndpoint::Border(_, _) = start {
continue;
@ -219,6 +236,12 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
continue;
}
}
if !opts.unmodified_trips && !modified {
continue;
}
if !opts.modified_trips && modified {
continue;
}
let (_, waiting) = sim.finished_trip_time(*id).unwrap();
let duration_before = if let Some(ref times) = trip_times_before {
@ -237,6 +260,7 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
trip: *id,
mode,
departure,
modified,
duration_after: *duration_after,
duration_before,
waiting,
@ -263,12 +287,15 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
// Render data
let mut rows = Vec::new();
for x in data.into_iter().skip(opts.skip).take(ROWS) {
let mut row = vec![
Text::from(Line(x.trip.0.to_string())).render_ctx(ctx),
let mut row = vec![Text::from(Line(x.trip.0.to_string())).render_ctx(ctx)];
if app.primary.has_modified_trips {
row.push(Text::from(Line(if x.modified { "Yes" } else { "No" })).render_ctx(ctx));
}
row.extend(vec![
Text::from(Line(x.mode.ongoing_verb()).fg(color_for_mode(app, x.mode))).render_ctx(ctx),
Text::from(Line(x.departure.ampm_tostring())).render_ctx(ctx),
Text::from(Line(x.duration_after.to_string())).render_ctx(ctx),
];
]);
if app.has_prebaked().is_some() {
row.push(
Text::from_all(cmp_duration_shorter(x.duration_after, x.duration_before))
@ -312,12 +339,15 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
Btn::text_bg2(name).build_def(ctx, None)
}
};
let mut headers = vec![
Line("Trip ID").draw(ctx),
let mut headers = vec![Line("Trip ID").draw(ctx)];
if app.primary.has_modified_trips {
headers.push(Line("Modified").draw(ctx));
}
headers.extend(vec![
Line("Type").draw(ctx),
btn(SortBy::Departure, "Departure"),
btn(SortBy::Duration, "Duration"),
];
]);
if app.has_prebaked().is_some() {
headers.push(btn(SortBy::RelativeDuration, "Comparison"));
headers.push(btn(SortBy::PercentChangeDuration, "Normalized"));
@ -330,6 +360,26 @@ fn make(ctx: &mut EventCtx, app: &App, opts: &Options) -> Composite {
col.push(Widget::row(vec![
Checkbox::text(ctx, "starting off-map", None, opts.off_map_starts),
Checkbox::text(ctx, "ending off-map", None, opts.off_map_ends),
if app.primary.has_modified_trips {
Checkbox::text(
ctx,
"trips unmodified by experiment",
None,
opts.unmodified_trips,
)
} else {
Widget::nothing()
},
if app.primary.has_modified_trips {
Checkbox::text(
ctx,
"trips modified by experiment",
None,
opts.modified_trips,
)
} else {
Widget::nothing()
},
]));
let (_, unfinished, _) = app.primary.sim.num_trips();
col.push(
@ -503,7 +553,7 @@ fn preview_route(g: &mut GfxCtx, app: &App, trip: TripID) -> GeomBatch {
}
}
let (_, start, end, _) = app.primary.sim.trip_info(trip);
let (_, start, end, _, _) = app.primary.sim.trip_info(trip);
batch.append(
GeomBatch::mapspace_svg(g.prerender, "system/assets/timeline/start_pos.svg")
.scale(10.0)

View File

@ -533,6 +533,7 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
},
TripEndpoint::Border(lane.src_i, None),
false,
false,
map,
);
}
@ -554,6 +555,7 @@ pub fn spawn_agents_around(i: IntersectionID, app: &mut App) {
},
TripEndpoint::Border(lane.src_i, None),
false,
false,
map,
);
}

View File

@ -11,6 +11,7 @@ use ezgui::{
HorizontalAlignment, Key, Line, Outcome, Spinner, Text, TextExt, VerticalAlignment, Widget,
};
use geom::Polygon;
use maplit::btreeset;
use sim::{ScenarioModifier, TripMode};
use std::collections::BTreeSet;
@ -23,7 +24,7 @@ pub struct PlayScenario {
impl PlayScenario {
pub fn new(
ctx: &mut EventCtx,
app: &App,
app: &mut App,
name: &String,
modifiers: Vec<ScenarioModifier>,
) -> Box<dyn GameplayState> {
@ -42,6 +43,10 @@ impl GameplayState for PlayScenario {
app: &mut App,
_: &mut SandboxControls,
) -> Option<Transition> {
// This should really happen in the constructor once, but the old PlayScenario's
// on_destroy can wipe this out.
app.primary.has_modified_trips = !self.modifiers.is_empty();
match self.top_center.event(ctx) {
Some(Outcome::Clicked(x)) => match x.as_ref() {
"change map" => {
@ -92,6 +97,10 @@ impl GameplayState for PlayScenario {
fn draw(&self, g: &mut GfxCtx, _: &App) {
self.top_center.draw(g);
}
fn on_destroy(&self, app: &mut App) {
app.primary.has_modified_trips = false;
}
}
fn make_top_center(
@ -308,7 +317,7 @@ impl ChangeMode {
Widget::dropdown(
ctx,
"to_mode",
TripMode::Drive,
TripMode::Bike,
TripMode::all()
.into_iter()
.map(|m| Choice::new(m.ongoing_verb(), m))
@ -322,10 +331,10 @@ impl ChangeMode {
Spinner::new(ctx, (1, 100), 50).named("pct_ppl"),
]),
"Types of trips to convert:".draw_text(ctx),
checkbox_per_mode(ctx, app, &BTreeSet::new()),
checkbox_per_mode(ctx, app, &btreeset! { TripMode::Drive }),
Widget::row(vec![
"Departing from:".draw_text(ctx),
AreaSlider::new(ctx, 0.25 * ctx.canvas.window_width, 0.3).named("depart from"),
AreaSlider::new(ctx, 0.25 * ctx.canvas.window_width, 0.0).named("depart from"),
]),
Widget::row(vec![
"Departing until:".draw_text(ctx),

View File

@ -138,7 +138,15 @@ impl Scenario {
&mut tmp_rng,
map,
);
spawner.schedule_trip(person, t.depart, spec, t.trip.start(map), t.cancelled, map);
spawner.schedule_trip(
person,
t.depart,
spec,
t.trip.start(map),
t.cancelled,
t.modified,
map,
);
}
}

View File

@ -62,7 +62,7 @@ pub enum TripSpec {
// This structure is created temporarily by a Scenario or to interactively spawn agents.
pub struct TripSpawner {
trips: Vec<(PersonID, Time, TripSpec, TripEndpoint, bool)>,
trips: Vec<(PersonID, Time, TripSpec, TripEndpoint, bool, bool)>,
}
impl TripSpawner {
@ -77,6 +77,7 @@ impl TripSpawner {
mut spec: TripSpec,
trip_start: TripEndpoint,
cancelled: bool,
modified: bool,
map: &Map,
) {
// TODO We'll want to repeat this validation when we spawn stuff later for a second leg...
@ -187,7 +188,7 @@ impl TripSpawner {
};
self.trips
.push((person.id, start_time, spec, trip_start, cancelled));
.push((person.id, start_time, spec, trip_start, cancelled, modified));
}
pub fn finalize(
@ -223,7 +224,8 @@ impl TripSpawner {
}
timer.start_iter("spawn trips", paths.len());
for ((p, start_time, spec, trip_start, cancelled), maybe_req, maybe_path) in paths {
for ((p, start_time, spec, trip_start, cancelled, modified), maybe_req, maybe_path) in paths
{
timer.next();
// TODO clone() is super weird to do here, but we just need to make the borrow checker
@ -248,6 +250,7 @@ impl TripSpawner {
} else {
TripMode::Drive
},
modified,
legs,
map,
)
@ -268,6 +271,7 @@ impl TripSpawner {
} else {
TripMode::Drive
},
modified,
legs,
map,
)
@ -288,6 +292,7 @@ impl TripSpawner {
start_time,
trip_start,
TripMode::Drive,
modified,
legs,
map,
)
@ -297,6 +302,7 @@ impl TripSpawner {
start_time,
trip_start,
TripMode::Walk,
modified,
vec![TripLeg::Walk(goal.clone())],
map,
),
@ -313,7 +319,15 @@ impl TripSpawner {
}
DrivingGoal::Border(_, _, _) => {}
};
trips.new_trip(person.id, start_time, trip_start, TripMode::Bike, legs, map)
trips.new_trip(
person.id,
start_time,
trip_start,
TripMode::Bike,
modified,
legs,
map,
)
}
TripSpec::UsingTransit {
route,
@ -328,6 +342,7 @@ impl TripSpawner {
start_time,
trip_start,
TripMode::Transit,
modified,
vec![
TripLeg::Walk(walk_to.clone()),
TripLeg::RideBus(route, stop2),
@ -341,6 +356,7 @@ impl TripSpawner {
start_time,
trip_start,
mode,
modified,
vec![TripLeg::Remote(to)],
map,
),

View File

@ -964,8 +964,9 @@ impl Sim {
self.trips.trip_to_agent(id)
}
// (start time, start position, end position, trip type)
pub fn trip_info(&self, id: TripID) -> (Time, TripEndpoint, TripEndpoint, TripMode) {
// (start time, start position, end position, trip type, modified)
// TODO Time for a struct
pub fn trip_info(&self, id: TripID) -> (Time, TripEndpoint, TripEndpoint, TripMode, bool) {
self.trips.trip_info(id)
}
// If trip is finished, returns (total time, total waiting time)

View File

@ -91,6 +91,7 @@ impl TripManager {
departure: Time,
start: TripEndpoint,
mode: TripMode,
modified: bool,
legs: Vec<TripLeg>,
map: &Map,
) -> TripID {
@ -125,6 +126,7 @@ impl TripManager {
legs: VecDeque::from(legs),
start,
end,
modified,
};
self.unfinished_trips += 1;
let person = &mut self.people[trip.person.0];
@ -855,9 +857,15 @@ impl TripManager {
std::mem::replace(&mut self.events, Vec::new())
}
pub fn trip_info(&self, id: TripID) -> (Time, TripEndpoint, TripEndpoint, TripMode) {
pub fn trip_info(&self, id: TripID) -> (Time, TripEndpoint, TripEndpoint, TripMode, bool) {
let t = &self.trips[id.0];
(t.departure, t.start.clone(), t.end.clone(), t.mode)
(
t.departure,
t.start.clone(),
t.end.clone(),
t.mode,
t.modified,
)
}
pub fn finished_trip_time(&self, id: TripID) -> Option<(Duration, Duration)> {
let t = &self.trips[id.0];
@ -1272,6 +1280,7 @@ struct Trip {
start: TripEndpoint,
end: TripEndpoint,
person: PersonID,
modified: bool,
}
impl Trip {