perf(chart): optimize axis plotted extent scan and cache non-bar error bar stats (#1266)

This commit is contained in:
Dylan M. Taylor
2026-10-24 04:41:23 -04:00
committed by GitHub
parent 9641817a15
commit 713f605566
2 changed files with 34 additions and 16 deletions
+32 -15
View File
@@ -609,27 +609,44 @@ impl Chart {
return (!secondary && self.kind != ChartType::Combo).then_some((0.0, 0.0));
}
let (mut lo, mut hi) = (f64::INFINITY, f64::NEG_INFINITY);
let n = ser.iter().map(|s| s.values.len()).max().unwrap_or(0);
let has_stacked_or_percent = ser.iter().any(|s| {
let k = self.series_kind(s);
k.stacked() || k.percent()
});
let mut percent = false;
for i in 0..n {
let (mut p, mut m, mut stacked) = (0.0, 0.0, false);
if has_stacked_or_percent {
let n = ser.iter().map(|s| s.values.len()).max().unwrap_or(0);
let budget = (ser.len() * n).min(10_000_000);
let n_clamped = if ser.is_empty() { 0 } else { n.min(budget / ser.len()) };
for i in 0..n_clamped {
let (mut p, mut m, mut stacked) = (0.0, 0.0, false);
for s in &ser {
let k = self.series_kind(s);
let Some(v) = s.values.get(i).copied().flatten().filter(|v| v.is_finite()) else { continue };
if k.percent() {
percent = true;
} else if k.stacked() {
stacked = true;
if v >= 0.0 { p += v } else { m += v }
} else {
hi = hi.max(v);
lo = lo.min(v);
}
}
if stacked {
hi = hi.max(p);
lo = lo.min(m);
}
}
} else {
for s in &ser {
let k = self.series_kind(s);
let Some(v) = s.values.get(i).copied().flatten().filter(|v| v.is_finite()) else { continue };
if k.percent() {
percent = true;
} else if k.stacked() {
stacked = true;
if v >= 0.0 { p += v } else { m += v }
} else {
for v in s.values.iter().flatten().copied().filter(|v| v.is_finite()) {
hi = hi.max(v);
lo = lo.min(v);
}
}
if stacked {
hi = hi.max(p);
lo = lo.min(m);
}
}
if !percent {
for s in &ser {
+2 -1
View File
@@ -696,6 +696,7 @@ fn draw_inner(ctx: &mut RenderContext, rctx: &Ctx, c: &Chart, m: Affine, w: f64,
let stacked = kind.stacked();
let area_kind = matches!(kind, ChartType::Area | ChartType::StackedArea | ChartType::FilledRadar);
let col = if area_kind { series_fill(rctx, c, si, Some(s)) } else { series_line(rctx, c, si, s) };
let series_stats = s.error_bars.as_ref().map(|_| deckcraft_model::chart::SeriesStats::of(&s.values));
let mut pts = vec![];
for i in 0..ncat {
let Some(v) = s.values.get(i).copied().flatten() else { continue };
@@ -719,7 +720,7 @@ fn draw_inner(ctx: &mut RenderContext, rctx: &Ctx, c: &Chart, m: Affine, w: f64,
}
_ => plot.x0 + band * (slot(i) + 0.5),
};
if let Some((lo, hi)) = s.error_bars.as_ref().and_then(|e| e.range(&s.values, i, v)) {
if let Some((lo, hi)) = s.error_bars.as_ref().and_then(|e| series_stats.as_ref().and_then(|st| e.range_in(st, i, v))) {
err_marks.push((x, to_axis(sec, lo), to_axis(sec, hi), base * 0.4, s.error_bars.as_ref().is_some_and(|e| e.end_cap)));
}
pts.push(Point::new(x, to_axis(sec, v)));