diff --git a/viewer/widgets/JFJochDiffractionImage.cpp b/viewer/widgets/JFJochDiffractionImage.cpp index 4aeaa3bb..e90fbfdf 100644 --- a/viewer/widgets/JFJochDiffractionImage.cpp +++ b/viewer/widgets/JFJochDiffractionImage.cpp @@ -12,57 +12,12 @@ #include #include +#include "JFJochSimpleImage.h" + // Constructor JFJochDiffractionImage::JFJochDiffractionImage(QWidget *parent) : JFJochImage(parent) {} - -void JFJochDiffractionImage::mousePressEvent(QMouseEvent *event) { - if (!scene() || !image) return; - - mouse_event_type = MouseEventType::None; - - bool mouse_inside_roi = false; - if (roiStartPos != roiEndPos) { - if (roi_type == RoiType::RoiBox) { - QRectF roiRect = QRectF(roiStartPos, roiEndPos).normalized(); - mouse_inside_roi = roiRect.contains(mapToScene(event->pos())); - } else { - auto curr_position = mapToScene(event->pos()); - auto delta = roiStartPos - curr_position; - auto radius_vec = roiStartPos - roiEndPos; - auto radius_2 = radius_vec.x() * radius_vec.x() + radius_vec.y() * radius_vec.y(); - mouse_inside_roi = (delta.x() * delta.x() + delta.y() * delta.y() < radius_2); - } - } - - if (event->button() == Qt::LeftButton) { - if (event->modifiers() & Qt::Modifier::SHIFT) { - setCursor(Qt::CursorShape::CrossCursor); - - if (mouse_inside_roi) { - roiEndPos = RoundPoint(mapToScene(event->pos())); - } else { - roiStartPos = RoundPoint(mapToScene(event->pos())); - roi_type = (event->modifiers() & Qt::Modifier::CTRL) ? RoiType::RoiCircle : RoiType::RoiBox; - } - mouse_event_type = MouseEventType::DrawingROI; - } else { - setCursor(Qt::ClosedHandCursor); // Change cursor to indicate panning - lastMousePos = event->pos(); - - if (mouse_inside_roi) - mouse_event_type = MouseEventType::MovingROI; - else - mouse_event_type = MouseEventType::Panning; - } - } - QGraphicsView::mousePressEvent(event); // Call base implementation -} - -// Handle panning while moving the mouse -void JFJochDiffractionImage::mouseMoveEvent(QMouseEvent *event) { - if (!scene() || !image) return; - +void JFJochDiffractionImage::mouseHover(QMouseEvent *event) { auto coord = mapToScene(event->pos()); if (image && (coord.x() >= 0) @@ -91,50 +46,9 @@ void JFJochDiffractionImage::mouseMoveEvent(QMouseEvent *event) { .arg(coord.y(), 0, 'f', 1) .arg(intensity_str) .arg(res, 0, 'f', 2)); - } else - emit writeStatusBar(""); + } else + emit writeStatusBar(""); - QPointF delta; - switch (mouse_event_type) { - case MouseEventType::Panning: - delta = coord - mapToScene(lastMousePos); - lastMousePos = event->pos(); - translate(delta.x(), delta.y()); // Adjust the view's pan - updateOverlay(); - break; - case MouseEventType::DrawingROI: - roiEndPos = RoundPoint(coord); - updateROI(); - break; - case MouseEventType::MovingROI: - delta = coord - mapToScene(lastMousePos); - roiStartPos += delta; - roiEndPos += delta; - lastMousePos = event->pos(); - updateROI(); - break; - default: - break; - } - QGraphicsView::mouseMoveEvent(event); -} - -void JFJochDiffractionImage::mouseReleaseEvent(QMouseEvent *event) { - if (!scene() || !image) return; - - setCursor(Qt::CursorShape::ArrowCursor); - - if (mouse_event_type == MouseEventType::MovingROI) { - if (roi_type == RoiType::RoiBox) { - roiStartPos = RoundPoint(roiStartPos); - roiEndPos = RoundPoint(roiEndPos); - } - updateROI(); - } - - mouse_event_type = MouseEventType::None; - - QGraphicsView::mouseReleaseEvent(event); } void JFJochDiffractionImage::LoadImageInternal() { diff --git a/viewer/widgets/JFJochDiffractionImage.h b/viewer/widgets/JFJochDiffractionImage.h index d4321259..675e5075 100644 --- a/viewer/widgets/JFJochDiffractionImage.h +++ b/viewer/widgets/JFJochDiffractionImage.h @@ -21,9 +21,6 @@ signals: void roiBoxUpdated(QRect box); void roiCircleUpdated(double x, double y, double radius); private: - void mousePressEvent(QMouseEvent *event) override; // Panning start - void mouseMoveEvent(QMouseEvent *event) override; // Panning interaction - void mouseReleaseEvent(QMouseEvent *event) override; void updateOverlay() override; void Redraw() override; @@ -50,6 +47,8 @@ private: bool highlight_ice_rings = true; float ice_ring_width_Q_recipA = 0.01; + + void mouseHover(QMouseEvent* event) override; public slots: void loadImage(std::shared_ptr image); void setAutoForeground(bool input); diff --git a/viewer/widgets/JFJochImage.cpp b/viewer/widgets/JFJochImage.cpp index 16439358..42a7d900 100644 --- a/viewer/widgets/JFJochImage.cpp +++ b/viewer/widgets/JFJochImage.cpp @@ -113,3 +113,155 @@ void JFJochImage::SetROICircle(double x, double y, double radius) { roiEndPos = roiBox.bottomRight(); Redraw(); } + +void JFJochImage::mousePressEvent(QMouseEvent *event) { + if (!scene()) return; + + if (event->button() == Qt::LeftButton) { + const QPointF scenePos = mapToScene(event->pos()); + bool mouse_inside_roi = false; + if (roiStartPos != roiEndPos) { + if (roi_type == RoiType::RoiBox) { + QRectF roiRect = QRectF(roiStartPos, roiEndPos).normalized(); + mouse_inside_roi = roiRect.contains(mapToScene(event->pos())); + } else { + auto curr_position = mapToScene(event->pos()); + auto delta = roiStartPos - curr_position; + auto radius_vec = roiStartPos - roiEndPos; + auto radius_2 = radius_vec.x() * radius_vec.x() + radius_vec.y() * radius_vec.y(); + mouse_inside_roi = (delta.x() * delta.x() + delta.y() * delta.y() < radius_2); + } + } + + // With Shift -> ROI interactions (draw/resize/move); without -> pan + if (event->modifiers() & Qt::Modifier::SHIFT) { + // Determine if we are over an existing ROI handle + active_handle_ = hitTestROIHandle(scenePos, 4.0 / std::sqrt(std::max(1e-4, scale_factor))); + if (active_handle_ != ResizeHandle::None && active_handle_ != ResizeHandle::Inside) { + mouse_event_type = MouseEventType::ResizingROI; + roiStartPos = roiBox.topLeft(); + roiEndPos = roiBox.bottomRight(); + setCursor(Qt::SizeAllCursor); + } else if (roiBox.contains(scenePos)) { + mouse_event_type = MouseEventType::MovingROI; + lastMousePos = event->pos(); + setCursor(Qt::ClosedHandCursor); + } else { + mouse_event_type = MouseEventType::DrawingROI; + roiStartPos = RoundPoint(scenePos); + roiEndPos = roiStartPos; + roi_type = (event->modifiers() & Qt::Modifier::CTRL) ? RoiType::RoiCircle : RoiType::RoiBox; + setCursor(Qt::CrossCursor); + } + } else { + mouse_event_type = MouseEventType::Panning; + setCursor(Qt::ClosedHandCursor); + lastMousePos = event->pos(); + } + } + + QGraphicsView::mousePressEvent(event); +} + +void JFJochImage::mouseMoveEvent(QMouseEvent *event) { + if (!scene()) return; + + const QPointF scenePos = mapToScene(event->pos()); + + switch (mouse_event_type) { + case MouseEventType::Panning: { + const QPointF delta = mapToScene(event->pos()) - mapToScene(lastMousePos); + lastMousePos = event->pos(); + translate(delta.x(), delta.y()); + updateOverlay(); + break; + } + case MouseEventType::DrawingROI: { + roiEndPos = RoundPoint(scenePos); + updateROI(); + break; + } + case MouseEventType::MovingROI: { + const QPointF delta = mapToScene(event->pos()) - mapToScene(lastMousePos); + lastMousePos = event->pos(); + roiBox.translate(delta); + updateROI(); + break; + } + case MouseEventType::ResizingROI: { + // Modify the corresponding edges based on active_handle_ + QRectF r = roiBox; + switch (active_handle_) { + case ResizeHandle::Left: r.setLeft(scenePos.x()); break; + case ResizeHandle::Right: r.setRight(scenePos.x()); break; + case ResizeHandle::Top: r.setTop(scenePos.y()); break; + case ResizeHandle::Bottom: r.setBottom(scenePos.y()); break; + case ResizeHandle::TopLeft: r.setTop(scenePos.y()); r.setLeft(scenePos.x()); break; + case ResizeHandle::TopRight: r.setTop(scenePos.y()); r.setRight(scenePos.x()); break; + case ResizeHandle::BottomLeft: r.setBottom(scenePos.y()); r.setLeft(scenePos.x()); break; + case ResizeHandle::BottomRight:r.setBottom(scenePos.y()); r.setRight(scenePos.x()); break; + default: break; + } + roiBox = r.normalized(); + updateROI(); + break; + } + case MouseEventType::None: { + mouseHover(event); + break; + } + } + + QGraphicsView::mouseMoveEvent(event); +} + +void JFJochImage::mouseReleaseEvent(QMouseEvent *event) { + if (!scene()) return; + + if (event->button() == Qt::LeftButton) { + if (mouse_event_type == MouseEventType::DrawingROI) { + roiEndPos = RoundPoint(mapToScene(event->pos())); + updateROI(); + } + mouse_event_type = MouseEventType::None; + active_handle_ = ResizeHandle::None; + setCursor(Qt::ArrowCursor); + } + + QGraphicsView::mouseReleaseEvent(event); + +} + +JFJochImage::ResizeHandle +JFJochImage::hitTestROIHandle(const QPointF& scenePos, qreal tol) const { + if (roiBox.isNull() || roiBox.width() <= 0 || roiBox.height() <= 0) + return ResizeHandle::None; + + const QRectF r = roiBox; + const QPointF tl = r.topLeft(); + const QPointF tr = r.topRight(); + const QPointF bl = r.bottomLeft(); + const QPointF br = r.bottomRight(); + + auto nearPt = [&](const QPointF& a, const QPointF& b, qreal t) { + return std::abs(a.x() - b.x()) <= t && std::abs(a.y() - b.y()) <= t; + }; + + if (nearPt(scenePos, tl, tol)) return ResizeHandle::TopLeft; + if (nearPt(scenePos, tr, tol)) return ResizeHandle::TopRight; + if (nearPt(scenePos, bl, tol)) return ResizeHandle::BottomLeft; + if (nearPt(scenePos, br, tol)) return ResizeHandle::BottomRight; + + // Edges + if (std::abs(scenePos.x() - r.left()) <= tol && scenePos.y() >= r.top() - tol && scenePos.y() <= r.bottom() + tol) + return ResizeHandle::Left; + if (std::abs(scenePos.x() - r.right()) <= tol && scenePos.y() >= r.top() - tol && scenePos.y() <= r.bottom() + tol) + return ResizeHandle::Right; + if (std::abs(scenePos.y() - r.top()) <= tol && scenePos.x() >= r.left() - tol && scenePos.x() <= r.right() + tol) + return ResizeHandle::Top; + if (std::abs(scenePos.y() - r.bottom()) <= tol && scenePos.x() >= r.left() - tol && scenePos.x() <= r.right() + tol) + return ResizeHandle::Bottom; + + if (r.contains(scenePos)) return ResizeHandle::Inside; + return ResizeHandle::None; +} diff --git a/viewer/widgets/JFJochImage.h b/viewer/widgets/JFJochImage.h index e83696e0..4740514a 100644 --- a/viewer/widgets/JFJochImage.h +++ b/viewer/widgets/JFJochImage.h @@ -17,7 +17,12 @@ class JFJochImage : public QGraphicsView { void wheelEvent(QWheelEvent* event) override; void resizeEvent(QResizeEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; protected: + virtual void mouseHover(QMouseEvent* event) = 0; + double scale_factor = 1.0; QColor feature_color = Qt::magenta; @@ -31,6 +36,11 @@ protected: MouseEventType mouse_event_type = MouseEventType::None; 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 hitTestROIHandle(const QPointF& scenePos, qreal tol = 3.0) const; + enum class RoiType {RoiBox, RoiCircle}; RoiType roi_type = RoiType::RoiBox; QPointF roiStartPos; diff --git a/viewer/widgets/JFJochSimpleImage.cpp b/viewer/widgets/JFJochSimpleImage.cpp index cdc826f3..8af8a989 100644 --- a/viewer/widgets/JFJochSimpleImage.cpp +++ b/viewer/widgets/JFJochSimpleImage.cpp @@ -48,122 +48,24 @@ void JFJochSimpleImage::Redraw() { } } -void JFJochSimpleImage::mousePressEvent(QMouseEvent* event) { - if (!scene() || !has_image_) return; - - if (event->button() == Qt::LeftButton) { - const QPointF scenePos = mapToScene(event->pos()); - - // With Shift -> ROI interactions (draw/resize/move); without -> pan - if (event->modifiers() & Qt::Modifier::SHIFT) { - // Determine if we are over an existing ROI handle - active_handle_ = hitTestROIHandle(scenePos, 4.0 / std::sqrt(std::max(1e-4, scale_factor))); - if (active_handle_ != ResizeHandle::None && active_handle_ != ResizeHandle::Inside) { - mouse_event_type = MouseEventType::ResizingROI; - roiStartPos = roiBox.topLeft(); - roiEndPos = roiBox.bottomRight(); - setCursor(Qt::SizeAllCursor); - } else if (roiBox.contains(scenePos)) { - mouse_event_type = MouseEventType::MovingROI; - lastMousePos = event->pos(); - setCursor(Qt::ClosedHandCursor); - } else { - mouse_event_type = MouseEventType::DrawingROI; - roiStartPos = RoundPoint(scenePos); - roiEndPos = roiStartPos; - setCursor(Qt::CrossCursor); - } - } else { - mouse_event_type = MouseEventType::Panning; - setCursor(Qt::ClosedHandCursor); - lastMousePos = event->pos(); - } - } - - QGraphicsView::mousePressEvent(event); -} - -void JFJochSimpleImage::mouseMoveEvent(QMouseEvent* event) { - if (!scene() || !has_image_) return; - +void JFJochSimpleImage::mouseHover(QMouseEvent *event) { const QPointF scenePos = mapToScene(event->pos()); - - switch (mouse_event_type) { - case MouseEventType::Panning: { - const QPointF delta = mapToScene(event->pos()) - mapToScene(lastMousePos); - lastMousePos = event->pos(); - translate(delta.x(), delta.y()); - updateOverlay(); - break; + // Hover feedback / status bar display + if ((scenePos.x() >= 0) + && (scenePos.x() < image_->image.GetWidth()) + && (scenePos.y() >= 0) + && (scenePos.y() < image_->image.GetHeight())) { + const auto ix = int(scenePos.x()); + const auto iy = int(scenePos.y()); + const auto idx = iy * int(image_->image.GetWidth()) + ix; + if (idx >= 0 && idx < int(image_values_.size())) + emit writeStatusBar(QString("x=%1 y=%2 I=%3") + .arg(scenePos.x(), 0, 'f', 1) + .arg(scenePos.y(), 0, 'f', 1) + .arg(image_values_[size_t(idx)]), 3000); + } else { + emit writeStatusBar("", 1000); } - case MouseEventType::DrawingROI: { - roiEndPos = RoundPoint(scenePos); - updateROI(); - break; - } - case MouseEventType::MovingROI: { - const QPointF delta = mapToScene(event->pos()) - mapToScene(lastMousePos); - lastMousePos = event->pos(); - roiBox.translate(delta); - updateROI(); - break; - } - case MouseEventType::ResizingROI: { - // Modify the corresponding edges based on active_handle_ - QRectF r = roiBox; - switch (active_handle_) { - case ResizeHandle::Left: r.setLeft(scenePos.x()); break; - case ResizeHandle::Right: r.setRight(scenePos.x()); break; - case ResizeHandle::Top: r.setTop(scenePos.y()); break; - case ResizeHandle::Bottom: r.setBottom(scenePos.y()); break; - case ResizeHandle::TopLeft: r.setTop(scenePos.y()); r.setLeft(scenePos.x()); break; - case ResizeHandle::TopRight: r.setTop(scenePos.y()); r.setRight(scenePos.x()); break; - case ResizeHandle::BottomLeft: r.setBottom(scenePos.y()); r.setLeft(scenePos.x()); break; - case ResizeHandle::BottomRight:r.setBottom(scenePos.y()); r.setRight(scenePos.x()); break; - default: break; - } - roiBox = r.normalized(); - updateROI(); - break; - } - case MouseEventType::None: { - // Hover feedback / status bar display - if ((scenePos.x() >= 0) - && (scenePos.x() < image_->image.GetWidth()) - && (scenePos.y() >= 0) - && (scenePos.y() < image_->image.GetHeight())) { - const auto ix = int(scenePos.x()); - const auto iy = int(scenePos.y()); - const auto idx = iy * int(image_->image.GetWidth()) + ix; - if (idx >= 0 && idx < int(image_values_.size())) - emit writeStatusBar(QString("x=%1 y=%2 I=%3") - .arg(scenePos.x(), 0, 'f', 1) - .arg(scenePos.y(), 0, 'f', 1) - .arg(image_values_[size_t(idx)]), 3000); - } else { - emit writeStatusBar("", 1000); - } - break; - } - } - - QGraphicsView::mouseMoveEvent(event); -} - -void JFJochSimpleImage::mouseReleaseEvent(QMouseEvent* event) { - if (!scene() || !has_image_) return; - - if (event->button() == Qt::LeftButton) { - if (mouse_event_type == MouseEventType::DrawingROI) { - roiEndPos = RoundPoint(mapToScene(event->pos())); - updateROI(); - } - mouse_event_type = MouseEventType::None; - active_handle_ = ResizeHandle::None; - setCursor(Qt::ArrowCursor); - } - - QGraphicsView::mouseReleaseEvent(event); } void JFJochSimpleImage::renderImage() { @@ -334,39 +236,6 @@ void JFJochSimpleImage::renderImage(QImage &qimg, const uint8_t *input) { } } -JFJochSimpleImage::ResizeHandle -JFJochSimpleImage::hitTestROIHandle(const QPointF& scenePos, qreal tol) const { - if (roiBox.isNull() || roiBox.width() <= 0 || roiBox.height() <= 0) - return ResizeHandle::None; - - const QRectF r = roiBox; - const QPointF tl = r.topLeft(); - const QPointF tr = r.topRight(); - const QPointF bl = r.bottomLeft(); - const QPointF br = r.bottomRight(); - - auto nearPt = [&](const QPointF& a, const QPointF& b, qreal t) { - return std::abs(a.x() - b.x()) <= t && std::abs(a.y() - b.y()) <= t; - }; - - if (nearPt(scenePos, tl, tol)) return ResizeHandle::TopLeft; - if (nearPt(scenePos, tr, tol)) return ResizeHandle::TopRight; - if (nearPt(scenePos, bl, tol)) return ResizeHandle::BottomLeft; - if (nearPt(scenePos, br, tol)) return ResizeHandle::BottomRight; - - // Edges - if (std::abs(scenePos.x() - r.left()) <= tol && scenePos.y() >= r.top() - tol && scenePos.y() <= r.bottom() + tol) - return ResizeHandle::Left; - if (std::abs(scenePos.x() - r.right()) <= tol && scenePos.y() >= r.top() - tol && scenePos.y() <= r.bottom() + tol) - return ResizeHandle::Right; - if (std::abs(scenePos.y() - r.top()) <= tol && scenePos.x() >= r.left() - tol && scenePos.x() <= r.right() + tol) - return ResizeHandle::Top; - if (std::abs(scenePos.y() - r.bottom()) <= tol && scenePos.x() >= r.left() - tol && scenePos.x() <= r.right() + tol) - return ResizeHandle::Bottom; - - if (r.contains(scenePos)) return ResizeHandle::Inside; - return ResizeHandle::None; -} void JFJochSimpleImage::updateROI() { // Normalize box if drawing diff --git a/viewer/widgets/JFJochSimpleImage.h b/viewer/widgets/JFJochSimpleImage.h index 97b852fd..b59b327b 100644 --- a/viewer/widgets/JFJochSimpleImage.h +++ b/viewer/widgets/JFJochSimpleImage.h @@ -23,11 +23,6 @@ public: void setImage(std::shared_ptr img); void clear(); -protected: - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - private: struct ROIStats { double min = std::numeric_limits::infinity(); @@ -60,14 +55,10 @@ private: template void renderImage(QImage &qimg, const uint8_t *input); - // Resizing which edge/corner - enum class ResizeHandle { None, Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight, Inside }; - ResizeHandle active_handle_ = ResizeHandle::None; - // Helpers - - ResizeHandle hitTestROIHandle(const QPointF& scenePos, qreal tol = 3.0) const; void updateROI() override; // normalize and request repaint bool auto_fgbg = true; + + void mouseHover(QMouseEvent* event) override; };