crosshair: axis badges, clustered cursor labels, and the shared-y horizontal line
CI / debug (tests + all adapters) (push) Canceled after 0s
CI / coverage (floor + measured table) (push) Canceled after 0s
CI / asan (AddressSanitizer) (push) Canceled after 0s

- 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:
Martino Ferrari committed 2026-11-05 17:58:49 +01:00
1 parent 2cb31cbb91
commit 1d5b286611
9 files changed
+469 -49

No files matched your search

+38
View File
@@ -1292,6 +1292,44 @@ PLOT_TEST(figure_row_y_cells_share_a_range_after_a_gesture) {
PLOT_CHECK(!figure.row_y_auto(0u));
}
/// The horizontal line is one *value*, and a split row's cells share one y scale: the line
/// spans the whole row instead of stopping at the divider between two cells that read the same
/// numbers. The rows of the figure still do not share y, so no other row gets one.
PLOT_TEST(figure_crosshair_horizontal_spans_a_split_rows_cells) {
plot::Figure figure = split_figure({2, 1});
figure.set_share_row_y(true);
figure.layout();
// The pointer in the *second* cell of the split row, half-way down it.
const plot::Rect cell1 = figure.layout().cells[1];
const plot::Transform ty = figure.plot(1u).y_transform(0u);
const plot::f32 y = static_cast<plot::f32>(ty.to_data_y(cell1.y + 0.5f * cell1.h));
figure.set_crosshair(50.0, 1u, y, true);
figure.layout();
const plot::CrosshairLayout& left = figure.plot(0u).layout().crosshair;
const plot::CrosshairLayout& right = figure.plot(1u).layout().crosshair;
const plot::CrosshairLayout& other = figure.plot(2u).layout().crosshair;
// Both cells of the row carry the line, at one screen row: they share the y scale, so the
// row's own y is the same value's height in each.
PLOT_CHECK(left.horizontal.w > 0.0f);
PLOT_CHECK(right.horizontal.w > 0.0f);
PLOT_CHECK_NEAR(left.horizontal.y + 0.5f * left.horizontal.h,
right.horizontal.y + 0.5f * right.horizontal.h, 0.75);
// The unsplit row keeps the shared-x vertical line and no horizontal one -- a line there
// would mean a value on a scale that row does not have.
PLOT_CHECK(other.vertical.w > 0.0f);
PLOT_CHECK(other.horizontal.h == 0.0f);
// The spread does not touch the x: both cells keep the one shared column.
PLOT_CHECK(left.vertical.w > 0.0f);
PLOT_CHECK(right.vertical.w > 0.0f);
// Without `set_share_row_y` the cells are separate scales again, and the line is the
// hovered cell's alone.
figure.set_share_row_y(false);
figure.set_crosshair(50.0, 1u, y, true);
figure.layout();
PLOT_CHECK(figure.plot(1u).layout().crosshair.horizontal.w > 0.0f);
PLOT_CHECK(figure.plot(0u).layout().crosshair.horizontal.h == 0.0f);
}
PLOT_TEST(figure_row_y_auto_fits_the_union_and_ignores_empty_cells) {
plot::Figure figure = split_figure({3});
for (plot::u32 i = 0; i < 3u; ++i) figure.plot(i).set_y_auto_scale(0u, true);