From 4bb3d44983ea5bb31b79f21f98e26bd129636a8e Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Wed, 2 Sep 2026 09:16:32 +0200 Subject: [PATCH] viewer: label the merge plot over the range it is drawn on The merge-statistics window asked for an absolute y-axis - CC1/2 and CCref on 0..100, everything else from 0 - by setting the range on the chart's value axis after JFJochSimpleChartView::UpdateData had already built the chart. The visible tick labels are a separate QCategoryAxis whose entries UpdateData had generated from the range of the data, and those entries were not rebuilt. So the plot was drawn over 0..100 while the labels down its left edge covered only 91.5..100 and crowded into the top tenth; the numbers matching the drawn range appeared only on the right-hand grid axis, which the same call had made visible. CC1/2 showed it worst because its range is the narrowest, but every metric was affected. Give UpdateData the range instead, so the ticks and both axes come from one number. The side-panel azimuthal-integration chart passes no range and renders pixel-identically. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N --- docs/CHANGELOG.md | 1 + viewer/charts/JFJochSimpleChartView.cpp | 10 +++++++++- viewer/charts/JFJochSimpleChartView.h | 8 +++++++- viewer/windows/JFJochMergeStatsWindow.cpp | 16 +++++----------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d03a19dfe..24bf18328 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -24,6 +24,7 @@ * `jfjoch_broker` sends `direct_beam_x`/`direct_beam_y` on the CBOR start message. * `jfjoch_viewer` reads PILATUS miniCBF sweeps natively, and draws grid scan cells in the proportion of the scan steps. * The rugnux manual is reorganised into task pages with a run overview, and gains worked phenix / REFMAC5 / POINTLESS-AIMLESS / careless examples. +* `jfjoch_viewer` labels the merge-statistics plot over the range the axis is drawn on, so the CC1/2 curve is no longer read against tick labels covering only part of it. ### 1.0.0-rc.165 This is an UNSTABLE release. It includes many experimental features, as well as many AI generated fixes. We recommend using rc.152 for production use. diff --git a/viewer/charts/JFJochSimpleChartView.cpp b/viewer/charts/JFJochSimpleChartView.cpp index e5f7524fe..d0651a540 100644 --- a/viewer/charts/JFJochSimpleChartView.cpp +++ b/viewer/charts/JFJochSimpleChartView.cpp @@ -22,7 +22,8 @@ JFJochSimpleChartView::JFJochSimpleChartView(QWidget *parent) } void JFJochSimpleChartView::UpdateData(const std::vector &in_x, const std::vector &in_y, - QString legend_x, QString legend_y, bool in_one_over_d) { + QString legend_x, QString legend_y, bool in_one_over_d, + std::optional> y_range) { one_over_d = in_one_over_d; x = in_x; y = in_y; @@ -71,6 +72,13 @@ void JFJochSimpleChartView::UpdateData(const std::vector &in_x, const std ymax += eps; } + // A range asked for by the caller replaces the one taken from the data. Everything below - both + // axes and the tick labels - is built from ymin/ymax, so the drawn range stays the labelled one. + if (y_range && y_range->second > y_range->first) { + ymin = y_range->first; + ymax = y_range->second; + } + // Hidden value axis (left): range + grid auto *axYvalue = new QValueAxis(); axYvalue->setTitleText(legend_y); diff --git a/viewer/charts/JFJochSimpleChartView.h b/viewer/charts/JFJochSimpleChartView.h index fcc10a839..4479be9d8 100644 --- a/viewer/charts/JFJochSimpleChartView.h +++ b/viewer/charts/JFJochSimpleChartView.h @@ -3,6 +3,9 @@ #pragma once +#include +#include + #include #include #include @@ -24,8 +27,11 @@ protected: public: JFJochSimpleChartView(QWidget *parent = nullptr); + // y_range fixes the value range of the Y axis instead of taking it from the data - the tick + // labels are built from it, so a caller must not change the range afterwards. virtual void UpdateData(const std::vector &in_x, const std::vector &in_y, - QString legend_x, QString legend_y, bool one_over_d); + QString legend_x, QString legend_y, bool one_over_d, + std::optional> y_range = std::nullopt); void ClearData(); }; diff --git a/viewer/windows/JFJochMergeStatsWindow.cpp b/viewer/windows/JFJochMergeStatsWindow.cpp index 520d6b964..8e77a26ac 100644 --- a/viewer/windows/JFJochMergeStatsWindow.cpp +++ b/viewer/windows/JFJochMergeStatsWindow.cpp @@ -322,23 +322,17 @@ void JFJochMergeStatsWindow::updatePlot() { if (std::isfinite(v)) dmax = std::max(dmax, v); } - chart_->UpdateData(x, y, "Resolution [Å]", metric_->currentText(), true); - // Absolute y-axis so positions are comparable: CC1/2 + CCref are 0..100, completeness 0..(>=100), - // multiplicity / R-meas / I-sigma start at 0. Show the labels (UpdateData hides them by default). - double ymin = 0.0, ymax = 1.0; + // multiplicity / R-meas / I-sigma start at 0. It has to be given to UpdateData rather than set + // on the axis afterwards, or the tick labels still describe the range of the data. + double ymax = 1.0; switch (metric) { case 0: case 5: ymax = 100.0; break; // CC1/2, CCref case 3: ymax = std::max(100.0, dmax * 1.05); break; // Completeness default: ymax = dmax > 0.0 ? dmax * 1.05 : 1.0; break; // R-meas, I/sigma, multiplicity } - const auto axes = chart_->chart()->axes(Qt::Vertical); - if (!axes.isEmpty()) { - if (auto *vax = qobject_cast(axes.first())) { - vax->setRange(ymin, ymax); - vax->setLabelsVisible(true); - } - } + chart_->UpdateData(x, y, "Resolution [Å]", metric_->currentText(), true, + std::make_pair(0.0, ymax)); } void JFJochMergeStatsWindow::buildTable() {