feat: a span declared invalid is shaded behind the data it is missing from
This commit is contained in:
1 parent
1ae72d4903
commit
ebc67bd318
6 files changed
+250
No files matched your search
@@ -344,6 +344,10 @@ void Plot::set_y_axis_origin_from_range(u32 axis, bool on) {
|
||||
|
||||
void Plot::set_x_note(std::string text) { x_note_ = std::move(text); }
|
||||
|
||||
void Plot::set_invalid_spans(std::span<const std::pair<f64, f64>> spans) {
|
||||
invalid_spans_.assign(spans.begin(), spans.end());
|
||||
}
|
||||
|
||||
f64 Plot::x_axis_origin(u32 axis) const {
|
||||
return axis < x_axes_.size() ? x_axes_[axis].origin : 0.0;
|
||||
}
|
||||
@@ -1091,6 +1095,7 @@ const PlotLayout& Plot::layout() {
|
||||
// measured from the *placed* live windows, which is what makes a region's data
|
||||
// interval map to the pixel columns the frame will actually use.
|
||||
build_regions();
|
||||
build_invalid_spans();
|
||||
build_legend();
|
||||
// The crosshair last: its panel is fitted into `axes` and its lines are held inside it, and
|
||||
// its rows are the sample values nearest a data x -- so it wants the transforms the
|
||||
@@ -1358,6 +1363,41 @@ void Plot::build_regions() {
|
||||
}
|
||||
}
|
||||
|
||||
void Plot::build_invalid_spans() {
|
||||
layout_.invalid_spans.clear();
|
||||
if (invalid_spans_.empty()) return;
|
||||
|
||||
const Rect& axes = layout_.axes;
|
||||
// The primary x axis' transform, taken here for `build_regions()`' reason: after the live
|
||||
// windows have been placed and the strips have settled, so these are the columns the frame
|
||||
// draws with and a band is anchored to the data by construction rather than by a second
|
||||
// mapping that would have to be kept in step with this one.
|
||||
const Transform tx = x_transform(0u);
|
||||
// A hole can be narrower than a column -- one missing sample in a window of hundreds of
|
||||
// thousands -- and a fill rounded to zero width would draw nothing, losing exactly the case
|
||||
// the band is most needed for. One device pixel is the floor.
|
||||
const f32 min_w = std::max(1.0f, style_.device(1.0f));
|
||||
|
||||
for (const std::pair<f64, f64>& span : invalid_spans_) {
|
||||
const f64 a = std::min(span.first, span.second);
|
||||
const f64 b = std::max(span.first, span.second);
|
||||
if (!std::isfinite(a) || !std::isfinite(b)) continue;
|
||||
const f32 lo = static_cast<f32>(tx.to_screen_x(a));
|
||||
const f32 hi = static_cast<f32>(tx.to_screen_x(b));
|
||||
// Scrolled out of the window: nothing to shade. Tested before the floor is applied, so a
|
||||
// span off the left edge is dropped rather than pinned to it as a one-pixel band that
|
||||
// accuses the samples at the window's start.
|
||||
if (hi < axes.x || lo > axes.right()) continue;
|
||||
const f32 first = std::clamp(lo, axes.x, axes.right());
|
||||
const f32 last = std::clamp(hi, axes.x, axes.right());
|
||||
const f32 w = std::min(std::max(last - first, min_w), axes.w);
|
||||
// Widening a band at the right edge would run it over the axis line, so the floor is taken
|
||||
// out of the left instead: the span's own edge is kept wherever there is room for it.
|
||||
const f32 x = std::clamp(first, axes.x, axes.right() - w);
|
||||
layout_.invalid_spans.push_back(Rect{x, axes.y, w, axes.h});
|
||||
}
|
||||
}
|
||||
|
||||
// --- crosshair (spec §15) --------------------------------------------------
|
||||
|
||||
CrosshairSpan crosshair_span(const data::Series& series, f64 x) noexcept {
|
||||
|
||||
@@ -516,6 +516,9 @@ void Renderer::draw_plot(Plot& plot, const PlotLayout& layout) {
|
||||
// against the line passes would not change a pixel.)
|
||||
set_clip(layout.axes);
|
||||
draw_grid(plot);
|
||||
// The holes over the grid and under the data: a band is chrome of the *background*, not an
|
||||
// overlay, so everything a frame draws inside the axes is drawn on top of it.
|
||||
draw_invalid_spans(layout);
|
||||
clear_clip();
|
||||
draw_axis_lines(plot);
|
||||
set_clip(layout.axes);
|
||||
@@ -1728,6 +1731,12 @@ void Renderer::draw_cursor_label(const Plot& plot, const PlotLayout& layout, con
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::draw_invalid_spans(const PlotLayout& layout) {
|
||||
for (const Rect& band : layout.invalid_spans) {
|
||||
if (band.w > 0.0f) fill_pixels(band, style_.invalid_fill);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::draw_regions(const Plot& plot, const PlotLayout& layout) {
|
||||
const HotHandles hot = plot.hot_handles();
|
||||
const f32 edge_w = style_.device(style_.line_width_px);
|
||||
|
||||
Reference in new issue
Block a user