feat: grade a sparse window's polyline by slope, so phosphor does not switch off on zoom

Intensity grading was gated on the column reduction, which is gated on a
density whose only job is killing the moire beat. Dwell has nothing to do
with that, so narrowing a window past the threshold turned the whole
phosphor model off and a discontinuity went from a third of a flat top's
ink to all of it in one pixel of zoom.

stage_trace_slopes() grades each segment by its own slope instead, with a
measure that agrees with the dense path at the boundary, and reports
itself as FrameStats::slope_traces -- disjoint from column_traces, so a
host asserting graded_traces == column_traces reads the same as before.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-10-08 16:56:10 +02:00
1 parent cc908efa36
commit 37e414bcc5
8 files changed
+442 -13

No files matched your search

+30
View File
@@ -38,6 +38,12 @@ struct FrameStats {
/// (`Style::intensity_grading`). A subset of `column_traces`: grading is a way of
/// drawing the reduction, not a third path.
u32 graded_traces = 0;
/// Traces drawn as a polyline whose *segments* were graded by slope -- the sparse
/// half of the same phosphor model (`Renderer::stage_trace_slopes`). **Disjoint from
/// `column_traces`**, and therefore from `graded_traces`: a window is either dense
/// enough to reduce onto the pixel grid or it is drawn as a polyline, never both. A
/// host asserting `graded_traces == column_traces` keeps that reading unchanged.
u32 slope_traces = 0;
u64 visible_samples = 0; ///< samples the gathered windows covered, summed
u32 staged_segments = 0; ///< line segments staged, all colour groups
u32 staged_markers = 0; ///< marker shapes staged over the traces
@@ -539,6 +545,30 @@ class Renderer {
/// from its own min and max (see `Style::intensity_grading`).
u32 stage_trace_density(const Plot& plot, const PlotLayout& layout, const Trace& trace,
f64 x_origin, u32 points);
/// The **sparse** window's polyline, graded by the same measure: each segment is
/// staged into the step its own *slope* earns, so a near-vertical segment between two
/// adjacent samples -- a discontinuity, which the signal crossed without dwelling
/// anywhere in it -- is faint and a near-flat one is bright.
///
/// It exists because grading used to be gated on the column reduction, and that
/// reduction is gated on a *density* (two gathered points per pixel column) whose only
/// purpose is removing the moire beat. Dwell has nothing to do with that density, so
/// zooming in past it switched the whole phosphor model off and drew an edge as
/// brightly as a flat top -- the edge got brighter the further in you zoomed.
///
/// The measure agrees with `stage_trace_density()`'s at the boundary but is **absolute
/// rather than per column**: below two points a column there is no column peak to
/// normalise against, so a segment's dwell is `1 / (1 + rows it spans)` outright. A
/// flat segment earns the top step and one crossing the whole axis earns
/// `Style::intensity_floor`, which is what a riser's column already gets on the dense
/// side.
///
/// Geometry is staged exactly as `stage_xy()` stages it -- in the pre-affine space,
/// relative to `x_origin` -- so only *which batch* a segment lands in differs from the
/// ungraded polyline, and the caller flushes with the trace's own `Mapping` and
/// `snap = false`. Returns the segments staged across all steps (0 stages nothing).
u32 stage_trace_slopes(const Plot& plot, const Trace& trace, const f32* xy, u32 points,
f64 x_origin, bool log_x, bool log_y);
/// The cursors, both orientations: one stroke per cursor, placed at the coordinate its own
/// orientation owns (`Plot::cursor_screen_coordinate()`) and spanning the other axis' whole
/// range, in its own colour -- or in the accent at handle width while it is hovered or
+8
View File
@@ -202,6 +202,14 @@ struct Style {
/// bright mark. Counting a row at +1 per segment that touches it instead -- the
/// obvious thing -- grades a sine's fast middle as brightly as its rails, which
/// is the pattern inverted.
///
/// Below `dense_threshold_spp`, where there are no columns to reduce, this grades
/// the **polyline** by each segment's own slope instead (`FrameStats::slope_traces`
/// against `graded_traces`) -- the same measure for a sparse window, where a
/// segment *is* the sub-column. One switch covers both because the alternative was
/// a defect: gated on the reduction, the model switched off as a window narrowed
/// past the threshold, and an edge drawn at a third of a flat top's ink jumped to
/// all of it in one pixel of zoom.
bool intensity_grading = false;
/// Sub-buckets per pixel column the gather is asked for when grading: the level