feat: a phosphor density is the trace, one grid per trace in its own colour
A user looked at the display and reported three things at once: "there is clearly a gain offset between latest acquisition and persistent image", "persistent instead of being brighter + opaque is just gray/white", and "last acquisition does not seem to be affected, it looks like persistent image + last acquisition (so for example last acquisition never fade in time mode)". What they add up to is that this was not a phosphor yet: it was a **density with a solid trace drawn over it**. Four changes, and the first is the one the others follow from: - **The current acquisition is deposited like every other, and no solid trace is drawn over the image.** `Renderer::draw_traces()` returns at once while a density is on: the trace ink *is* the image, so the newest acquisition is a sweep inside it and fades with it. Drawing the polyline as well put a solid, unfading stroke over its own faint ghost -- the reported "persistent image + last acquisition", where the last acquisition never faded in time mode. - **One grid per trace, in that trace's own colour.** `Plot::density_grid(trace)` is one screen-sized `f32` grid per trace, deposited from that trace and drawn in `Trace::color`, because a density that had one hue for the plot could not say *which* trace put the ink there -- two signals agreeing in a cell would be one blended pixel. The display's memory is therefore `traces * columns * rows * 4` bytes rather than one plot's worth (a trace with no series, an unbound slot, holds no grid), which is the one cost this change adds: four signals in one 800x480 row are four grids, 6 MB. - **The ceiling is the trace's own brightness, not the fade's steady state.** `full_scale` now defaults to **one sweep**: a cell with one acquisition's ink is drawn in its trace's colour at that colour's own alpha, and a signal crossing the same place every acquisition settles *at* the colour its trace would have drawn (the ceiling clamps) rather than a fraction of a brighter steady state -- the gain offset the user reported. `Style::density`'s near-white 0.85 tint is gone with it, because there is no one tint any more. A caller that wants the fade's ramp on screen instead still sets `full_scale` to the sum the fade settles at and accepts the offset; that is a legitimate choice, just not the one a phosphor makes. The reading is a **framebuffer** one, because neither `ink`/`peak` nor a tint can make a colour or a fade: `a_phosphor_density_is_the_trace_in_its_own_colour` deposits one sweep, checks the saturated cell is *exactly* the trace's colour (`full=224`, the trace's `0xE0` green, against `bg=25` -- the old monochrome tint read ~190 red), then fades it one time constant and checks the same pixel is dimmer (`faded=115`) and still the trace's hue -- which a stroke over the density could not do. It moves the trace and draws without depositing to prove no polyline is drawn (`moved` equals the no-density frame at the moved row), and reads a steady state against a quarter of it (`steady=224`, `quarter=138`), in the signal's colour throughout. `a_density_holds_one_grid_per_trace` reads two traces' grids apart (`267 472 cells each, ink 584/584`, in two rows). The grid's own cases are unchanged where they should be (`a_density_halves_per_acquisition_and_keeps_the_newest_brightest` `peak_steady=2.000`, `a_density_fades_by_its_time_constant` `exp(-dt/tau)`). Suite: **397 cases, 0 failed**, 4/4 ctest entries.
This commit is contained in:
1 parent
738205293e
commit
1b8b55d3ee
12 files changed
+534
-299
No files matched your search
@@ -35,6 +35,14 @@ enum class DensityDecay {
|
||||
/// A plot's phosphor density: whether the grid is on, how it fades, and how a cell's count is
|
||||
/// turned into brightness. Plain data, replaced wholesale by `Plot::set_density()`.
|
||||
///
|
||||
/// The image is **one grid per trace**, drawn in that trace's own colour (`Trace::color`), because
|
||||
/// a density that had one hue for the whole plot could not say *which* trace put the ink there --
|
||||
/// two signals agreeing in a cell would be one blended pixel, and a display of several traces
|
||||
/// could not be read back to the trace it came from. A cell's brightness is a count of sweeps in
|
||||
/// *that trace's* colour: `full_scale` (default one sweep, the trace's own brightness) is where it
|
||||
/// saturates, and a signal crossing the same place every acquisition settles exactly at the colour
|
||||
/// its trace would have drawn.
|
||||
///
|
||||
/// Its reason to exist as a *plot* feature rather than a host's overlay is the same one
|
||||
/// `Plot::set_invalid_spans()` has: the plot is what knows a trace's device geometry -- the
|
||||
/// visible window about to be drawn, on the axes rect the layout settled -- so the host would
|
||||
@@ -56,15 +64,21 @@ struct DensityConfig {
|
||||
/// what a phosphor's afterglow looks like sweep to sweep: 1, 0.5, 0.25, 0.125 and by the
|
||||
/// seventh the layer is under 1 %.
|
||||
f32 per_acquisition = 0.5f;
|
||||
/// The cell value that reads as **full brightness**, in accumulated sweeps. The default is the
|
||||
/// ink the **default fade settles at** -- `1/(1 - 0.5)`, the geometric sum of
|
||||
/// `PerAcquisition`'s factor -- and that is the rule worth keeping: a ceiling at *one* sweep
|
||||
/// puts the newest acquisition at full white and everything above it clamps to the same pixel,
|
||||
/// so a display of one sweep and a display of five read alike and the fade is invisible
|
||||
/// (measured: a cell at a quarter of the scale rendered 104 of 255 while one at the ceiling
|
||||
/// rendered 156, but with the ceiling at one sweep both were 199). A caller whose fade settles
|
||||
/// elsewhere wants its own sum here.
|
||||
f32 full_scale = 2.0f;
|
||||
/// The cell value that reads as **full brightness**, in accumulated sweeps -- and the one field
|
||||
/// a caller reads *off its own trace* rather than off the fade. A cell at this value is drawn in
|
||||
/// its trace's own colour at that colour's own alpha; the default of **one sweep** is therefore
|
||||
/// the ink the newest acquisition leaves, so the acquisition just drawn reads exactly as the
|
||||
/// trace it is a picture of (a signal crossing the same place every acquisition settles at the
|
||||
/// trace's brightness, where a ceiling above one sweep would settle *brighter* than the trace and
|
||||
/// leave a gain offset between the newest sweep and the image behind it). Everything above the
|
||||
/// ceiling clamps to the same pixel: where several acquisitions agree, the cell is the trace's
|
||||
/// colour at full strength and no brighter.
|
||||
///
|
||||
/// A caller that wants the fade's own ramp on screen instead -- the newest sweep a *fraction* of
|
||||
/// the steady state, so the eye can rank the sweeps -- sets this to the sum the fade settles at
|
||||
/// (for `PerAcquisition`'s factor `f` that is `1/(1-f)`) and accepts the gain offset; that was
|
||||
/// this field's first default, and it is a legitimate choice, not the one a phosphor makes.
|
||||
f32 full_scale = 1.0f;
|
||||
/// The gamma applied to the normalised cell value before it becomes a luminance. **1.0 by
|
||||
/// default: linear in sweeps**, because that is what a count means -- a cell covered by half the
|
||||
/// acquisitions is half the ink. Under 1 lifts the faint end, and that is what
|
||||
@@ -79,10 +93,13 @@ struct DensityConfig {
|
||||
/// resolution is the screen's and not the data's -- the whole point of accumulating into a grid
|
||||
/// rather than keeping the acquisitions that were drawn into it. Memory is therefore
|
||||
/// `columns() * rows() * 4` bytes whatever the trigger has done: an 800x480 axes is 1.5 MB and a
|
||||
/// 1920x1080 one is 8.3 MB, and a thousand acquisitions cost the same as one. The renderer adds
|
||||
/// one byte per cell as the luminance it uploads (`DensityGrid::cells()` is `f32` because a
|
||||
/// **count** is what decays and what `ink()`/`peak()` report; the byte is derived per changed
|
||||
/// frame, not stored here).
|
||||
/// 1920x1080 one is 8.3 MB, and a thousand acquisitions cost the same as one. A plot holds **one
|
||||
/// grid per trace** (see `DensityConfig`), so a display's memory is that figure times the number of
|
||||
/// traces actually drawn -- a plot's unbound slots hold no grid -- and four signals in one 800x480
|
||||
/// row are four grids, 6 MB, while a 1920x1080 row of eight is 66 MB. The renderer adds one byte
|
||||
/// per cell as the luminance it uploads (`DensityGrid::cells()`
|
||||
/// is `f32` because a **count** is what decays and what `ink()`/`peak()` report; the byte is
|
||||
/// derived per changed frame, not stored here).
|
||||
///
|
||||
/// A cell's value is a **number of sweeps**: every visible trace of the plot that covers that cell
|
||||
/// during one `Plot::accumulate_density()` leaves 1.0 in it -- once, however many of that trace's
|
||||
|
||||
+53
-39
@@ -590,13 +590,16 @@ class Plot {
|
||||
/// default -- is the plot exactly as it was: no grid is allocated, no geometry is deposited
|
||||
/// and no frame uploads anything.
|
||||
///
|
||||
/// On, the plot keeps a **screen-sized intensity grid** behind its traces (see `DensityGrid`
|
||||
/// for the resolution, the per-cell meaning and the memory cost) and deposits the acquisition
|
||||
/// on screen into it with `accumulate_density()`: brightness means how often a trace passed,
|
||||
/// monochrome, and every cell fades as the display's own clock moves on. It is deliberately
|
||||
/// *not* a set of retained traces -- the library has the trace geometry in hand, so a density
|
||||
/// costs one grid whatever the trigger has done, where retaining the acquisitions would cost
|
||||
/// a window copy each.
|
||||
/// On, the plot keeps a **screen-sized intensity grid behind each of its traces**, in that
|
||||
/// trace's own colour (see `DensityGrid` for the resolution and per-cell meaning, and
|
||||
/// `DensityConfig` for the per-trace colour and the memory the grids cost), and deposits the
|
||||
/// acquisition on screen into it with `accumulate_density()`. While a density is on the
|
||||
/// renderer draws **no solid trace** over it: the trace ink *is* the image, so the newest
|
||||
/// acquisition is deposited like every other and fades with the rest -- a trace left drawn on
|
||||
/// top of its own density could never fade, which is what a report of "the last acquisition
|
||||
/// never fades" was. It is deliberately *not* a set of retained traces -- the library has the
|
||||
/// trace geometry in hand, so a density costs one grid per trace whatever the trigger has done,
|
||||
/// where retaining the acquisitions would cost a window copy each.
|
||||
///
|
||||
/// Like `set_invalid_spans()` it reserves **no layout space** and nothing on it can be picked:
|
||||
/// it is drawn inside the axes it was sized to, and a pointer neither finds it nor is found by
|
||||
@@ -611,18 +614,24 @@ class Plot {
|
||||
void set_density(const DensityConfig& config);
|
||||
/// The configuration, as set.
|
||||
const DensityConfig& density() const noexcept { return density_; }
|
||||
/// The accumulated image, for a host that wants to draw it or read it back. Empty (0x0) until
|
||||
/// the first `accumulate_density()` on a laid-out plot.
|
||||
const DensityGrid& density_grid() const noexcept { return density_grid_; }
|
||||
/// The accumulated image **of a trace**, for a host that wants to draw it or read it back. There
|
||||
/// is one per trace, in trace order, because each is drawn in that trace's own colour (see
|
||||
/// `DensityConfig`); an out-of-range trace reads an empty (0x0) image. Empty until the first
|
||||
/// `accumulate_density()` on a laid-out plot, and dropped by `set_density()` off.
|
||||
const DensityGrid& density_grid(u32 trace) const noexcept {
|
||||
return trace < density_grids_.size() ? density_grids_[trace] : empty_density_grid();
|
||||
}
|
||||
|
||||
/// Deposits **the acquisition on screen** into the grid: every visible trace of this plot,
|
||||
/// over the x window its own axis is showing, 1.0 into every cell it covers -- **once per cell
|
||||
/// per sweep**, however many of that trace's own samples crossed it, so a cell's value is how
|
||||
/// many traces have covered it since the last decay and not how much the beam moved (see
|
||||
/// `DensityGrid`; the dwell question is `Style::intensity_grading`'s). Called once per
|
||||
/// *acquisition*, which is the cadence the grid counts in: a
|
||||
/// caller that deposited every frame would add one sweep sixty times a second, and the
|
||||
/// per-acquisition fade would have nothing to measure itself against.
|
||||
/// Deposits **the acquisition on screen** into each trace's grid: every visible trace of this
|
||||
/// plot, the newest acquisition included, over the x window its own axis is showing, 1.0 into
|
||||
/// every cell it covers -- **once per cell per sweep**, however many of that trace's own samples
|
||||
/// crossed it, so a cell's value is how many traces have covered it since the last decay and not
|
||||
/// how much the beam moved (see `DensityGrid`; the dwell question is
|
||||
/// `Style::intensity_grading`'s). Called once per *acquisition*, which is the cadence the grids
|
||||
/// count in: a caller that deposited every frame would add one sweep sixty times a second, and
|
||||
/// the per-acquisition fade would have nothing to measure itself against. The renderer draws no
|
||||
/// polyline for a trace whose density is on (see `set_density()`), so this deposit is the whole
|
||||
/// of what the newest acquisition looks like -- and it fades with the rest.
|
||||
///
|
||||
/// The geometry is the frame's own. The visible x range is mapped onto sample indices through
|
||||
/// the series' own x (`Series::neighbours()`), never the `dx()` grid, and never the whole
|
||||
@@ -997,23 +1006,26 @@ class Plot {
|
||||
/// columns, with a one-device-pixel floor, and the state that picks its tint. Here and not in
|
||||
/// the renderer for `build_regions()`' reason -- one x mapping, the frame's own.
|
||||
void build_invalid_spans();
|
||||
/// Deposits one trace's visible window into the density grid: the same index window
|
||||
/// Deposits one trace's visible window into its own density grid: the same index window
|
||||
/// `Renderer::draw_traces()` maps -- `neighbours()` for the ends, the 64-spp gate for the LOD
|
||||
/// level -- gathered into `density_points_`, mapped to pixels through the transforms of the
|
||||
/// plot's last layout, and left with `deposit_density_segment()`. A trace with no series, no
|
||||
/// visible window or non-positive `dx()` deposits nothing.
|
||||
void deposit_density_trace(const Trace& trace);
|
||||
/// Deposits one already-mapped segment of a trace polyline: for every grid column the segment
|
||||
/// crosses, the rows it covers in that column, +1 each. The columns it also *clips*: a column
|
||||
/// whose slice of the segment lies entirely outside the axes is skipped, so a trace off the
|
||||
/// top of the plot deposits nothing rather than a band along the edge, and a segment with both
|
||||
/// ends past an edge keeps the part that is inside. A degenerate segment (`x0 == x1`, or a
|
||||
void deposit_density_trace(const Trace& trace, DensityGrid& grid);
|
||||
/// Deposits one already-mapped segment of a trace polyline into `grid`: for every grid column
|
||||
/// the segment crosses, the rows it covers in that column, +1 each. The columns it also *clips*:
|
||||
/// a column whose slice of the segment lies entirely outside the axes is skipped, so a trace off
|
||||
/// the top of the plot deposits nothing rather than a band along the edge, and a segment with
|
||||
/// both ends past an edge keeps the part that is inside. A degenerate segment (`x0 == x1`, or a
|
||||
/// non-finite coordinate) fills the single column it is in.
|
||||
void deposit_density_segment(f32 x0, f32 y0, f32 x1, f32 y1) noexcept;
|
||||
/// Grows `density_grid_` to the last layout's axes rect in device pixels, clearing it when the
|
||||
/// size moved (a *different* screen is a different image), and returns whether the grid is
|
||||
/// usable. The one place the grid is allocated.
|
||||
bool size_density_grid();
|
||||
void deposit_density_segment(f32 x0, f32 y0, f32 x1, f32 y1, DensityGrid& grid) noexcept;
|
||||
/// Grows `density_grids_` -- one grid per trace -- to the last layout's axes rect in device
|
||||
/// pixels, clearing a grid when the size moved (a *different* screen is a different image), and
|
||||
/// returns whether any grid is usable. The one place the grids are allocated.
|
||||
bool size_density_grids();
|
||||
/// The image an out-of-range trace reads (see `density_grid()`): a shared empty grid, so the
|
||||
/// accessor need not hand back a reference into nothing.
|
||||
static const DensityGrid& empty_density_grid() noexcept;
|
||||
/// Exact statistics of `region` over `series`, or an invalid result when the
|
||||
/// interval covers no sample. The one place the region arithmetic lives.
|
||||
static RegionStats evaluate_region_stats(const Region& region, const data::Series& series);
|
||||
@@ -1062,20 +1074,22 @@ class Plot {
|
||||
std::vector<Annotation> annotations_;
|
||||
std::vector<Region> regions_;
|
||||
std::vector<InvalidSpan> invalid_spans_;
|
||||
/// The phosphor density: its configuration, the grid it accumulates into, and the scratch the
|
||||
/// one visible window being deposited is gathered into. The grid is a member and not a local
|
||||
/// because it is the *display's* memory -- it outlives the frames it was drawn in, which is the
|
||||
/// whole point of accumulating into a screen-sized image rather than retaining traces.
|
||||
/// The phosphor density: its configuration, the grids it accumulates into, and the scratch the
|
||||
/// one visible window being deposited is gathered into. **One grid per trace** -- the image is
|
||||
/// screen-sized but each trace's is tinted with that trace's own colour (see `DensityConfig`), so
|
||||
/// the memory is `traces * columns * rows * 4` bytes and not one plot's worth. The vector tracks
|
||||
/// `traces_` and outlives the frames its images were drawn in, which is the whole point of
|
||||
/// accumulating into a screen-sized image rather than retaining traces.
|
||||
DensityConfig density_{};
|
||||
DensityGrid density_grid_{};
|
||||
std::vector<DensityGrid> density_grids_;
|
||||
/// Scratch for `deposit_density_trace()`: interleaved (x, y) of one gathered window, re-used
|
||||
/// across traces and frames so a steady-state accumulation allocates nothing. Bounded by the
|
||||
/// window, not the series, as `Renderer::gather_` is.
|
||||
std::vector<f32> density_points_;
|
||||
/// The per-sweep dedupe's stamps, one per grid **row**: the key of the last (trace, column) to
|
||||
/// deposit at that row, so one acquisition's own samples cannot count a cell twice (see
|
||||
/// `deposit_density_segment()`). One `u64` a row -- a few kB at any window -- and not one per
|
||||
/// cell, which would double the grid's memory for a comparison.
|
||||
/// The per-sweep dedupe's stamps, one per grid **row** and shared by every trace's deposit: the
|
||||
/// key of the last (trace, column) to deposit at that row, so one acquisition's own samples
|
||||
/// cannot count a cell twice (see `deposit_density_segment()`). One `u64` a row -- a few kB at
|
||||
/// any window -- and not one per cell, which would double a grid's memory for a comparison.
|
||||
std::vector<u64> density_stamp_;
|
||||
/// The sweep's own number, bumped once per trace deposited: the high half of every stamp key, so
|
||||
/// every trace of a plot, and every acquisition, deposits exactly once per cell it covers.
|
||||
|
||||
@@ -48,10 +48,11 @@ struct FrameStats {
|
||||
u32 staged_segments = 0; ///< line segments staged, all colour groups
|
||||
u32 staged_markers = 0; ///< marker shapes staged over the traces
|
||||
u32 draw_calls = 0; ///< draw calls issued, line and text
|
||||
/// The phosphor density's own cells, when one was drawn: `Plot::density_grid()`'s resolution,
|
||||
/// zero for a plot with the density off. It is what the *frame* drew, so a host reading the
|
||||
/// density's cost off a frame reads it here rather than off the grid, which says what is
|
||||
/// accumulated and not what was spent.
|
||||
/// The phosphor density's own cells, when one was drawn: the **sum** of every trace's
|
||||
/// `Plot::density_grid()` resolution, zero for a plot with the density off. It is what the
|
||||
/// *frame* drew, so a host reading the density's cost off a frame reads it here rather than off
|
||||
/// the grids, which say what is accumulated and not what was spent -- and it is the number that
|
||||
/// scales with the trace count, since the image is one grid per trace (see `DensityConfig`).
|
||||
u32 density_cells = 0;
|
||||
/// The bytes of that image the frame **uploaded**: `columns * rows` when the grid had changed
|
||||
/// since the last frame and 0 when it had not. It is the one field that separates the two
|
||||
@@ -524,18 +525,23 @@ class Renderer {
|
||||
void draw_plot(Plot& plot, const PlotLayout& layout);
|
||||
|
||||
void draw_grid(const Plot& plot);
|
||||
/// The plot's **phosphor density** (`Plot::set_density()`), when one is on: the accumulated
|
||||
/// image as one textured quad over the axes rect, in `Style::density`'s monochrome tint. Drawn
|
||||
/// over the grid and the invalid-span bands and **under the traces**, which is what makes it a
|
||||
/// record of where the traces have been rather than a second set of them.
|
||||
/// The plot's **phosphor density** (`Plot::set_density()`), when one is on: one textured quad per
|
||||
/// trace over the axes rect, each in **that trace's own colour** -- a saturated cell is the trace
|
||||
/// at its own brightness -- drawn over the grid and the invalid-span bands. The traces themselves
|
||||
/// are not drawn while it is on (`draw_traces()` returns), so this is the whole of the data ink
|
||||
/// and the newest acquisition is inside it.
|
||||
///
|
||||
/// The image reaches the GPU as an `GL_R8` texture, one texel per cell, converted here from the
|
||||
/// grid's counts -- the scale, the gamma and the tint are the plot's and this stage derives
|
||||
/// none of them, so the numbers a host reads back and the picture it sees are one derivation.
|
||||
/// A frame whose grid did not change (nothing accumulated, nothing decayed -- the steady state
|
||||
/// of a display that has stopped being swept) skips the conversion *and* the upload and costs
|
||||
/// the one draw call, which `FrameStats::density_upload_bytes` says per frame.
|
||||
/// Each trace's image reaches the GPU as an `GL_R8` texture, one texel per cell, converted here
|
||||
/// from the grid's counts -- the scale and the gamma are the plot's and this stage derives
|
||||
/// neither, so the numbers a host reads back and the picture it sees are one derivation. A frame
|
||||
/// whose grids did not change skips the conversion *and* the uploads and costs the draw calls,
|
||||
/// which `FrameStats::density_upload_bytes` says per frame.
|
||||
void draw_density(const Plot& plot, const PlotLayout& layout);
|
||||
/// One grid's quad: fills the shared `GL_R8` texture from `grid` (skipping when its revision and
|
||||
/// address are the ones already uploaded) and draws it over the axes rect in `tint`. The one
|
||||
/// place the density conversion lives, shared by every trace of `draw_density()`.
|
||||
void draw_density_grid(const DensityConfig& config, const DensityGrid& grid, Color tint,
|
||||
const PlotLayout& layout);
|
||||
/// `Plot::invalid_spans()`, as `PlotLayout::invalid_spans` measured them: one fill per span,
|
||||
/// in `style_.invalid_fill` or — for a span the host has marked repaired —
|
||||
/// `style_.invalid_repaired_fill`. Over the grid and under everything else inside the axes, so
|
||||
@@ -702,10 +708,12 @@ class Renderer {
|
||||
render::TextAtlas atlas_;
|
||||
render::LineProgram line_program_;
|
||||
render::TextProgram text_program_;
|
||||
/// The phosphor density's pipeline, its one attribute-less vertex array and the texture the
|
||||
/// grid is uploaded into. The texture is `GL_R8`, one texel per cell of the plot's grid, and
|
||||
/// re-created when that grid's size moves -- a resized plot is a differently sized image, and
|
||||
/// the plot has already dropped what the old one held.
|
||||
/// The phosphor density's pipeline, its one attribute-less vertex array and the texture a grid is
|
||||
/// uploaded into. The texture is `GL_R8`, one texel per cell of the plot's grid, and is re-filled
|
||||
/// for each trace in turn -- a trace's grid is drawn before the next trace's replaces it -- so a
|
||||
/// row of several traces still costs the one texture. It is re-created when the grid's size moves
|
||||
/// (a resized plot is a differently sized image, and the plot has already dropped what the old one
|
||||
/// held).
|
||||
render::DensityProgram density_program_;
|
||||
gl::VertexArray density_vao_;
|
||||
gl::Texture density_texture_;
|
||||
@@ -714,8 +722,12 @@ class Renderer {
|
||||
std::vector<u8> density_pixels_;
|
||||
u32 density_w_ = 0;
|
||||
u32 density_h_ = 0;
|
||||
/// The grid revision the texture currently holds. Compared per frame, so a frame whose grid
|
||||
/// did not move skips the conversion *and* the upload -- the pause in a persistence display.
|
||||
/// The grid the texture currently holds, by address, and its revision. Compared per frame and per
|
||||
/// trace, so a frame whose grids did not move skips the conversion *and* the upload -- the pause
|
||||
/// in a persistence display. The address is part of the key because the texture is shared: two
|
||||
/// traces of one plot have two grids with two revisions, and without the address a skip would
|
||||
/// draw one trace's image under another's colour.
|
||||
const DensityGrid* density_uploaded_grid_ = nullptr;
|
||||
u64 density_uploaded_revision_ = 0;
|
||||
render::LineVertexArray line_vao_;
|
||||
render::TextVertexArray text_vao_;
|
||||
|
||||
@@ -77,14 +77,6 @@ struct Style {
|
||||
/// different kinds of thing; and still above the threshold where the difference from *no* band
|
||||
/// is visible, which is the floor this alpha is chosen against.
|
||||
Color invalid_repaired_fill = Color::hex(0x8A94A6, 0.08f);
|
||||
/// The tint a plot's **phosphor density** is drawn in (`Plot::set_density()`): the grid's
|
||||
/// luminance comes from the accumulated counts, and this is the monochrome hue it is multiplied
|
||||
/// into -- the density says *how often*, so it must not also say *which signal*, and a trace
|
||||
/// colour here would make a ghost read as a trace that is not there. A near-white neutral, one
|
||||
/// step brighter than `invalid_fill`'s grey, because this is ink the plot *has* and a hole is
|
||||
/// ink it is missing. The alpha is the whole brightness scale's ceiling -- the shader multiplies
|
||||
/// it by each cell's own value -- so under 1 it puts a lid on how bright the display can get.
|
||||
Color density = Color::hex(0xDFF2FF, 0.85f);
|
||||
/// The crosshair's colour: its two lines and the readout panel's border (spec §15). Markers are
|
||||
/// deliberately *not* this colour -- they carry each trace's own, because a marker says *which*
|
||||
/// trace and the line says *where*.
|
||||
|
||||
Reference in new issue
Block a user