Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

meter shows "finished trips vs baseline" instead of time #565

Merged
merged 2 commits into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 143 additions & 59 deletions game/src/sandbox/speed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub struct TimePanel {
time: Time,
paused: bool,
setting: SpeedSetting,
// if present, how many trips were completed in the baseline at this point
baseline_finished_trips: Option<usize>,
}

#[derive(Clone, Copy, PartialEq, PartialOrd)]
Expand All @@ -42,6 +44,7 @@ impl TimePanel {
time: app.primary.sim.time(),
paused: false,
setting: SpeedSetting::Realtime,
baseline_finished_trips: None,
};
time.recreate_panel(ctx, app);
time
Expand Down Expand Up @@ -156,34 +159,138 @@ impl TimePanel {
self.panel = panel.build(ctx);
}

fn create_time_panel(&mut self, ctx: &EventCtx, app: &App) -> Widget {
fn trips_completion_bar(&mut self, ctx: &EventCtx, app: &App) -> Widget {
let text_color = Color::WHITE;
let bar_fg = ctx.style().primary_fg;
let bar_bg = bar_fg.tint(0.6).shade(0.2);
let cursor_fg = Color::hex("#939393");

// This is manually tuned
let bar_width = 400.0;
let bar_height = 27.0;

let (finished, unfinished) = app.primary.sim.num_trips();
let trip_results = Widget::row(vec![
{
let mut txt = Text::new();
let pct = if unfinished == 0 {
100.0
let total = finished + unfinished;
let ratio = if total > 0 {
finished as f64 / total as f64
} else {
0.0
};
let finished_width = ratio * bar_width;

if app.has_prebaked().is_some() {
let now = self.time;
let mut baseline_finished = self.baseline_finished_trips.unwrap_or(0);
for (t, _, _, _) in &app.prebaked().finished_trips[baseline_finished..] {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, clever trick. Most of the things that read Analytics recalculate everything from scratch constantly. To one day speed this up, I was imagining making some kind of stateful "cursor" objects to remember where a computation left off. But that's total overkill for something like this -- just remembering the count is much simpler.

if *t > now {
break;
}
baseline_finished += 1;
}
// memoized for perf.
// A bit of profiling shows we save about 0.7% of runtime
// (using montlake, zoomed out, at max speed)
self.baseline_finished_trips = Some(baseline_finished);
}

let baseline_finished_ratio: Option<f64> =
self.baseline_finished_trips.and_then(|baseline_finished| {
if unfinished + baseline_finished > 0 {
Some(baseline_finished as f64 / (baseline_finished + unfinished) as f64)
} else {
100.0 * (finished as f64) / ((finished + unfinished) as f64)
};
txt.add(Line(format!(
"Finished trips: {} ({}%)",
prettyprint_usize(finished),
pct as usize
)));
txt.into_widget(ctx).centered_vert()
},
if app.primary.dirty_from_edits {
ctx.style()
.btn_plain
.icon("system/assets/tools/warning.svg")
.build_widget(ctx, "see why results are tentative")
.centered_vert()
.align_right()
None
}
});
let baseline_finished_width: Option<f64> = baseline_finished_ratio
.map(|baseline_finished_ratio| baseline_finished_ratio * bar_width);

let cursor_width = 2.0;
let mut progress_bar = GeomBatch::new();

{
// TODO Why is the rounding so hard? The white background is always rounded
// at both ends. The moving bar should always be rounded on the left, flat
// on the right, except at the very end (for the last 'radius' pixels). And
// when the width is too small for the radius, this messes up.
progress_bar.push(bar_bg, Polygon::rectangle(bar_width, bar_height));
progress_bar.push(bar_fg, Polygon::rectangle(finished_width, bar_height));

if let Some(baseline_finished_width) = baseline_finished_width {
if baseline_finished_width > 0.0 {
let baseline_cursor = Polygon::rectangle(cursor_width, bar_height)
.translate(baseline_finished_width, 0.0);
progress_bar.push(cursor_fg, baseline_cursor);
}
}
}

let text_geom = Text::from(
Line(format!("Finished Trips: {}", prettyprint_usize(finished))).fg(text_color),
)
.render(ctx)
.translate(8.0, 0.0);
progress_bar.append(text_geom);

if let Some(baseline_finished_width) = baseline_finished_width {
let triangle_width = 9.0;
let triangle_height = 9.0;

// Add a triangle-shaped cursor above the baseline cursor
progress_bar = progress_bar.translate(0.0, triangle_height);

use geom::Triangle;
let triangle = Triangle::new(
Pt2D::zero(),
Pt2D::new(triangle_width, 0.0),
Pt2D::new(triangle_width / 2.0, triangle_height),
);
let mut triangle_poly = Polygon::from_triangle(&triangle);
triangle_poly = triangle_poly.translate(
baseline_finished_width - triangle_width / 2.0 + cursor_width / 2.0,
0.0,
);

progress_bar.push(cursor_fg, triangle_poly);
}

use widgetry::DrawWithTooltips;
let mut tooltip_text = Text::from(Line("Finished Trips"));
tooltip_text.add(Line(format!(
"{} ({}% of total)",
prettyprint_usize(finished),
(ratio * 100.0) as usize
)));
if let Some(baseline_finished) = self.baseline_finished_trips {
// TODO: up/down icons
let line = if baseline_finished > finished {
let difference = baseline_finished - finished;
Line(format!(
"{} less than baseline",
prettyprint_usize(difference)
))
.fg(ctx.style().text_destructive_color)
} else if baseline_finished < finished {
let difference = finished - baseline_finished;
Line(format!(
"{} more than baseline",
prettyprint_usize(difference)
))
.fg(ctx.style().text_tooltip_color)
} else {
Widget::nothing()
},
]);
Line("No change from baseline")
};
tooltip_text.add(line);
}

let bounds = progress_bar.get_bounds();
let bounding_box = Polygon::rectangle(bounds.width(), bounds.height());
let tooltip = vec![(bounding_box, tooltip_text)];
DrawWithTooltips::new(ctx, progress_bar, tooltip, Box::new(|_| GeomBatch::new()))
}

fn create_time_panel(&mut self, ctx: &EventCtx, app: &App) -> Widget {
let trips_bar = self.trips_completion_bar(ctx, app);

// TODO This likely fits better in the top center panel, but no easy way to squeeze it
// into the panel for all gameplay modes
let record_trips = if let Some(n) = app.primary.sim.num_recorded_trips() {
Expand All @@ -205,42 +312,19 @@ impl TimePanel {
Widget::nothing()
};

let time_bar = {
let mut batch = GeomBatch::new();
// This is manually tuned
let width = 400.0;
let height = 15.0;
// Just clamp if we simulate past the expected end
let percent = self
.time
.to_percent(app.primary.sim.get_end_of_day())
.min(1.0);

// TODO Why is the rounding so hard? The white background is always rounded
// at both ends. The moving bar should always be rounded on the left, flat
// on the right, except at the very end (for the last 'radius' pixels). And
// when the width is too small for the radius, this messes up.

batch.push(Color::WHITE, Polygon::rectangle(width, height));

if percent != 0.0 {
batch.push(
if percent < 0.25 || percent > 0.75 {
app.cs.night_time_slider
} else {
app.cs.day_time_slider
},
Polygon::rectangle(percent * width, height),
);
}

batch.into_widget(ctx)
};

Widget::col(vec![
Text::from(Line(self.time.ampm_tostring()).big_monospaced()).into_widget(ctx),
time_bar,
trip_results,
trips_bar.margin_above(12),
if app.primary.dirty_from_edits {
ctx.style()
.btn_plain
.icon("system/assets/tools/warning.svg")
.build_widget(ctx, "see why results are tentative")
.centered_vert()
.align_right()
} else {
Widget::nothing()
},
record_trips,
])
}
Expand Down
2 changes: 1 addition & 1 deletion geom/src/polygon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ pub struct Triangle {
}

impl Triangle {
pub(crate) fn new(pt1: Pt2D, pt2: Pt2D, pt3: Pt2D) -> Triangle {
pub fn new(pt1: Pt2D, pt2: Pt2D, pt3: Pt2D) -> Triangle {
Triangle { pt1, pt2, pt3 }
}

Expand Down
4 changes: 4 additions & 0 deletions geom/src/pt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ impl Pt2D {
}
}

pub fn zero() -> Self {
Self::new(0.0, 0.0)
}

// TODO This is a small first step...
pub fn approx_eq(self, other: Pt2D, threshold: Distance) -> bool {
self.dist_to(other) <= threshold
Expand Down
4 changes: 4 additions & 0 deletions widgetry/src/style/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct Style {
pub field_bg: Color,
pub dropdown_border: Color,
pub icon_fg: Color,
pub primary_fg: Color,
pub text_fg_color: Color,
pub text_tooltip_color: Color,
pub text_hotkey_color: Color,
Expand Down Expand Up @@ -154,6 +155,7 @@ impl Style {
section_outline: (2.0, Color::WHITE.shade(0.1)),
loading_tips: Text::new(),
icon_fg: hex("#4C4C4C"),
primary_fg: hex("#EE702E"),
text_fg_color: hex("#4C4C4C"),
text_hotkey_color: hex("#EE702E"),
text_tooltip_color: Color::WHITE,
Expand Down Expand Up @@ -185,6 +187,7 @@ impl Style {
section_outline: (2.0, Color::WHITE),
loading_tips: Text::new(),
icon_fg: Color::WHITE,
primary_fg: hex("#EE702E"),
text_fg_color: Color::WHITE,
text_hotkey_color: Color::GREEN,
text_tooltip_color: Color::WHITE,
Expand All @@ -211,6 +214,7 @@ impl Style {
section_outline: (DEFAULT_OUTLINE_THICKNESS, navy.shade(0.2)),
loading_tips: Text::new(),
icon_fg: Color::WHITE,
primary_fg: hex("#EE702E"),
text_fg_color: Color::WHITE,
text_hotkey_color: Color::GREEN,
text_tooltip_color: Color::WHITE,
Expand Down
5 changes: 5 additions & 0 deletions widgetry/src/widgets/just_draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ pub struct DrawWithTooltips {
}

impl DrawWithTooltips {
/// `batch`: the `GeomBatch` to draw
/// `tooltips`: (hitbox, text) tuples where each `text` is shown when the user hovers over
/// the respective `hitbox`
/// `hover`: returns a GeomBatch to render upon hovering. Return an `GeomBox::new()` if
/// you want hovering to be a no-op
pub fn new(
ctx: &EventCtx,
batch: GeomBatch,
Expand Down