Files
Jungfraujoch/viewer/image_viewer/JFJochImage.h
T
leonarski_fandClaude Opus 4.8 142cb88aa8 viewer: shift/ctrl-drag creates list ROIs; remove the old scratch panel
The interactive shift-drag (box) / shift+ctrl-drag (circle) now creates a
new persistent ROI in the list instead of feeding the old single-ROI
scratch panel. The base emits a roiScratchDrawn hook on release; the
diffraction image turns the drawn shape into a named ROI committed via
SetROIDefinition.

The old JFJochViewerImageROIStatistics scratch panel and all its wiring
(box/circle configuration, single-ROI result, add/subtract user mask) are
removed from the side panel and window; the ROI list is now the single
source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 14:31:23 +02:00

151 lines
5.1 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <QGraphicsView>
#include <QMouseEvent>
#include <QTransform>
#include <QPointF>
#include "../../common/ColorScale.h"
#include "../../common/JFJochMessages.h"
// Q_DECLARE_METATYPE(ROIMessage)
class JFJochImage : public QGraphicsView {
Q_OBJECT
bool m_adjustForegroundWithWheel = false;
// Viewport-lock support: guard prevents the emit<->apply ping-pong between
// two linked views, and the helper broadcasts the current transform+center.
bool m_applyingViewport = false;
void emitViewportChanged();
void DrawROI();
virtual void addCustomOverlay();
void updateROI();
void writePixelLabels();
void wheelEvent(QWheelEvent* event) override;
void resizeEvent(QResizeEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
ROIMessage accumulateROI(int64_t xmin, int64_t xmax, int64_t ymin, int64_t ymax,
const std::function<bool(int64_t,int64_t)>& inside);
protected:
virtual void beforeOverlayCleared();
bool show_saturation = false;
bool auto_bg = false;
bool auto_fg = false;
bool hdr_mode = false;
double scale_factor = 1.0;
size_t W = 0, H = 0;
size_t prev_W = 0, prev_H = 0;
// Track initial fit state and last image size
bool initial_fit_done_ = false;
QColor feature_color = Qt::magenta;
// Decimal places for non-integer per-pixel value labels. Float images are
// unreadable with many decimals, so subclasses can lower this.
int label_decimals_ = 3;
float foreground = 10.0;
float background = 0.0;
ColorScale color_scale;
std::vector<rgb> image_rgb;
std::vector<float> image_fp;
QPixmap pixmap;
QImage qimg_buffer_; // reusable image buffer — avoids 64MB alloc per frame
// Persistent pixmap item — never destroyed/recreated on overlay update
QGraphicsPixmapItem *pixmap_item_ = nullptr;
// Overlay items managed separately
QList<QGraphicsItem *> overlay_items_;
// Helper: add an overlay item to the scene and track it for selective removal
void addOverlayItem(QGraphicsItem *item);
enum class MouseEventType {None, Panning, DrawingROI, MovingROI, ResizingROI, EditingExternalROI};
MouseEventType mouse_event_type = MouseEventType::None;
// Hooks for editing a named, persistent ROI drawn by a subclass (the base only
// owns the mouse events; the subclass knows the loaded ROIs). roiEditPress returns
// true to claim the gesture for ROI editing.
virtual bool roiEditPress(const QPointF &scenePos) { return false; }
virtual void roiEditMove(const QPointF &scenePos) {}
virtual void roiEditRelease() {}
virtual void roiScratchDrawn() {} // a shift/ctrl-drag finished drawing a new box/circle
QPoint lastMousePos; // To track panning movement
// Resizing which edge/corner
enum class ResizeHandle { None, Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight, Inside };
ResizeHandle active_handle_ = ResizeHandle::None;
ResizeHandle hover_handle_ = ResizeHandle::None;
enum class RoiType {RoiBox, RoiCircle};
RoiType roi_type = RoiType::RoiBox;
QPointF roiStartPos;
QPointF roiEndPos;
QRectF roiBox;
static QPointF RoundPoint(const QPointF& p);
virtual void mouseHover(QMouseEvent* event) = 0;
ResizeHandle hitTestROIHandle(const QPointF& scenePos, qreal tol = 3.0) const;
void updateOverlay();
void GeneratePixmap();
void Redraw();
void CalcROI();
// Invalidate pixmap_item_ and overlay tracking after scene()->clear()
void resetScenePointers();
// Perform initial fit-to-view (shorter direction), once per image size
void fitToViewShorterSideOnce();
QSize last_fit_viewport_ = {};
void copyImageToClipboard();
void copyImageWithOverlayToClipboard();
void clearROIInternal();
signals:
void autoForegroundChanged(bool input);
void foregroundChanged(float v);
void backgroundChanged(float v);
void writeStatusBar(QString string, int timeout_ms = 0);
void roiBoxUpdated(QRect box);
void roiCircleUpdated(double x, double y, double radius);
void roiCalculated(ROIMessage &output);
void viewportChanged(QTransform transform, QPointF center);
void hoverScenePos(QPointF scenePos);
private slots:
void onScroll(int value);
public slots:
void setFeatureColor(QColor input);
void setColorMap(int color_map);
virtual void changeForeground(float val);
void changeBackground(float val);
void SetROIBox(QRect box);
void SetROICircle(double x, double y, double radius);
void centerOnSpot(QPointF point);
void applyViewport(QTransform transform, QPointF center);
void fitToView();
void adjustForeground(bool input);
void setZoom(double input);
public:
explicit JFJochImage(QWidget *parent = nullptr);
double GetScaleFactor() const;
};