Drawing an ROI only means something where there are detector counts to accumulate. Gate the gesture on a virtual AllowROI(), true only for JFJochDiffractionImage: shift-drag, the resize handles, the hover cursor and the "Clear ROI" context entry now do nothing in the azimuthal, grid-scan and calibration views, which cannot report anything about a box anyway. The statistics move out of the base class into the diffraction view and read the int32 image directly, so no float copy of the detector image is built for them either. With the labels already converted, image_fp is now untouched by the diffraction view, and the lazy EnsurePixelValues machinery it needed is gone. image_fp stays as the base's representation for the views whose data really is float: the azimuthal profile, the grid-scan 1/sigma^2 map, and the calibration viewer's eight source types. Removed with it: the ROI readouts in the calibration and 2D azimuthal windows, which were the only two consumers of roiCalculated -- the diffraction view emitted it and nothing listened. Nothing surfaces ROI statistics now; the pixel-mask case wants rectangles counting excluded pixels and deserves its own design. JFJochViewerROIResult is still used by the side-panel ROI list, so the widget stays. Verified in the GUI: shift-drag in the diffraction view still draws the box, turns it into a named ROI and runs the statistics; fit-view panel remains pixel-identical to the pre-series baseline. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
174 lines
5.3 KiB
C++
174 lines
5.3 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "JFJochGridScanImage.h"
|
|
|
|
#include <QGraphicsItem>
|
|
|
|
JFJochGridScanImage::JFJochGridScanImage(QWidget *parent) : JFJochImage(parent) {}
|
|
|
|
void JFJochGridScanImage::clear() {
|
|
W = 0;
|
|
H = 0;
|
|
this->settings = std::nullopt;
|
|
if (scene())
|
|
scene()->clear();
|
|
resetScenePointers();
|
|
}
|
|
|
|
void JFJochGridScanImage::loadData(const std::vector<float> &data, const GridScanSettings &settings, bool in_one_over_d2) {
|
|
if (data.empty()) {
|
|
clear();
|
|
return;
|
|
}
|
|
this->settings = settings;
|
|
|
|
W = settings.GetGridSizeX_step();
|
|
H = settings.GetGridSizeY_step();
|
|
image_fp = settings.Rearrange(data, NAN);
|
|
|
|
std::vector<int64_t> indices(image_fp.size());
|
|
std::iota(indices.begin(), indices.end(), 0);
|
|
|
|
image_index = settings.Rearrange(indices, -1);
|
|
|
|
one_over_d2 = in_one_over_d2;
|
|
if (one_over_d2) {
|
|
for (int i = 0; i < image_fp.size(); i++)
|
|
if (!std::isnan(image_fp[i]))
|
|
image_fp[i] = 1.0f / (image_fp[i] * image_fp[i]);
|
|
}
|
|
|
|
float minv = std::numeric_limits<float>::infinity();
|
|
float maxv = -std::numeric_limits<float>::infinity();
|
|
for (float v : image_fp) {
|
|
if (std::isfinite(v)) {
|
|
if (v < minv) minv = v;
|
|
if (v > maxv) maxv = v;
|
|
}
|
|
}
|
|
if (!std::isfinite(minv) || !std::isfinite(maxv)) {
|
|
minv = 0.0f;
|
|
maxv = 1.0f;
|
|
}
|
|
if (!(maxv > minv)) {
|
|
maxv = minv + 1.0f;
|
|
}
|
|
background = minv;
|
|
foreground = maxv;
|
|
|
|
RenderImage();
|
|
Redraw();
|
|
}
|
|
|
|
void JFJochGridScanImage::mouseHover(const QPointF &pt, Qt::KeyboardModifiers modifiers) {
|
|
// Map mouse position to image pixel if inside bounds
|
|
if (W == 0 || H == 0 || image_index.empty())
|
|
return;
|
|
|
|
// Convert view coordinates to image pixel by truncation
|
|
int x = static_cast<int>(pt.x());
|
|
int y = static_cast<int>(pt.y());
|
|
|
|
if (x < 0 || x >= W || y < 0 || y >= H)
|
|
return;
|
|
|
|
int idx = y * W + x;
|
|
if (idx < 0 || idx >= static_cast<int>(image_index.size()))
|
|
return;
|
|
int64_t image_id = image_index[idx];
|
|
if (image_id >= 0) {
|
|
if (modifiers & Qt::ShiftModifier)
|
|
emit imageSelected(image_id);
|
|
|
|
if (one_over_d2) {
|
|
if (std::isnan(image_fp[idx]))
|
|
emit writeStatusBar(QString("Image %1 x %2 y %3 no resolution estimation")
|
|
.arg(image_id)
|
|
.arg(x)
|
|
.arg(y),
|
|
6000);
|
|
else
|
|
emit writeStatusBar(QString("Image %1 x %2 y %3 d = %4 Å")
|
|
.arg(image_id)
|
|
.arg(x)
|
|
.arg(y)
|
|
.arg(QString::number(1 / std::sqrt(image_fp[idx]), 'f', 2)),
|
|
6000);
|
|
} else {
|
|
emit writeStatusBar(QString("Image %1 x %2 y %3 value %4")
|
|
.arg(image_id)
|
|
.arg(x)
|
|
.arg(y)
|
|
.arg(QString::number(image_fp[idx], 'f', 3)),
|
|
6000);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JFJochGridScanImage::loadImage(QMouseEvent *event) {
|
|
// Map mouse position to image pixel if inside bounds
|
|
if (W == 0 || H == 0 || image_index.empty())
|
|
return;
|
|
|
|
const QPointF pt = mapToScene(event->pos());
|
|
|
|
// Convert view coordinates to image pixel by truncation
|
|
int x = static_cast<int>(pt.x());
|
|
int y = static_cast<int>(pt.y());
|
|
|
|
if (x < 0 || x >= W || y < 0 || y >= H)
|
|
return;
|
|
|
|
int idx = y * W + x;
|
|
if (idx < 0 || idx >= static_cast<int>(image_index.size()))
|
|
return;
|
|
int64_t image_id = image_index[idx];
|
|
if (image_id >= 0)
|
|
emit imageSelected(image_id);
|
|
}
|
|
|
|
void JFJochGridScanImage::mouseDoubleClickEvent(QMouseEvent *event) {
|
|
// Map mouse position to image pixel if inside bounds
|
|
if (W == 0 || H == 0 || image_index.empty())
|
|
return;
|
|
|
|
const QPointF pt = mapToScene(event->pos());
|
|
|
|
// Convert view coordinates to image pixel by truncation
|
|
int x = static_cast<int>(pt.x());
|
|
int y = static_cast<int>(pt.y());
|
|
|
|
if (x < 0 || x >= W || y < 0 || y >= H)
|
|
return;
|
|
|
|
int idx = y * W + x;
|
|
if (idx < 0 || idx >= static_cast<int>(image_index.size()))
|
|
return;
|
|
int64_t image_id = image_index[idx];
|
|
if (image_id >= 0)
|
|
emit imageSelected(image_id);
|
|
JFJochImage::mouseDoubleClickEvent(event);
|
|
}
|
|
|
|
void JFJochGridScanImage::setImage(int64_t val) {
|
|
if (settings) {
|
|
current_image_W = settings->GetElementPosX_step(val);
|
|
current_image_H = settings->GetElementPosY_step(val);
|
|
} else {
|
|
current_image_W = -1;
|
|
current_image_H = -1;
|
|
}
|
|
Redraw();
|
|
}
|
|
|
|
void JFJochGridScanImage::addCustomOverlay() {
|
|
if (current_image_W < 0 || current_image_H < 0 || current_image_W >= W || current_image_H >= H)
|
|
return;
|
|
|
|
QPen pen(feature_color, 3);
|
|
pen.setCosmetic(true);
|
|
|
|
auto *rect = scene()->addRect(current_image_W, current_image_H, 1, 1, pen);
|
|
addOverlayItem(rect);
|
|
} |