crosshair: axis badges, clustered cursor labels, and the shared-y horizontal line
- Axis badges: the y value in the y label strip and the x time in the x strip, each a full-opacity panel in the crosshair's colour, **centred on the line it names**. The tick text is flushed before the fill -- the over-text batch is flushed after every line batch, so without that flush the label behind printed through the panel (the "transparent backplate" defect). - Cursor labels: collected across the cursor loop and clustered at its end, so two marks in one column share a panel with one line each instead of overprinting, and a label stays on its own mark's row. - Figure: a split row's cells share one y (`set_share_row_y`), so the horizontal line is drawn in every cell of the hovered row -- one value, one scale, spanning the row -- and still in no other row. Tests: `figure_crosshair_horizontal_spans_a_split_rows_cells`, plus the render-smoke additions for the badge placement/flush and the clustered labels.
This commit is contained in:
1 parent
2cb31cbb91
commit
1d5b286611
9 files changed
+469
-49
No files matched your search
@@ -91,6 +91,14 @@ struct Crosshair {
|
||||
/// On by default, and free to leave on: a plot with no visible trace has no rows to
|
||||
/// measure and the panel is then not placed at all.
|
||||
bool show_readout = true;
|
||||
/// Write the two values the crosshair's lines meet the axes at as small badges in the
|
||||
/// label strips -- the y value beside the y ticks, the x value beside the x ticks, each
|
||||
/// in its axis' own format. On by default: it is the axis reading an oscilloscope puts
|
||||
/// where the eye already is, and a host that does not want it turns it off
|
||||
/// (`Plot::set_crosshair_axis_labels()`). The badges are placed only when the strip they
|
||||
/// belong to exists and the line they belong to is drawn (a vertical-only crosshair has
|
||||
/// no y to badge, and a row whose x strip went to a neighbour badges nothing).
|
||||
bool axis_labels = true;
|
||||
/// The data x of the vertical line, on x axis 0.
|
||||
f64 x = 0.0;
|
||||
/// The data y of the horizontal line, on y axis 0. Meaningless for a vertical-only
|
||||
@@ -277,6 +285,17 @@ struct CrosshairLayout {
|
||||
/// `Crosshair::show_readout` is off, or when the panel could not be placed inside the
|
||||
/// axes at all.
|
||||
Rect panel{};
|
||||
/// The two axis badges, when `Crosshair::axis_labels` is on and the crosshair is active:
|
||||
/// the y value in the **y label strip**, right-aligned on the axis line at the horizontal
|
||||
/// line's height, and the x value in the **x label strip**, centred on the vertical
|
||||
/// line's column -- each in its axis' own format, so a badge reads exactly like the tick
|
||||
/// labels beside it. Zero-sized (and the text empty) when the flag is off, the crosshair
|
||||
/// is inactive, the line they belong to is not drawn, their strip does not exist (a row
|
||||
/// whose x strip went to the row below), or there is no text backend to measure them.
|
||||
Rect y_axis_badge{};
|
||||
std::string y_axis_text;
|
||||
Rect x_axis_badge{};
|
||||
std::string x_axis_text;
|
||||
/// One row per visible trace, in trace order -- see `CrosshairRow`. Present whether or
|
||||
/// not the panel could list them, so the markers are drawn even in a plot too small to
|
||||
/// hold a panel.
|
||||
|
||||
@@ -247,9 +247,12 @@ class Figure {
|
||||
|
||||
/// Places the crosshair with the pointer's **plot** and that plot's local y (spec §17): the
|
||||
/// shared x goes to every plot -- vertical lines everywhere, as the plain overload does -- and
|
||||
/// the horizontal line is drawn **only in `hovered_row`** -- the **flattened plot index**, not
|
||||
/// the figure-row index; they differ when a row holds several cells -- where its y is a value of
|
||||
/// *that* plot's own scale. It is the fix for the reported defect -- a figure showing no horizontal
|
||||
/// the horizontal line is drawn in `hovered_row` -- the **flattened plot index**, not the
|
||||
/// figure-row index; they differ when a row holds several cells -- where its y is a value of
|
||||
/// *that* plot's own scale. **When the row is a split whose cells share one y
|
||||
/// (`set_share_row_y`), the line is drawn in every cell of that row**: it is one value on one
|
||||
/// scale, so stopping it at the divider would hide the number the neighbouring cell is read
|
||||
/// against. Rows never share y with each other, so no other row gets a line. It is the fix for the reported defect -- a figure showing no horizontal
|
||||
/// line at all -- without inventing a figure-wide y, which would mean a different value in
|
||||
/// every row: the line appears where the pointer is, and no row ever shows another row's y.
|
||||
///
|
||||
|
||||
@@ -557,6 +557,14 @@ class Plot {
|
||||
/// belongs to. An id that names no cursor is ignored, like every other setter here.
|
||||
void set_cursor_color(u32 id, Color color);
|
||||
|
||||
/// A cursor's caption (see `Cursor::label`): a vertical marker's badge text -- a horizontal
|
||||
/// ruler writes its own value and needs no caption to be readable. Rendered on a
|
||||
/// backplate, and labels whose backplates meet are merged into one panel with one line per
|
||||
/// label (see the renderer), which is what keeps a marker pair half a division apart
|
||||
/// readable instead of a smear of two texts over one column. An empty string removes the
|
||||
/// caption; an id that names no cursor is ignored, like every other setter here.
|
||||
void set_cursor_label(u32 id, std::string label);
|
||||
|
||||
/// A cursor's stroke: its logical width (`<= 0` for the style's default) and its dash
|
||||
/// (`dash.off <= 0` for solid). The instrument's own, like its colour -- see `Cursor::width`.
|
||||
void set_cursor_stroke(u32 id, f32 width, Vec2 dash);
|
||||
@@ -888,6 +896,14 @@ class Plot {
|
||||
/// **not** part of `set_crosshair()`, so placing a hover cannot silently change it.
|
||||
void set_crosshair_readout(bool on) noexcept { crosshair_.show_readout = on; }
|
||||
|
||||
/// Writes or clears the two **axis badges** (see `Crosshair::axis_labels`): the y value in
|
||||
/// the y label strip and the x value in the x label strip, at the heights and columns the
|
||||
/// crosshair's lines meet the axes at. Takes effect on the next `layout()`. **Not** part of
|
||||
/// `set_crosshair()`, for the same reason the readout flag is not.
|
||||
void set_crosshair_axis_labels(bool on) noexcept { crosshair_.axis_labels = on; }
|
||||
/// The axis-badge flag, as set.
|
||||
bool crosshair_axis_labels() const noexcept { return crosshair_.axis_labels; }
|
||||
|
||||
/// Overrides this plot's crosshair stroke and dash, in logical pixels, without touching the
|
||||
/// global style every other plot draws with (spec §17): each field left negative in `s`
|
||||
/// defers to the matching `Style::crosshair_*` metric, so `CrosshairStyle{}` (the default)
|
||||
|
||||
@@ -95,6 +95,24 @@ struct FrameStats {
|
||||
/// a guard -- and the frame reports it (see `last_frame_had_gl_error()`).
|
||||
///
|
||||
/// Not thread-safe, and confined to the thread that owns the current context.
|
||||
|
||||
/// One cursor label's measured text and placed badge: what `Renderer::collect_cursor_label()`
|
||||
/// builds and `Renderer::draw_clustered_cursor_labels()` places and draws. A rendering
|
||||
/// detail of the cursor labels' backplates, declared at namespace scope only because a
|
||||
/// private nested type cannot be named in an out-of-class member definition's signature.
|
||||
struct CursorLabel {
|
||||
std::string text; ///< the one line the backplate carries
|
||||
Color border{}; ///< the panel's border: the cursor's own colour
|
||||
f32 anchor = 0.0f; ///< the coordinate the label belongs to (x for a marker, y for a ruler)
|
||||
f32 w = 0.0f; ///< the measured text width
|
||||
bool horizontal = false;
|
||||
/// The placed pen and backplate: centred on the anchor across the mark, nudged onto a
|
||||
/// free row when a close neighbour's badge is in the way, and clustered into one panel
|
||||
/// by `draw_clustered_cursor_labels()`.
|
||||
Vec2 pen{};
|
||||
Rect box{};
|
||||
};
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer() noexcept = default;
|
||||
@@ -621,13 +639,22 @@ class Renderer {
|
||||
/// the clip, and a range maximum that undershoots the edge by a ulp is not mistaken for
|
||||
/// off the plot.
|
||||
void draw_cursors(const Plot& plot, const PlotLayout& layout);
|
||||
/// One cursor's label: the caller's caption for either orientation, and for a horizontal
|
||||
/// ruler the value itself, written in its y axis' own tick format (`Axis::format_value()`)
|
||||
/// -- the number a ruler exists to be read off. A vertical cursor's caption starts at the
|
||||
/// top of the axes beside its marker; a ruler's text starts at the axes' left edge on its
|
||||
/// own row, with the caption first on that line when there is one.
|
||||
void draw_cursor_label(const Plot& plot, const PlotLayout& layout, const Cursor& cursor,
|
||||
f32 at);
|
||||
/// One cursor's label, measured rather than drawn: a horizontal ruler's line is the value
|
||||
/// itself, written in its y axis' own tick format (`Axis::format_value()`) -- the number a
|
||||
/// ruler exists to be read off -- with the caption first on the line when there is one; a
|
||||
/// vertical cursor's line is its caption, beside the marker at the top of the axes. The
|
||||
/// label is then placed (centred on its mark, stacked clear of close neighbours) and drawn
|
||||
/// clustered (`draw_clustered_cursor_labels()`), which is what keeps a pair of readout
|
||||
/// marks half a division apart readable: two texts over one column is a smear, one panel
|
||||
/// with two lines is a readout.
|
||||
void collect_cursor_label(const Plot& plot, const PlotLayout& layout, const Cursor& cursor,
|
||||
f32 at, std::vector<CursorLabel>& labels);
|
||||
/// Draws the placed labels' backplates -- one panel per cluster of meeting boxes, every
|
||||
/// line at its own placed pen -- and stages the lines. Clustering is what keeps a pair of
|
||||
/// readout marks half a division apart readable: two texts over one column is a smear, one
|
||||
/// panel with two lines is a readout.
|
||||
void draw_clustered_cursor_labels(const PlotLayout& layout,
|
||||
const std::vector<CursorLabel>& labels);
|
||||
/// The regions: the fill, one bound per edge, the optional label. Every rect comes
|
||||
/// from `PlotLayout::regions` -- the same geometry the controller hit-tests -- so
|
||||
/// a region is drawn where it can be grabbed, and neither side derives the other's
|
||||
@@ -656,8 +683,12 @@ class Renderer {
|
||||
/// outermost pixels the fill covers (the crisp-hairline rule -- a border drawn on the measured
|
||||
/// rect's fractional edges is lit half-heartedly, and a frame bright on three sides and dim on the
|
||||
/// fourth reads as a mistake). One helper, two callers: the border's colour is the only thing a
|
||||
/// legend panel and a readout panel do not share.
|
||||
void draw_readout_panel(const Rect& panel, Color border);
|
||||
/// legend panel and a readout panel do not share. `background_alpha` scales the frame's own
|
||||
/// background under the border: the legend and the crosshair's readout keep the shared
|
||||
/// translucent backplate (what they cover stays faintly visible), while a *badge* -- a
|
||||
/// cursor's value box, the crosshair's axis badges -- is read against whatever it sits on
|
||||
/// (ticks, a noisy trace) and is drawn opaque.
|
||||
void draw_readout_panel(const Rect& panel, Color border, float background_alpha);
|
||||
/// One trace's colour swatch in `box`, opaque whatever the trace's own alpha is: a swatch drawn at
|
||||
/// a trace's 15% would be the least legible thing in the frame, and the swatch is the one element
|
||||
/// that says which colour an entry or a readout row *is*. The legend's entries and the crosshair's
|
||||
@@ -687,6 +718,12 @@ class Renderer {
|
||||
/// Drawn after every other interior pass -- the legend included -- because it is what the
|
||||
/// pointer is on: a readout the legend covered would be the feature failing at its one job.
|
||||
void draw_crosshair_readout(const Plot& plot, const PlotLayout& layout);
|
||||
/// The crosshair's **axis badges** (`CrosshairLayout::y_axis_badge`/`x_axis_badge`): the
|
||||
/// values the two lines meet the axes at, each on the shared readout backplate with its
|
||||
/// border in the crosshair's colour, drawn over the tick labels they sit among. Called
|
||||
/// from `draw_plot()` after the label strips are drawn, which is the only order in which a
|
||||
/// badge in a strip can sit on the ticks and still be read.
|
||||
void draw_crosshair_axis_badges(const PlotLayout& layout);
|
||||
|
||||
/// The host's box-zoom frame (`PlotLayout::selection`): a fill at `selection_fill` under a border at
|
||||
/// `selection_edge`, or nothing at all when nothing was swept. Drawn last of the interior passes, so
|
||||
|
||||
Reference in new issue
Block a user