From abc1d1c079cf4a001360eeb7017a4cd013e2b8ff Mon Sep 17 00:00:00 2001 From: leonarski_f Date: Fri, 19 Jun 2026 15:08:59 +0200 Subject: [PATCH] viewer: add/subtract the selected ROI to/from the user mask Restore the ROI-to-mask action on the new list: "Add to mask" and "Subtract from mask" buttons rasterise the selected ROI into the user mask (set or clear), through the same UpdateUserMask_i path. The ROI is evaluated with per-pixel resolution and phi from the geometry, so box, circle and azimuthal (sector) ROIs all map correctly. Co-Authored-By: Claude Opus 4.8 --- viewer/JFJochImageReadingWorker.cpp | 31 ++++++++++++++++++++++++++ viewer/JFJochImageReadingWorker.h | 1 + viewer/JFJochViewerSidePanel.cpp | 1 + viewer/JFJochViewerSidePanel.h | 1 + viewer/JFJochViewerWindow.cpp | 2 ++ viewer/widgets/JFJochViewerROIList.cpp | 13 +++++++++++ viewer/widgets/JFJochViewerROIList.h | 1 + 7 files changed, 50 insertions(+) diff --git a/viewer/JFJochImageReadingWorker.cpp b/viewer/JFJochImageReadingWorker.cpp index 75aae6f8..d9cca04b 100644 --- a/viewer/JFJochImageReadingWorker.cpp +++ b/viewer/JFJochImageReadingWorker.cpp @@ -584,6 +584,37 @@ void JFJochImageReadingWorker::SetROIDefinition(const ROIDefinition &rois) { SetROIDefinition_i(rois); } +void JFJochImageReadingWorker::MaskFromSelectedROI(QString name, bool add) { + QMutexLocker locker(&m); + if (!current_image_ptr) + return; + + const auto &exp = current_image_ptr->Dataset().experiment; + const auto rois = exp.ROI().GetROIDefinition(); + const std::string n = name.toStdString(); + + const ROIElement *elem = nullptr; + for (const auto &b : rois.boxes) if (b.GetName() == n) { elem = &b; break; } + if (!elem) for (const auto &c : rois.circles) if (c.GetName() == n) { elem = &c; break; } + if (!elem) for (const auto &a : rois.azimuthal) if (a.GetName() == n) { elem = &a; break; } + if (!elem) + return; + + auto user_mask = current_image_ptr->Dataset().pixel_mask.GetUserMask(); + auto geom = exp.GetDiffractionGeometry(); + const int64_t width = exp.GetXPixelsNum(); + const int64_t height = exp.GetYPixelsNum(); + for (int64_t y = 0; y < height; y++) { + for (int64_t x = 0; x < width; x++) { + const float res = geom.PxlToRes(x, y); + const float phi = geom.Phi_rad(x, y) * 180.0f / static_cast(PI); + if (elem->CheckROI(x, y, res, phi)) + user_mask[x + y * width] = add ? 1 : 0; + } + } + UpdateUserMask_i(user_mask); +} + void JFJochImageReadingWorker::DownloadROIsFromServer() { QMutexLocker locker(&m); if (!http_mode) diff --git a/viewer/JFJochImageReadingWorker.h b/viewer/JFJochImageReadingWorker.h index 85d2f8dd..6ab4e7e4 100644 --- a/viewer/JFJochImageReadingWorker.h +++ b/viewer/JFJochImageReadingWorker.h @@ -166,6 +166,7 @@ public slots: void SetROIDefinition(const ROIDefinition &rois); void DownloadROIsFromServer(); void UploadROIsToServer(); + void MaskFromSelectedROI(QString name, bool add); // rasterise an ROI into the user mask void SaveUserMaskTIFF(QString filename); void LoadUserMaskTIFF(QString filename, bool replace); diff --git a/viewer/JFJochViewerSidePanel.cpp b/viewer/JFJochViewerSidePanel.cpp index 2426a6cb..85220768 100644 --- a/viewer/JFJochViewerSidePanel.cpp +++ b/viewer/JFJochViewerSidePanel.cpp @@ -132,6 +132,7 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent) connect(roi_list, &JFJochViewerROIList::selectedROIChanged, this, &JFJochViewerSidePanel::selectedROIChanged); connect(roi_list, &JFJochViewerROIList::downloadROIs, this, &JFJochViewerSidePanel::downloadROIs); connect(roi_list, &JFJochViewerROIList::uploadROIs, this, &JFJochViewerSidePanel::uploadROIs); + connect(roi_list, &JFJochViewerROIList::maskFromROI, this, &JFJochViewerSidePanel::maskFromROI); layout->addWidget(new TitleLabel("Data analysis", this)); diff --git a/viewer/JFJochViewerSidePanel.h b/viewer/JFJochViewerSidePanel.h index 549d9d8a..4aec0662 100644 --- a/viewer/JFJochViewerSidePanel.h +++ b/viewer/JFJochViewerSidePanel.h @@ -35,6 +35,7 @@ signals: void selectedROIChanged(QString name); void downloadROIs(); void uploadROIs(); + void maskFromROI(QString name, bool add); void findBeamCenter(const UnitCell &input, bool guess); void analyze(); diff --git a/viewer/JFJochViewerWindow.cpp b/viewer/JFJochViewerWindow.cpp index 90f04c95..7ec2f070 100644 --- a/viewer/JFJochViewerWindow.cpp +++ b/viewer/JFJochViewerWindow.cpp @@ -201,6 +201,8 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString reading_worker, &JFJochImageReadingWorker::DownloadROIsFromServer); connect(side_panel, &JFJochViewerSidePanel::uploadROIs, reading_worker, &JFJochImageReadingWorker::UploadROIsToServer); + connect(side_panel, &JFJochViewerSidePanel::maskFromROI, + reading_worker, &JFJochImageReadingWorker::MaskFromSelectedROI); connect(menuBar, &JFJochViewerMenu::clearUserMaskSelected, reading_worker, &JFJochImageReadingWorker::ClearUserMask); diff --git a/viewer/widgets/JFJochViewerROIList.cpp b/viewer/widgets/JFJochViewerROIList.cpp index 178e0b95..1b648995 100644 --- a/viewer/widgets/JFJochViewerROIList.cpp +++ b/viewer/widgets/JFJochViewerROIList.cpp @@ -55,6 +55,19 @@ JFJochViewerROIList::JFJochViewerROIList(QWidget *parent) : QWidget(parent) { } layout->addLayout(grid); + auto *mask = new QHBoxLayout(); + auto *mask_add = new QPushButton("Add to mask", this); + auto *mask_sub = new QPushButton("Subtract from mask", this); + mask->addWidget(mask_add); + mask->addWidget(mask_sub); + layout->addLayout(mask); + connect(mask_add, &QPushButton::clicked, [this] { + if (!SelectedName().isEmpty()) emit maskFromROI(SelectedName(), true); + }); + connect(mask_sub, &QPushButton::clicked, [this] { + if (!SelectedName().isEmpty()) emit maskFromROI(SelectedName(), false); + }); + result_ = new JFJochViewerROIResult(this); layout->addWidget(result_); } diff --git a/viewer/widgets/JFJochViewerROIList.h b/viewer/widgets/JFJochViewerROIList.h index ae468ae2..c1138b13 100644 --- a/viewer/widgets/JFJochViewerROIList.h +++ b/viewer/widgets/JFJochViewerROIList.h @@ -52,4 +52,5 @@ signals: void selectedROIChanged(QString name); void downloadROIs(); // fetch ROIs from the connected broker void uploadROIs(); // push ROIs to the connected broker + void maskFromROI(QString name, bool add); // add/subtract the selected ROI to/from the user mask };