feat(plot): the figure crosshair marks the hovered row, and the stroke is thin, dashed and configurable

This commit is contained in:
Martino Ferrari committed 2026-09-28 20:33:35 +02:00
1 parent f8a364e963
commit ea70cf4fca
1 file changed
+32
+32
View File
@@ -528,6 +528,14 @@ int main() {
/// The scripted region sequence, off unless `PLOT_DEMO_REGIONS` asks for it. Same shape /// The scripted region sequence, off unless `PLOT_DEMO_REGIONS` asks for it. Same shape
/// as the follow script: a driven run whose verdict is carried out in its `failed`. /// as the follow script: a driven run whose verdict is carried out in its `failed`.
RegionScript region_script = RegionScript::from_env(); RegionScript region_script = RegionScript::from_env();
/// `PLOT_DEMO_CROSSHAIR=1`: park a pointer at the centre of the data area every frame, so the
/// controller places the two-line crosshair (spec §17's thin, dashed hover instrument) and a
/// headless screenshot has its ink to measure. Off by default, like the two scripts above.
bool crosshair_demo = false;
if (const char* v = std::getenv("PLOT_DEMO_CROSSHAIR");
v != nullptr && v[0] != '\0' && v[0] != '0') {
crosshair_demo = true;
}
// The region the frame carries: a direct-manipulation primitive with the statistics // The region the frame carries: a direct-manipulation primitive with the statistics
// readout that is the point of one, so the demo reaches it without a script as well as // readout that is the point of one, so the demo reaches it without a script as well as
@@ -589,6 +597,19 @@ int main() {
} }
} }
if (crosshair_demo) {
// A pointer parked at the centre of the data area, injected through the controller so the
// *real* placement path runs: `update_crosshair()` reads both data coordinates off the
// pointer's pixel position and the plot draws the full cross there.
const plot::Rect a = std::as_const(graph).layout().axes;
plot::input::PointerEvent e;
e.x = a.x + 0.5f * a.w;
e.y = a.y + 0.45f * a.h;
e.mods = 0;
e.down = false;
host.controller().on_pointer(e);
}
meter.elapsed += dt; meter.elapsed += dt;
meter.frames += 1; meter.frames += 1;
meter.frame_ms += 0.1 * (dt * 1.0e3 - meter.frame_ms); // EMA meter.frame_ms += 0.1 * (dt * 1.0e3 - meter.frame_ms); // EMA
@@ -674,6 +695,17 @@ int main() {
const plot::Renderer* r = host.renderer(); const plot::Renderer* r = host.renderer();
const plot::FrameStats stats = r != nullptr ? r->stats() : plot::FrameStats{}; const plot::FrameStats stats = r != nullptr ? r->stats() : plot::FrameStats{};
const plot::Rect axes = std::as_const(p).layout().axes; const plot::Rect axes = std::as_const(p).layout().axes;
// The crosshair's geometry as the last frame laid it out (spec §17): the stroke and dash the
// layout resolved and the two stroke boxes -- what the screenshot's ink is read against.
{
const plot::CrosshairLayout& ch = std::as_const(p).layout().crosshair;
std::printf("[glfw_demo] crosshair active %d, stroke %.2f device px, dash %.2f/%.2f,"
" vertical box w %.2f at x %.1f, horizontal box h %.2f at y %.1f\n",
ch.active ? 1 : 0, static_cast<plot::f64>(ch.stroke),
static_cast<plot::f64>(ch.dash.x), static_cast<plot::f64>(ch.dash.y),
static_cast<plot::f64>(ch.vertical.w), static_cast<plot::f64>(ch.vertical.x),
static_cast<plot::f64>(ch.horizontal.h), static_cast<plot::f64>(ch.horizontal.y));
}
const f64 spp = const f64 spp =
axes.w > 0.0f ? static_cast<f64>(stats.visible_samples) / static_cast<f64>(axes.w) : 0.0; axes.w > 0.0f ? static_cast<f64>(stats.visible_samples) / static_cast<f64>(axes.w) : 0.0;
const f64 dense = r != nullptr ? static_cast<f64>(r->style().dense_threshold_spp) : 0.0; const f64 dense = r != nullptr ? static_cast<f64>(r->style().dense_threshold_spp) : 0.0;