viewer: draw grid scan cells in the proportion of the scan steps

A grid scan with, say, a 20 um step in x and a 5 um step in y was drawn as a
square grid, so the picture had nothing to do with the shape of the area that
was scanned. The cells now carry that proportion.

The anisotropy lives only in the view transform: JFJochImage gains a
pixel_aspect_ (the drawn height of one pixel in units of its width, 1.0 for a
detector image), the initial fit fits the image as if it were that much taller
and then puts the factor back into the transform, and the grid view sets it
from |step_y / step_x|. Scene coordinates stay one unit per cell, so the mouse
mapping, the selected-image box, the pixel labels and the ROI code need no
change and the uniform wheel zoom keeps the proportion.

Measured on an 8x4 grid driven offscreen: m11/m22 comes out 4.000 for steps
20/5 um, 0.250 for 5/20, 12.333 for 37/3 and exactly 1.000 - the old
behaviour - for a square step. Every cell centre still maps back through
mapToScene to its own cell in all four cases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hcoh6VNrmSswjfeMeDQqWP
This commit is contained in:
2026-08-31 13:38:51 +02:00
co-authored by Claude Opus 5
parent 756c3a22bb
commit d189faeec7
4 changed files with 19 additions and 3 deletions
+1
View File
@@ -10,6 +10,7 @@
* `rugnux` writes the unmerged MTZ by default, and a P1 merge beside it, so a wrong space group can be re-merged without reprocessing.
* Significant improvements to symmetry handling in `rugnux`: the lattice, the point group, the setting and the systematic absences.
* The `rugnux` report gives the resolution the CC1/2 fit reached, beside the range the reflections were written to.
* The `jfjoch_viewer` grid scan view draws the cells in the proportion of the scan steps, so the map has the shape of the scanned area.
### 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.
@@ -25,6 +25,9 @@ void JFJochGridScanImage::loadData(const std::vector<float> &data, const GridSca
W = settings.GetGridSizeX_step();
H = settings.GetGridSizeY_step();
// Draw a cell in the proportion of the area it covers, so a scan with a coarse x step and a
// fine y step looks like the sample region it was taken over, not like a square grid.
pixel_aspect_ = std::fabs(settings.GetGridStepY_um() / settings.GetGridStepX_um());
image_fp = settings.Rearrange(data, NAN);
std::vector<int64_t> indices(image_fp.size());
+8 -3
View File
@@ -954,7 +954,7 @@ void JFJochImage::fitToView() {
}
void JFJochImage::fitToViewShorterSideOnce() {
if (initial_fit_done_ && prev_H == H && prev_W == W)
if (initial_fit_done_ && prev_H == H && prev_W == W && prev_aspect_ == pixel_aspect_)
return;
@@ -963,6 +963,7 @@ void JFJochImage::fitToViewShorterSideOnce() {
prev_H = H;
prev_W = W;
prev_aspect_ = pixel_aspect_;
// Guard against tiny or zero viewport (happens before layout settles)
const QSize vp = viewport()->size();
@@ -976,7 +977,11 @@ void JFJochImage::fitToViewShorterSideOnce() {
setTransformationAnchor(QGraphicsView::AnchorViewCenter);
setTransform(QTransform());
fitInView(QRectF(0, 0, static_cast<qreal>(W), static_cast<qreal>(H)), Qt::KeepAspectRatio);
// Non-square pixels are fitted as if the image were as tall as they draw it, and the extra
// height is then put back into the transform: the image still fits, and the uniform zooms
// that follow keep the proportion.
fitInView(QRectF(0, 0, static_cast<qreal>(W), static_cast<qreal>(H) * pixel_aspect_), Qt::KeepAspectRatio);
scale(1.0, pixel_aspect_);
scale_factor = transform().m11();
centerOn(QPointF(static_cast<qreal>(W) * 0.5, static_cast<qreal>(H) * 0.5));
@@ -998,7 +1003,7 @@ void JFJochImage::setZoom(double input) {
scale_factor = input;
if (!scene())
return;
setTransform(QTransform::fromScale(input, input));
setTransform(QTransform::fromScale(input, input * pixel_aspect_));
updateOverlay();
emitViewportChanged();
}
+7
View File
@@ -109,6 +109,13 @@ protected:
size_t W = 0, H = 0;
size_t prev_W = 0, prev_H = 0;
// Height of one pixel as it is drawn, in units of its width. 1.0 for a detector image, where
// pixels are square; a grid scan whose x and y steps differ sets it so that a cell is drawn
// in the proportion of the area it covers. Only the view transform is anisotropic - scene
// coordinates stay one unit per pixel everywhere.
qreal pixel_aspect_ = 1.0;
qreal prev_aspect_ = 1.0;
// Track initial fit state and last image size
bool initial_fit_done_ = false;