From 1d9dab9d8e4b951c61164fedf1e7b5c682dfcae6 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 21 Mar 2025 09:38:01 +0100 Subject: [PATCH 01/14] Work on integration new two-dimensional renderer --- src/NavigationAction.cpp | 2 - src/ScatterplotPlugin.cpp | 4 +- src/ScatterplotWidget.cpp | 171 +++++++------------------------------- src/ScatterplotWidget.h | 28 +------ 4 files changed, 38 insertions(+), 167 deletions(-) diff --git a/src/NavigationAction.cpp b/src/NavigationAction.cpp index 3406e5a..f07d6fb 100644 --- a/src/NavigationAction.cpp +++ b/src/NavigationAction.cpp @@ -49,8 +49,6 @@ void NavigationAction::initialize(ScatterplotWidget* scatterplotWidget) _scatterplotWidget = scatterplotWidget; _scatterplotWidget->addAction(&_zoomDataExtentsAction); - - connect(&_zoomDataExtentsAction, &TriggerAction::triggered, _scatterplotWidget, &ScatterplotWidget::resetView); } void NavigationAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 1c7bab9..6a192ab 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -360,7 +360,7 @@ void ScatterplotPlugin::selectPoints() auto& pixelSelectionTool = _scatterPlotWidget->getPixelSelectionTool(); // Only proceed with a valid points position dataset and when the pixel selection tool is active - if (!_positionDataset.isValid() || !pixelSelectionTool.isActive() || _scatterPlotWidget->isNavigating() || !pixelSelectionTool.isEnabled()) + if (!_positionDataset.isValid() || !pixelSelectionTool.isActive() || _scatterPlotWidget->_pointRenderer.getNavigator().isNavigating() || !pixelSelectionTool.isEnabled()) return; auto selectionAreaImage = pixelSelectionTool.getAreaPixmap().toImage(); @@ -450,7 +450,7 @@ void ScatterplotPlugin::samplePoints() { auto& samplerPixelSelectionTool = _scatterPlotWidget->getSamplerPixelSelectionTool(); - if (!_positionDataset.isValid() || !samplerPixelSelectionTool.isActive() || _scatterPlotWidget->isNavigating() || !samplerPixelSelectionTool.isEnabled()) + if (!_positionDataset.isValid() || !samplerPixelSelectionTool.isActive() || _scatterPlotWidget->_pointRenderer.getNavigator().isNavigating() || !samplerPixelSelectionTool.isEnabled()) return; auto selectionAreaImage = samplerPixelSelectionTool.getAreaPixmap().toImage(); diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index f95a50c..20136b2 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -49,8 +49,6 @@ namespace } ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : - QOpenGLWidget(), - _pointRenderer(), _densityRenderer(DensityRenderer::RenderMode::DENSITY), _isInitialized(false), _renderMode(SCATTERPLOT), @@ -59,21 +57,17 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : _widgetSizeInfo(), _dataRectangleAction(this, "Data rectangle"), _navigationAction(this, "Navigation"), - _colorMapImage(), _pixelSelectionTool(this), _samplerPixelSelectionTool(this), _pixelRatio(1.0), - _mousePositions(), - _isNavigating(false), _weightDensity(false), _parentPlugin(parentPlugin) { setContextMenuPolicy(Qt::CustomContextMenu); setAcceptDrops(true); setMouseTracking(true); - setFocusPolicy(Qt::ClickFocus); + //setFocusPolicy(Qt::ClickFocus); grabGesture(Qt::PinchGesture); - //setAttribute(Qt::WA_TranslucentBackground); installEventFilter(this); _navigationAction.initialize(this); @@ -146,159 +140,58 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : update(); }); -} -bool ScatterplotWidget::event(QEvent* event) -{ - if (!event) - return QOpenGLWidget::event(event); + connect(&_navigationAction.getZoomDataExtentsAction(), &TriggerAction::triggered, this, [this]() -> void { + _pointRenderer.getNavigator().resetView(); + }); - auto setIsNavigating = [this](bool isNavigating) -> void { - _isNavigating = isNavigating; + connect(&_pointRenderer.getNavigator(), &Navigator2D::isNavigatingChanged, this, [this](bool isNavigating) -> void { _pixelSelectionTool.setEnabled(!isNavigating); - if (isNavigating) { + + if (isNavigating) { _samplerPixelSelectionTool.setEnabled(false); } - else if (_parentPlugin) { // reset to UI-setting + else if (_parentPlugin) { _samplerPixelSelectionTool.setEnabled(_parentPlugin->getSamplerAction().getEnabledAction().isChecked()); } + }); - }; - - // Set navigation flag on Alt press/release - if (event->type() == QEvent::KeyRelease) { - if (const auto* keyEvent = static_cast(event)) { - if (keyEvent->key() == Qt::Key_Alt) { - setIsNavigating(false); - } - } - - } - else if (event->type() == QEvent::KeyPress) { - if (const auto* keyEvent = static_cast(event)) { - if (keyEvent->key() == Qt::Key_Alt) { - setIsNavigating(true); - } - } + connect(&_pointRenderer, &Renderer2D::zoomRectangleChanged, this, [this]() -> void { update(); }); - } + //auto setIsNavigating = [this](bool isNavigating) -> void { + // _isNavigating = isNavigating; + // _pixelSelectionTool.setEnabled(!isNavigating); + // if (isNavigating) { + // _samplerPixelSelectionTool.setEnabled(false); + // } + // else if (_parentPlugin) { // reset to UI-setting + // _samplerPixelSelectionTool.setEnabled(_parentPlugin->getSamplerAction().getEnabledAction().isChecked()); + // } - // Interactions when Alt is pressed - if (isInitialized() && QGuiApplication::keyboardModifiers() == Qt::AltModifier) { + // }; - switch (event->type()) - { - case QEvent::Wheel: - { - // Scroll to zoom - if (auto* wheelEvent = static_cast(event)) - zoomAround(wheelEvent->position().toPoint(), wheelEvent->angleDelta().x() / 1200.f); - - break; - } - - case QEvent::MouseButtonPress: - { - if (const auto* mouseEvent = static_cast(event)) - { - if(mouseEvent->button() == Qt::MiddleButton) - resetView(); - - // Navigation - if (mouseEvent->buttons() == Qt::LeftButton) - { - setIsNavigating(true); - setCursor(Qt::ClosedHandCursor); - _mousePositions << mouseEvent->pos(); - update(); - } - } - - break; - } - - case QEvent::MouseButtonRelease: - { - setIsNavigating(false); - setCursor(Qt::ArrowCursor); - _mousePositions.clear(); - update(); - - break; - } - - case QEvent::MouseMove: - { - if (const auto* mouseEvent = static_cast(event)) - { - _mousePositions << mouseEvent->pos(); + _pointRenderer.getNavigator().initialize(this); +} - if (mouseEvent->buttons() == Qt::LeftButton && _mousePositions.size() >= 2) - { - const auto& previousMousePosition = _mousePositions[_mousePositions.size() - 2]; - const auto& currentMousePosition = _mousePositions[_mousePositions.size() - 1]; - const auto panVector = currentMousePosition - previousMousePosition; +bool ScatterplotWidget::event(QEvent* event) +{ + if (!event) + return QOpenGLWidget::event(event); - panBy(panVector); - } - } + // Need this to receive key release events in the two-dimensional renderer + if (event->type() == QEvent::ShortcutOverride) { + const auto keyEvent = dynamic_cast(event); - break; - } + if (keyEvent->key() == Qt::Key_Alt) { + event->accept(); + return QOpenGLWidget::event(event); } - } return QOpenGLWidget::event(event); } -void ScatterplotWidget::resetView() -{ - _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); -} - -void ScatterplotWidget::panBy(const QPointF& to) -{ - auto& zoomRectangleAction = _navigationAction.getZoomRectangleAction(); - - const auto moveBy = QPointF(to.x() / _widgetSizeInfo.width * zoomRectangleAction.getWidth() * _widgetSizeInfo.ratioWidth * -1.f, - to.y() / _widgetSizeInfo.height * zoomRectangleAction.getHeight() * _widgetSizeInfo.ratioHeight); - - zoomRectangleAction.translateBy({ moveBy.x(), moveBy.y() }); - - update(); -} - -void ScatterplotWidget::zoomAround(const QPointF& at, float factor) -{ - auto& zoomRectangleAction = _navigationAction.getZoomRectangleAction(); - - // the widget might have a different aspect ratio than the square opengl viewport - const auto offsetBounds = QPointF(zoomRectangleAction.getWidth() * (0.5f * (1 - _widgetSizeInfo.ratioWidth)), - zoomRectangleAction.getHeight() * (0.5f * (1 - _widgetSizeInfo.ratioHeight)) * -1.f); - - const auto originBounds = QPointF(zoomRectangleAction.getLeft(), zoomRectangleAction.getTop()); - - // translate mouse point in widget to mouse point in bounds coordinates - const auto atTransformed = QPointF(at.x() / _widgetSizeInfo.width * zoomRectangleAction.getWidth() * _widgetSizeInfo.ratioWidth, - at.y() / _widgetSizeInfo.height * zoomRectangleAction.getHeight() * _widgetSizeInfo.ratioHeight * -1.f); - - const auto atInBounds = originBounds + offsetBounds + atTransformed; - - // ensure mouse position is the same after zooming - const auto currentBoundCenter = zoomRectangleAction.getCenter(); - - float moveMouseX = (atInBounds.x() - currentBoundCenter.first) * factor; - float moveMouseY = (atInBounds.y() - currentBoundCenter.second) * factor; - - // zoom and move view - zoomRectangleAction.translateBy({ moveMouseX, moveMouseY }); - zoomRectangleAction.expandBy(-1.f * factor); - - update(); -} - bool ScatterplotWidget::isInitialized() const { return _isInitialized; diff --git a/src/ScatterplotWidget.h b/src/ScatterplotWidget.h index 1b9545c..f687492 100644 --- a/src/ScatterplotWidget.h +++ b/src/ScatterplotWidget.h @@ -123,25 +123,8 @@ class ScatterplotWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_C return _dataRectangleAction.getBounds(); } - /* - mv::Bounds getZoomBounds() const { - return _zoomBounds; - } - - void setZoomBounds(const mv::Bounds& newBounds) { - _zoomBounds = newBounds; - _pointRenderer.setBounds(_zoomBounds); - emit zoomBoundsChanged(_zoomBounds); - update(); - } - */ - NavigationAction& getNavigationAction() { return _navigationAction; } - bool isNavigating() const { - return _isNavigating; - } - mv::Vector3f getColorMapRange() const; void setColorMapRange(const float& min, const float& max); @@ -258,10 +241,6 @@ class ScatterplotWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_C bool event(QEvent* event) override; - void zoomAround(const QPointF& at, float factor); - void panBy(const QPointF& to); - void resetView(); - public: // Const access to renderers const PointRenderer& getPointRenderer() const { @@ -308,9 +287,11 @@ public slots: private slots: void updatePixelRatio(); -private: +protected: PointRenderer _pointRenderer; /** For rendering point data as points */ DensityRenderer _densityRenderer; /** For rendering point data as a density plot */ + +private: bool _isInitialized; /** Boolean determining whether the widget it properly initialized or not */ RenderMode _renderMode; /** Current render mode */ QColor _backgroundColor; /** Background color */ @@ -322,11 +303,10 @@ private slots: PixelSelectionTool _pixelSelectionTool; /** 2D pixel selection tool */ PixelSelectionTool _samplerPixelSelectionTool; /** 2D pixel selection tool */ float _pixelRatio; /** Current pixel ratio */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Boolean determining whether view navigation is currently taking place or not */ bool _weightDensity; /** Use point scalar sizes to weight density */ mv::plugin::ViewPlugin* _parentPlugin = nullptr; + friend class ScatterplotPlugin; friend class NavigationAction; }; From c91e739f648408bbcbcf59dec3677d6b1fdcefc3 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 26 Mar 2025 17:29:01 +0100 Subject: [PATCH 02/14] Color sample works --- src/ScatterplotPlugin.cpp | 15 ++++++++- src/ScatterplotWidget.cpp | 66 +++++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 6a192ab..4ab84b8 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -384,8 +384,21 @@ void ScatterplotPlugin::selectPoints() QPointF uvNormalized = {}; QPoint uv = {}; + auto& navigator = _scatterPlotWidget->_pointRenderer.getNavigator(); + + auto zoomRectangleWorld = navigator.getZoomRectangleWorld(); + + const auto margin = _scatterPlotWidget->_pointRenderer.getNavigator().getZoomRectangleMargin(); + + //zoomRectangleWorld.setSize(zoomRectangleWorld.size() - QSize(2 * margin, 2 * margin)); + + qDebug() << zoomRectangleWorld; + for (std::uint32_t localPointIndex = 0; localPointIndex < _positions.size(); localPointIndex++) { - uvNormalized = QPointF((_positions[localPointIndex].x - zoomRectangleAction.getLeft()) / zoomRectangleAction.getWidth(), (zoomRectangleAction.getTop() - _positions[localPointIndex].y) / zoomRectangleAction.getHeight()); + //const auto screenPoint = _scatterPlotWidget->getPointRenderer().getWorldPositionToScreenPoint(QVector3D(_positions[localPointIndex].x, _positions[localPointIndex].y, 0.f)); + + uvNormalized = QPointF((_positions[localPointIndex].x - zoomRectangleWorld.left()) / zoomRectangleWorld.width(), (zoomRectangleWorld.bottom() - _positions[localPointIndex].y) / zoomRectangleWorld.height()); + //uvNormalized = QPointF(static_cast(screenPoint.x()) / static_cast(_scatterPlotWidget->width()), static_cast(screenPoint.y()) / static_cast(_scatterPlotWidget->height())); uv = uvOffset + QPoint(uvNormalized.x() * size, uvNormalized.y() * size); if (uv.x() >= selectionAreaImage.width() || uv.x() < 0 || uv.y() >= selectionAreaImage.height() || uv.y() < 0) diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 20136b2..6518deb 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -128,19 +128,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : QObject::connect(winHandle, &QWindow::screenChanged, this, &ScatterplotWidget::updatePixelRatio, Qt::UniqueConnection); }); - connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, [this]() -> void { - auto& zoomRectangleAction = _navigationAction.getZoomRectangleAction(); - - const auto zoomBounds = zoomRectangleAction.getBounds(); - - _pointRenderer.setViewBounds(zoomBounds); - _densityRenderer.setBounds(zoomBounds); - - _navigationAction.getZoomDataExtentsAction().setEnabled(zoomBounds != _dataRectangleAction.getBounds()); - - update(); - }); - connect(&_navigationAction.getZoomDataExtentsAction(), &TriggerAction::triggered, this, [this]() -> void { _pointRenderer.getNavigator().resetView(); }); @@ -156,7 +143,29 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : } }); - connect(&_pointRenderer, &Renderer2D::zoomRectangleChanged, this, [this]() -> void { update(); }); + const auto zoomRectangleChanged = [this]() -> void { + _pointRenderer.getNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); + update(); + }; + + zoomRectangleChanged(); + + connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + + connect(&_pointRenderer.getNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { + disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); + { + _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getNavigator().hasUserNavigated()); + + _navigationAction.getZoomRectangleAction().setLeft(currentZoomRectangleWorld.left()); + _navigationAction.getZoomRectangleAction().setRight(currentZoomRectangleWorld.right()); + _navigationAction.getZoomRectangleAction().setTop(currentZoomRectangleWorld.bottom()); + _navigationAction.getZoomRectangleAction().setBottom(currentZoomRectangleWorld.top()); + } + connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + + update(); + }); //auto setIsNavigating = [this](bool isNavigating) -> void { // _isNavigating = isNavigating; @@ -171,6 +180,7 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : // }; _pointRenderer.getNavigator().initialize(this); + _densityRenderer.getNavigator().initialize(this); } bool ScatterplotWidget::event(QEvent* event) @@ -211,6 +221,9 @@ void ScatterplotWidget::setRenderMode(const RenderMode& renderMode) emit renderModeChanged(_renderMode); + _pointRenderer.getNavigator().setEnabled(_renderMode == SCATTERPLOT); + _densityRenderer.getNavigator().setEnabled(_renderMode == DENSITY || _renderMode == LANDSCAPE); + switch (_renderMode) { case ScatterplotWidget::SCATTERPLOT: @@ -275,32 +288,26 @@ void ScatterplotWidget::setData(const std::vector* points) auto dataBounds = getDataBounds(*points); // pass un-adjusted data bounds to renderer for 2D colormapping - _pointRenderer.setDataBounds(dataBounds); - - // Adjust data points for projection matrix creation (add a little white space around data) - dataBounds.ensureMinimumSize(1e-07f, 1e-07f); - dataBounds.makeSquare(); - dataBounds.expand(0.1f); + _pointRenderer.setDataBounds(QRectF(dataBounds.getLeft(), dataBounds.getTop(), dataBounds.getWidth(), dataBounds.getHeight())); const auto shouldSetBounds = (mv::projects().isOpeningProject() || mv::projects().isImportingProject()) ? false : !_navigationAction.getFreezeZoomAction().isChecked(); - if (shouldSetBounds) - _pointRenderer.setViewBounds(dataBounds); + //if (shouldSetBounds) + // _pointRenderer.setViewBounds(dataBounds); _densityRenderer.setBounds(dataBounds); - _dataRectangleAction.setBounds(dataBounds); - if (shouldSetBounds) - _navigationAction.getZoomRectangleAction().setBounds(dataBounds); - _pointRenderer.setData(*points); _densityRenderer.setData(points); switch (_renderMode) { case ScatterplotWidget::SCATTERPLOT: + { + _pointRenderer.getNavigator().resetView(); break; + } case ScatterplotWidget::DENSITY: case ScatterplotWidget::LANDSCAPE: @@ -308,9 +315,6 @@ void ScatterplotWidget::setData(const std::vector* points) _densityRenderer.computeDensity(); break; } - - default: - break; } // _pointRenderer.setSelectionOutlineColor(Vector3f(1, 0, 0)); @@ -661,8 +665,8 @@ void ScatterplotWidget::resizeGL(int w, int h) // Pixel ratio tells us how many pixels map to a point // That is needed as macOS calculates in points and we do in pixels // On macOS high dpi displays pixel ration is 2 - w *= _pixelRatio; - h *= _pixelRatio; + //w *= _pixelRatio; + //h *= _pixelRatio; _pointRenderer.resize(QSize(w, h)); _densityRenderer.resize(QSize(w, h)); From 3a2e656a3b52a52b591b2a2865d1ee1488778f2a Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 28 Mar 2025 09:45:59 +0100 Subject: [PATCH 03/14] Fix pixel selection tool visibility --- src/ScatterplotPlugin.cpp | 41 +++++++++++---------------- src/ScatterplotWidget.cpp | 59 ++++++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 4ab84b8..42dea3a 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -374,37 +374,30 @@ void ScatterplotPlugin::selectPoints() _positionDataset->getGlobalIndices(localGlobalIndices); - auto& zoomRectangleAction = _scatterPlotWidget->getNavigationAction().getZoomRectangleAction(); - - const auto width = selectionAreaImage.width(); - const auto height = selectionAreaImage.height(); - const auto size = width < height ? width : height; - const auto uvOffset = QPoint((selectionAreaImage.width() - size) / 2.0f, (selectionAreaImage.height() - size) / 2.0f); - - QPointF uvNormalized = {}; - QPoint uv = {}; - - auto& navigator = _scatterPlotWidget->_pointRenderer.getNavigator(); + auto& pointRenderer = _scatterPlotWidget->_pointRenderer; + auto& navigator = pointRenderer.getNavigator(); - auto zoomRectangleWorld = navigator.getZoomRectangleWorld(); - - const auto margin = _scatterPlotWidget->_pointRenderer.getNavigator().getZoomRectangleMargin(); - - //zoomRectangleWorld.setSize(zoomRectangleWorld.size() - QSize(2 * margin, 2 * margin)); - - qDebug() << zoomRectangleWorld; + const auto zoomRectangleWorld = navigator.getZoomRectangleWorld(); + const auto screenRectangle = QRect(QPoint(), pointRenderer.getRenderSize()); + // Go over all points in the dataset to see if they are selected for (std::uint32_t localPointIndex = 0; localPointIndex < _positions.size(); localPointIndex++) { - //const auto screenPoint = _scatterPlotWidget->getPointRenderer().getWorldPositionToScreenPoint(QVector3D(_positions[localPointIndex].x, _positions[localPointIndex].y, 0.f)); + + // Compute the offset of the point in the world space + const auto pointOffsetWorld = QPointF(_positions[localPointIndex].x - zoomRectangleWorld.left(), _positions[localPointIndex].y - zoomRectangleWorld.top()); + + // Normalize it + const auto pointOffsetWorldNormalized = QPointF(pointOffsetWorld.x() / zoomRectangleWorld.width(), pointOffsetWorld.y() / zoomRectangleWorld.height()); - uvNormalized = QPointF((_positions[localPointIndex].x - zoomRectangleWorld.left()) / zoomRectangleWorld.width(), (zoomRectangleWorld.bottom() - _positions[localPointIndex].y) / zoomRectangleWorld.height()); - //uvNormalized = QPointF(static_cast(screenPoint.x()) / static_cast(_scatterPlotWidget->width()), static_cast(screenPoint.y()) / static_cast(_scatterPlotWidget->height())); - uv = uvOffset + QPoint(uvNormalized.x() * size, uvNormalized.y() * size); + // Convert it to screen space + const auto pointOffsetScreen = QPoint(pointOffsetWorldNormalized.x() * screenRectangle.width(), screenRectangle.height() - pointOffsetWorldNormalized.y() * screenRectangle.height()); - if (uv.x() >= selectionAreaImage.width() || uv.x() < 0 || uv.y() >= selectionAreaImage.height() || uv.y() < 0) + // Continue to next point if the point is outside the screen + if (!screenRectangle.contains(pointOffsetScreen)) continue; - if (selectionAreaImage.pixelColor(uv).alpha() > 0) + // If the corresponding pixel is not transparent, add the point to the selection + if (selectionAreaImage.pixelColor(pointOffsetScreen).alpha() > 0) targetSelectionIndices.push_back(localGlobalIndices[localPointIndex]); } diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 6518deb..a3c9197 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -167,18 +167,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : update(); }); - //auto setIsNavigating = [this](bool isNavigating) -> void { - // _isNavigating = isNavigating; - // _pixelSelectionTool.setEnabled(!isNavigating); - // if (isNavigating) { - // _samplerPixelSelectionTool.setEnabled(false); - // } - // else if (_parentPlugin) { // reset to UI-setting - // _samplerPixelSelectionTool.setEnabled(_parentPlugin->getSamplerAction().getEnabledAction().isChecked()); - // } - - // }; - _pointRenderer.getNavigator().initialize(this); _densityRenderer.getNavigator().initialize(this); } @@ -199,6 +187,37 @@ bool ScatterplotWidget::event(QEvent* event) } } + auto setIsNavigating = [this](bool isNavigating) -> void { + _pixelSelectionTool.setEnabled(getRenderMode() == RenderMode::SCATTERPLOT && !isNavigating); + + if (isNavigating) { + _samplerPixelSelectionTool.setEnabled(false); + } + else if (_parentPlugin) { + _samplerPixelSelectionTool.setEnabled(_parentPlugin->getSamplerAction().getEnabledAction().isChecked()); + } + }; + + if (event->type() == QEvent::KeyPress) { + const auto keyEvent = dynamic_cast(event); + + if (keyEvent->key() == Qt::Key_Alt) { + setIsNavigating(true); + + return QOpenGLWidget::event(event); + } + } + + if (event->type() == QEvent::KeyRelease) { + const auto keyEvent = dynamic_cast(event); + + if (keyEvent->key() == Qt::Key_Alt) { + setIsNavigating(false); + + return QOpenGLWidget::event(event); + } + } + return QOpenGLWidget::event(event); } @@ -285,18 +304,13 @@ void ScatterplotWidget::computeDensity() // by reference then we can upload the data to the GPU, but not store it in the widget. void ScatterplotWidget::setData(const std::vector* points) { - auto dataBounds = getDataBounds(*points); - - // pass un-adjusted data bounds to renderer for 2D colormapping - _pointRenderer.setDataBounds(QRectF(dataBounds.getLeft(), dataBounds.getTop(), dataBounds.getWidth(), dataBounds.getHeight())); - - const auto shouldSetBounds = (mv::projects().isOpeningProject() || mv::projects().isImportingProject()) ? false : !_navigationAction.getFreezeZoomAction().isChecked(); + auto bounds = getDataBounds(*points); - //if (shouldSetBounds) - // _pointRenderer.setViewBounds(dataBounds); + const auto dataBounds = QRectF(bounds.getLeft(), bounds.getTop(), bounds.getWidth(), bounds.getHeight()); - _densityRenderer.setBounds(dataBounds); - _dataRectangleAction.setBounds(dataBounds); + _pointRenderer.setDataBounds(dataBounds); + _densityRenderer.setDataBounds(dataBounds); + _dataRectangleAction.setBounds(bounds); _pointRenderer.setData(*points); _densityRenderer.setData(points); @@ -316,7 +330,6 @@ void ScatterplotWidget::setData(const std::vector* points) break; } } - // _pointRenderer.setSelectionOutlineColor(Vector3f(1, 0, 0)); update(); } From 532169ee9504b57c4b5c46cc8b6138ad0452cfdf Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 28 Mar 2025 10:02:28 +0100 Subject: [PATCH 04/14] Fix point sampling routine --- src/ScatterplotPlugin.cpp | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 42dea3a..e8fc51d 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -468,31 +468,37 @@ void ScatterplotPlugin::samplePoints() std::vector localGlobalIndices; _positionDataset->getGlobalIndices(localGlobalIndices); - - auto& zoomRectangleAction = _scatterPlotWidget->getNavigationAction().getZoomRectangleAction(); - - const auto width = selectionAreaImage.width(); - const auto height = selectionAreaImage.height(); - const auto size = width < height ? width : height; - const auto uvOffset = QPoint((selectionAreaImage.width() - size) / 2.0f, (selectionAreaImage.height() - size) / 2.0f); - - QPointF pointUvNormalized; - - QPoint pointUv, mouseUv = _scatterPlotWidget->mapFromGlobal(QCursor::pos()); std::vector focusHighlights(_positions.size()); std::vector> sampledPoints; + auto& pointRenderer = _scatterPlotWidget->_pointRenderer; + auto& navigator = pointRenderer.getNavigator(); + + const auto zoomRectangleWorld = navigator.getZoomRectangleWorld(); + const auto screenRectangle = QRect(QPoint(), pointRenderer.getRenderSize()); + const auto mousePositionWorld = pointRenderer.getScreenPointToWorldPosition(pointRenderer.getNavigator().getViewMatrix(), _scatterPlotWidget->mapFromGlobal(QCursor::pos())); + + // Go over all points in the dataset to see if they should be sampled for (std::uint32_t localPointIndex = 0; localPointIndex < _positions.size(); localPointIndex++) { - pointUvNormalized = QPointF((_positions[localPointIndex].x - zoomRectangleAction.getLeft()) / zoomRectangleAction.getWidth(), (zoomRectangleAction.getTop() - _positions[localPointIndex].y) / zoomRectangleAction.getHeight()); - pointUv = uvOffset + QPoint(pointUvNormalized.x() * size, pointUvNormalized.y() * size); - if (pointUv.x() >= selectionAreaImage.width() || pointUv.x() < 0 || pointUv.y() >= selectionAreaImage.height() || pointUv.y() < 0) + // Compute the offset of the point in the world space + const auto pointOffsetWorld = QPointF(_positions[localPointIndex].x - zoomRectangleWorld.left(), _positions[localPointIndex].y - zoomRectangleWorld.top()); + + // Normalize it + const auto pointOffsetWorldNormalized = QPointF(pointOffsetWorld.x() / zoomRectangleWorld.width(), pointOffsetWorld.y() / zoomRectangleWorld.height()); + + // Convert it to screen space + const auto pointOffsetScreen = QPoint(pointOffsetWorldNormalized.x() * screenRectangle.width(), screenRectangle.height() - pointOffsetWorldNormalized.y() * screenRectangle.height()); + + // Continue to next point if the point is outside the screen + if (!screenRectangle.contains(pointOffsetScreen)) continue; - if (selectionAreaImage.pixelColor(pointUv).alpha() > 0) { - const auto sample = std::pair((QVector2D(mouseUv) - QVector2D(pointUv)).length(), localPointIndex); + // If the corresponding pixel is not transparent, add the point to the selection + if (selectionAreaImage.pixelColor(pointOffsetScreen).alpha() > 0) { + const auto sample = std::pair((QVector2D(_positions[localPointIndex].x, _positions[localPointIndex].y) - mousePositionWorld.toVector2D()).length(), localPointIndex); sampledPoints.emplace_back(sample); } From 630732f32b51b075a54037b79cd2e51333c6d7ae Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 28 Mar 2025 11:13:51 +0100 Subject: [PATCH 05/14] Fix sampling points --- src/ScatterplotPlugin.cpp | 2 +- src/ScatterplotWidget.cpp | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index e8fc51d..9b1cf42 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -456,7 +456,7 @@ void ScatterplotPlugin::samplePoints() { auto& samplerPixelSelectionTool = _scatterPlotWidget->getSamplerPixelSelectionTool(); - if (!_positionDataset.isValid() || !samplerPixelSelectionTool.isActive() || _scatterPlotWidget->_pointRenderer.getNavigator().isNavigating() || !samplerPixelSelectionTool.isEnabled()) + if (!_positionDataset.isValid() || _scatterPlotWidget->_pointRenderer.getNavigator().isNavigating() || !samplerPixelSelectionTool.isActive()) return; auto selectionAreaImage = samplerPixelSelectionTool.getAreaPixmap().toImage(); diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index a3c9197..2ff9666 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -4,11 +4,9 @@ #include -#include +#include #include -#include -#include #include #include #include @@ -16,9 +14,7 @@ #include #include -#include - -#include +#include using namespace mv; @@ -76,10 +72,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : _pixelSelectionTool.setMainColor(QColor(Qt::black)); _pixelSelectionTool.setFixedBrushRadiusModifier(Qt::AltModifier); - _samplerPixelSelectionTool.setEnabled(true); - _samplerPixelSelectionTool.setMainColor(QColor(Qt::black)); - _samplerPixelSelectionTool.setFixedBrushRadiusModifier(Qt::AltModifier); - connect(&_pixelSelectionTool, &PixelSelectionTool::shapeChanged, [this]() { if (isInitialized()) update(); @@ -169,6 +161,10 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : _pointRenderer.getNavigator().initialize(this); _densityRenderer.getNavigator().initialize(this); + + _samplerPixelSelectionTool.setEnabled(true); + _samplerPixelSelectionTool.setMainColor(QColor(Qt::black)); + _samplerPixelSelectionTool.setFixedBrushRadiusModifier(Qt::AltModifier); } bool ScatterplotWidget::event(QEvent* event) @@ -432,7 +428,7 @@ mv::Vector3f ScatterplotWidget::getColorMapRange() const break; } - return Vector3f(); + return {}; } void ScatterplotWidget::setColorMapRange(const float& min, const float& max) From b60a188723a433a40ae061044b8c2d483209138c Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 31 Mar 2025 15:30:04 +0200 Subject: [PATCH 06/14] Incorporate changes from the new renderer and add ability to freeze the selection --- src/ScatterplotPlugin.cpp | 3 ++ src/ScatterplotWidget.cpp | 64 +++++++++++++++++++-------------------- src/ScatterplotWidget.h | 9 ------ src/SelectionAction.cpp | 8 ++++- src/SelectionAction.h | 2 ++ 5 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 9b1cf42..554d7e2 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -357,6 +357,9 @@ void ScatterplotPlugin::createSubset(const bool& fromSourceData /*= false*/, con void ScatterplotPlugin::selectPoints() { + if (getSettingsAction().getSelectionAction().getFreezeSelectionAction().isChecked()) + return; + auto& pixelSelectionTool = _scatterPlotWidget->getPixelSelectionTool(); // Only proceed with a valid points position dataset and when the pixel selection tool is active diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 2ff9666..e8dacd7 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -34,14 +34,6 @@ namespace return bounds; } - - void translateBounds(Bounds& b, float x, float y) - { - b.setLeft(b.getLeft() + x); - b.setRight(b.getRight() + x); - b.setBottom(b.getBottom() + y); - b.setTop(b.getTop() + y); - } } ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : @@ -50,7 +42,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : _renderMode(SCATTERPLOT), _backgroundColor(255, 255, 255, 255), _coloringMode(ColoringMode::Constant), - _widgetSizeInfo(), _dataRectangleAction(this, "Data rectangle"), _navigationAction(this, "Navigation"), _pixelSelectionTool(this), @@ -137,6 +128,8 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : const auto zoomRectangleChanged = [this]() -> void { _pointRenderer.getNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); + _densityRenderer.getNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); + update(); }; @@ -159,6 +152,21 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : update(); }); + connect(&_densityRenderer.getNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { + disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); + { + _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getNavigator().hasUserNavigated()); + + _navigationAction.getZoomRectangleAction().setLeft(currentZoomRectangleWorld.left()); + _navigationAction.getZoomRectangleAction().setRight(currentZoomRectangleWorld.right()); + _navigationAction.getZoomRectangleAction().setTop(currentZoomRectangleWorld.bottom()); + _navigationAction.getZoomRectangleAction().setBottom(currentZoomRectangleWorld.top()); + } + connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + + update(); + }); + _pointRenderer.getNavigator().initialize(this); _densityRenderer.getNavigator().initialize(this); @@ -286,10 +294,12 @@ PixelSelectionTool& ScatterplotWidget::getSamplerPixelSelectionTool() void ScatterplotWidget::computeDensity() { - emit densityComputationStarted(); - - _densityRenderer.computeDensity(); + qDebug() << "ScatterplotWidget::computeDensity()"; + emit densityComputationStarted(); + { + _densityRenderer.computeDensity(); + } emit densityComputationEnded(); update(); @@ -300,13 +310,17 @@ void ScatterplotWidget::computeDensity() // by reference then we can upload the data to the GPU, but not store it in the widget. void ScatterplotWidget::setData(const std::vector* points) { - auto bounds = getDataBounds(*points); + auto dataBounds = getDataBounds(*points); + + _pointRenderer.setDataBounds(QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight()))); + + _dataRectangleAction.setBounds(dataBounds); - const auto dataBounds = QRectF(bounds.getLeft(), bounds.getTop(), bounds.getWidth(), bounds.getHeight()); + dataBounds.ensureMinimumSize(1e-07f, 1e-07f); + dataBounds.makeSquare(); + dataBounds.expand(0.1f); - _pointRenderer.setDataBounds(dataBounds); - _densityRenderer.setDataBounds(dataBounds); - _dataRectangleAction.setBounds(bounds); + _densityRenderer.setDataBounds(QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight()))); _pointRenderer.setData(*points); _densityRenderer.setData(points); @@ -322,6 +336,7 @@ void ScatterplotWidget::setData(const std::vector* points) case ScatterplotWidget::DENSITY: case ScatterplotWidget::LANDSCAPE: { + _densityRenderer.getNavigator().resetView(); _densityRenderer.computeDensity(); break; } @@ -662,21 +677,6 @@ void ScatterplotWidget::initializeGL() void ScatterplotWidget::resizeGL(int w, int h) { - _widgetSizeInfo.width = static_cast(w); - _widgetSizeInfo.height = static_cast(h); - _widgetSizeInfo.minWH = _widgetSizeInfo.width < _widgetSizeInfo.height ? _widgetSizeInfo.width : _widgetSizeInfo.height; - _widgetSizeInfo.ratioWidth = _widgetSizeInfo.width / _widgetSizeInfo.minWH; - _widgetSizeInfo.ratioHeight = _widgetSizeInfo.height / _widgetSizeInfo.minWH; - - // we need this here as we do not have the screen yet to get the actual devicePixelRatio when the view is created - _pixelRatio = devicePixelRatio(); - - // Pixel ratio tells us how many pixels map to a point - // That is needed as macOS calculates in points and we do in pixels - // On macOS high dpi displays pixel ration is 2 - //w *= _pixelRatio; - //h *= _pixelRatio; - _pointRenderer.resize(QSize(w, h)); _densityRenderer.resize(QSize(w, h)); } diff --git a/src/ScatterplotWidget.h b/src/ScatterplotWidget.h index f687492..5f0c1b4 100644 --- a/src/ScatterplotWidget.h +++ b/src/ScatterplotWidget.h @@ -25,14 +25,6 @@ namespace mv::plugin class ViewPlugin; } -struct widgetSizeInfo { - float width; - float height; - float minWH; - float ratioWidth; - float ratioHeight; -}; - class ScatterplotWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core { Q_OBJECT @@ -296,7 +288,6 @@ private slots: RenderMode _renderMode; /** Current render mode */ QColor _backgroundColor; /** Background color */ ColoringMode _coloringMode; /** Type of point/density coloring */ - widgetSizeInfo _widgetSizeInfo; /** Info about size of the scatterplot widget */ DecimalRectangleAction _dataRectangleAction; /** Rectangle action for the bounds of the loaded data */ NavigationAction _navigationAction; /** All navigation-related actions are grouped in this action */ QImage _colorMapImage; /** 1D/2D color map image */ diff --git a/src/SelectionAction.cpp b/src/SelectionAction.cpp index 3aca6e6..5a15802 100644 --- a/src/SelectionAction.cpp +++ b/src/SelectionAction.cpp @@ -17,7 +17,8 @@ SelectionAction::SelectionAction(QObject* parent, const QString& title) : _outlineOverrideColorAction(this, "Custom color", true), _outlineScaleAction(this, "Scale", 100.0f, 500.0f, 200.0f, 1), _outlineOpacityAction(this, "Opacity", 0.0f, 100.0f, 100.0f, 1), - _outlineHaloEnabledAction(this, "Halo") + _outlineHaloEnabledAction(this, "Halo"), + _freezeSelectionAction(this, "Freeze selection") { setIconByName("mouse-pointer"); @@ -35,6 +36,7 @@ SelectionAction::SelectionAction(QObject* parent, const QString& title) : addAction(&getOutlineScaleAction()); addAction(&getOutlineOpacityAction()); addAction(&getOutlineHaloEnabledAction()); + addAction(&getFreezeSelectionAction()); _pixelSelectionAction.getOverlayColorAction().setText("Color"); @@ -148,6 +150,7 @@ void SelectionAction::connectToPublicAction(WidgetAction* publicAction, bool rec actions().connectPrivateActionToPublicAction(&_outlineScaleAction, &publicSelectionAction->getOutlineScaleAction(), recursive); actions().connectPrivateActionToPublicAction(&_outlineOpacityAction, &publicSelectionAction->getOutlineOpacityAction(), recursive); actions().connectPrivateActionToPublicAction(&_outlineHaloEnabledAction, &publicSelectionAction->getOutlineHaloEnabledAction(), recursive); + actions().connectPrivateActionToPublicAction(&_freezeSelectionAction, &publicSelectionAction->getFreezeSelectionAction(), recursive); } GroupAction::connectToPublicAction(publicAction, recursive); @@ -165,6 +168,7 @@ void SelectionAction::disconnectFromPublicAction(bool recursive) actions().disconnectPrivateActionFromPublicAction(&_outlineScaleAction, recursive); actions().disconnectPrivateActionFromPublicAction(&_outlineOpacityAction, recursive); actions().disconnectPrivateActionFromPublicAction(&_outlineHaloEnabledAction, recursive); + actions().disconnectPrivateActionFromPublicAction(&_freezeSelectionAction, recursive); } GroupAction::disconnectFromPublicAction(recursive); @@ -181,6 +185,7 @@ void SelectionAction::fromVariantMap(const QVariantMap& variantMap) _outlineScaleAction.fromParentVariantMap(variantMap); _outlineOpacityAction.fromParentVariantMap(variantMap); _outlineHaloEnabledAction.fromParentVariantMap(variantMap); + _freezeSelectionAction.fromParentVariantMap(variantMap); } QVariantMap SelectionAction::toVariantMap() const @@ -194,6 +199,7 @@ QVariantMap SelectionAction::toVariantMap() const _outlineScaleAction.insertIntoVariantMap(variantMap); _outlineOpacityAction.insertIntoVariantMap(variantMap); _outlineHaloEnabledAction.insertIntoVariantMap(variantMap); + _freezeSelectionAction.insertIntoVariantMap(variantMap); return variantMap; } \ No newline at end of file diff --git a/src/SelectionAction.h b/src/SelectionAction.h index 708a454..d564c5e 100644 --- a/src/SelectionAction.h +++ b/src/SelectionAction.h @@ -64,6 +64,7 @@ class SelectionAction : public GroupAction DecimalAction& getOutlineScaleAction() { return _outlineScaleAction; } DecimalAction& getOutlineOpacityAction() { return _outlineOpacityAction; } ToggleAction& getOutlineHaloEnabledAction() { return _outlineHaloEnabledAction; } + ToggleAction& getFreezeSelectionAction() { return _freezeSelectionAction; } private: PixelSelectionAction _pixelSelectionAction; /** Pixel selection action */ @@ -73,6 +74,7 @@ class SelectionAction : public GroupAction DecimalAction _outlineScaleAction; /** Selection outline scale action */ DecimalAction _outlineOpacityAction; /** Selection outline opacity action */ ToggleAction _outlineHaloEnabledAction; /** Selection outline halo enabled action */ + ToggleAction _freezeSelectionAction; /** Freeze selection action */ friend class mv::AbstractActionsManager; }; From b281cea87a297c25ef8bcfd53ded09089e2bae96 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 09:36:13 +0200 Subject: [PATCH 07/14] Revamp bottom toolbar --- CMakeLists.txt | 2 - src/NavigationAction.cpp | 100 -------------------------------------- src/NavigationAction.h | 83 ------------------------------- src/ScatterplotPlugin.cpp | 35 ++++++++----- src/ScatterplotPlugin.h | 1 - src/ScatterplotWidget.cpp | 14 +++--- src/ScatterplotWidget.h | 5 +- 7 files changed, 35 insertions(+), 205 deletions(-) delete mode 100644 src/NavigationAction.cpp delete mode 100644 src/NavigationAction.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 754613b..fe1180e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,8 +78,6 @@ set(Actions src/ExportAction.cpp src/DatasetsAction.h src/DatasetsAction.cpp - src/NavigationAction.h - src/NavigationAction.cpp ) set(Models diff --git a/src/NavigationAction.cpp b/src/NavigationAction.cpp deleted file mode 100644 index f07d6fb..0000000 --- a/src/NavigationAction.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "NavigationAction.h" -#include "ScatterplotWidget.h" - -#include -#include - -#include - -using namespace mv; -using namespace mv::gui; - -NavigationAction::NavigationAction(QObject* parent, const QString& title) : - HorizontalGroupAction(parent, title), - _scatterplotWidget(nullptr), - _zoomRectangleAction(this, "Zoom Rectangle"), - _zoomDataExtentsAction(this, "Zoom to data extents"), - _freezeZoomAction(this, "Freeze zoom") -{ - setIconByName("image"); - setShowLabels(false); - - addAction(&_zoomRectangleAction); - addAction(&_zoomDataExtentsAction); - addAction(&_freezeZoomAction); - - _zoomRectangleAction.setToolTip("Extents of the current view"); - _zoomRectangleAction.setIcon(combineIcons(StyledIcon("expand"), StyledIcon("ellipsis-h"))); - _zoomRectangleAction.setIconByName("vector-square"); - _zoomRectangleAction.setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup); - - _zoomDataExtentsAction.setToolTip("Zoom to the extents of the data (Alt + O)"); - _zoomDataExtentsAction.setIconByName("expand"); - _zoomDataExtentsAction.setDefaultWidgetFlags(TriggerAction::Icon); - _zoomDataExtentsAction.setConnectionPermissionsToForceNone(true); - _zoomDataExtentsAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _zoomDataExtentsAction.setShortcut(QKeySequence("Alt+O")); - - _freezeZoomAction.setToolTip("Freeze the zoom extents"); - _freezeZoomAction.setConnectionPermissionsToForceNone(true); -} - -void NavigationAction::initialize(ScatterplotWidget* scatterplotWidget) -{ - Q_ASSERT(scatterplotWidget != nullptr); - - if (scatterplotWidget == nullptr) - return; - - _scatterplotWidget = scatterplotWidget; - - _scatterplotWidget->addAction(&_zoomDataExtentsAction); -} - -void NavigationAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) -{ - auto publicNavigationAction = dynamic_cast(publicAction); - - Q_ASSERT(publicNavigationAction != nullptr); - - if (publicNavigationAction == nullptr) - return; - - if (recursive) { - actions().connectPrivateActionToPublicAction(&_zoomRectangleAction, &publicNavigationAction->getZoomRectangleAction(), recursive); - } - - HorizontalGroupAction::connectToPublicAction(publicAction, recursive); -} - -void NavigationAction::disconnectFromPublicAction(bool recursive) -{ - if (!isConnected()) - return; - - if (recursive) { - actions().disconnectPrivateActionFromPublicAction(&_zoomRectangleAction, recursive); - } - - HorizontalGroupAction::disconnectFromPublicAction(recursive); -} - -void NavigationAction::fromVariantMap(const QVariantMap& variantMap) -{ - HorizontalGroupAction::fromVariantMap(variantMap); - - _zoomRectangleAction.fromParentVariantMap(variantMap); - _zoomDataExtentsAction.fromParentVariantMap(variantMap); - _freezeZoomAction.fromParentVariantMap(variantMap); -} - -QVariantMap NavigationAction::toVariantMap() const -{ - auto variantMap = HorizontalGroupAction::toVariantMap(); - - _zoomRectangleAction.insertIntoVariantMap(variantMap); - _zoomDataExtentsAction.insertIntoVariantMap(variantMap); - _freezeZoomAction.insertIntoVariantMap(variantMap); - - return variantMap; -} diff --git a/src/NavigationAction.h b/src/NavigationAction.h deleted file mode 100644 index d5d1e55..0000000 --- a/src/NavigationAction.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -using namespace mv::gui; - -class ScatterplotWidget; - -/** - * Navigation action class - * - * Action class for navigating - * - * @author Thomas Kroes - */ -class NavigationAction : public HorizontalGroupAction -{ -public: - - /** - * Construct with \p parent and \p title - * @param parent Pointer to parent object - * @param title Title of the action - */ - Q_INVOKABLE NavigationAction(QObject* parent, const QString& title); - - /** - * Initialize the navigation action with pointer to owning \p scatterplotWidget - * @param scatterplotWidget Pointer to scatterplot widget - */ - void initialize(ScatterplotWidget* scatterplotWidget); - -protected: // Linking - - /** - * Connect this action to a public action - * @param publicAction Pointer to public action to connect to - * @param recursive Whether to also connect descendant child actions - */ - void connectToPublicAction(WidgetAction* publicAction, bool recursive) override; - - /** - * Disconnect this action from its public action - * @param recursive Whether to also disconnect descendant child actions - */ - void disconnectFromPublicAction(bool recursive) override; - -public: // Serialization - - /** - * Load widget action from variant map - * @param Variant map representation of the widget action - */ - void fromVariantMap(const QVariantMap& variantMap) override; - - /** - * Save widget action to variant map - * @return Variant map representation of the widget action - */ - - QVariantMap toVariantMap() const override; - -public: // Action getters - - DecimalRectangleAction& getZoomRectangleAction() { return _zoomRectangleAction; } - TriggerAction& getZoomDataExtentsAction() { return _zoomDataExtentsAction; } - ToggleAction& getFreezeZoomAction() { return _freezeZoomAction; } - -private: - ScatterplotWidget* _scatterplotWidget; /** Pointer to owning scatterplot widget */ - DecimalRectangleAction _zoomRectangleAction; /** Rectangle action for setting the current zoom bounds */ - TriggerAction _zoomDataExtentsAction; /** Trigger action to zoom to data extents */ - ToggleAction _freezeZoomAction; /** Action for toggling the zoom rectangle freeze */ - - friend class mv::AbstractActionsManager; -}; - -Q_DECLARE_METATYPE(NavigationAction) - -inline const auto navigationActionMetaTypeId = qRegisterMetaType("NavigationAction"); \ No newline at end of file diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 554d7e2..dd20fba 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -47,8 +47,7 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : _scatterPlotWidget(new ScatterplotWidget(this)), _numPoints(0), _settingsAction(this, "Settings"), - _primaryToolbarAction(this, "Primary Toolbar"), - _secondaryToolbarAction(this, "Secondary Toolbar") + _primaryToolbarAction(this, "Primary Toolbar") { setObjectName("Scatterplot"); @@ -105,11 +104,7 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : connect(_scatterPlotWidget, &ScatterplotWidget::renderModeChanged, this, updateReadOnly); connect(&_positionDataset, &Dataset::changed, this, updateReadOnly); - _secondaryToolbarAction.addAction(&_settingsAction.getColoringAction().getColorMap1DAction(), 1); - _secondaryToolbarAction.addAction(focusSelectionAction, 2); - //_secondaryToolbarAction.addAction(&_settingsAction.getExportAction()); - _secondaryToolbarAction.addAction(&_settingsAction.getMiscellaneousAction()); - _secondaryToolbarAction.addAction(&_scatterPlotWidget->getNavigationAction()); + //_secondaryToolbarAction.addAction(&_settingsAction.getMiscellaneousAction()); connect(_scatterPlotWidget, &ScatterplotWidget::customContextMenuRequested, this, [this](const QPoint& point) { if (!_positionDataset.isValid()) @@ -252,7 +247,23 @@ void ScatterplotPlugin::init() layout->setSpacing(0); layout->addWidget(_primaryToolbarAction.createWidget(&getWidget())); layout->addWidget(_scatterPlotWidget, 100); - layout->addWidget(_secondaryToolbarAction.createWidget(&getWidget())); + + auto navigationLayout = new QHBoxLayout(); + + navigationLayout->addStretch(1); + { + auto renderersNavigationGroupAction = new HorizontalGroupAction(this, "Renderers Navigation"); + + renderersNavigationGroupAction->setShowLabels(false); + + renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getPointRenderer().getNavigator().getNavigationAction())); + renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getDensityRenderer().getNavigator().getNavigationAction())); + + navigationLayout->addWidget(renderersNavigationGroupAction->createWidget(&getWidget())); + } + navigationLayout->addStretch(1); + + layout->addLayout(navigationLayout); getWidget().setLayout(layout); @@ -325,6 +336,10 @@ void ScatterplotPlugin::init() return pointIndicesTableWidget; }); #endif + + _scatterPlotWidget->updateNavigationActionVisibility(); + + connect(&_settingsAction.getRenderModeAction(), &OptionAction::currentIndexChanged, _scatterPlotWidget, &ScatterplotWidget::updateNavigationActionVisibility); } void ScatterplotPlugin::loadData(const Datasets& datasets) @@ -804,7 +819,6 @@ void ScatterplotPlugin::fromVariantMap(const QVariantMap& variantMap) variantMapMustContain(variantMap, "Settings"); _primaryToolbarAction.fromParentVariantMap(variantMap); - _secondaryToolbarAction.fromParentVariantMap(variantMap); _settingsAction.fromParentVariantMap(variantMap); _scatterPlotWidget->getNavigationAction().fromParentVariantMap(variantMap); @@ -815,7 +829,6 @@ QVariantMap ScatterplotPlugin::toVariantMap() const QVariantMap variantMap = ViewPlugin::toVariantMap(); _primaryToolbarAction.insertIntoVariantMap(variantMap); - _secondaryToolbarAction.insertIntoVariantMap(variantMap); _settingsAction.insertIntoVariantMap(variantMap); _scatterPlotWidget->getNavigationAction().insertIntoVariantMap(variantMap); @@ -916,5 +929,5 @@ PluginTriggerActions ScatterplotPluginFactory::getPluginTriggerActions(const mv: QUrl ScatterplotPluginFactory::getRepositoryUrl() const { - return QUrl("https://github.com/ManiVaultStudio/Scatterplot"); + return { "https://github.com/ManiVaultStudio/Scatterplot" }; } diff --git a/src/ScatterplotPlugin.h b/src/ScatterplotPlugin.h index ba10fd8..16d279b 100644 --- a/src/ScatterplotPlugin.h +++ b/src/ScatterplotPlugin.h @@ -119,7 +119,6 @@ class ScatterplotPlugin : public ViewPlugin SettingsAction _settingsAction; /** Group action for all settings */ HorizontalToolbarAction _primaryToolbarAction; /** Horizontal toolbar for primary content */ - HorizontalToolbarAction _secondaryToolbarAction; /** Secondary toolbar for secondary content */ static const std::int32_t LAZY_UPDATE_INTERVAL = 2; diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index e8dacd7..73e12f7 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -57,8 +57,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : grabGesture(Qt::PinchGesture); installEventFilter(this); - _navigationAction.initialize(this); - _pixelSelectionTool.setEnabled(true); _pixelSelectionTool.setMainColor(QColor(Qt::black)); _pixelSelectionTool.setFixedBrushRadiusModifier(Qt::AltModifier); @@ -111,10 +109,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : QObject::connect(winHandle, &QWindow::screenChanged, this, &ScatterplotWidget::updatePixelRatio, Qt::UniqueConnection); }); - connect(&_navigationAction.getZoomDataExtentsAction(), &TriggerAction::triggered, this, [this]() -> void { - _pointRenderer.getNavigator().resetView(); - }); - connect(&_pointRenderer.getNavigator(), &Navigator2D::isNavigatingChanged, this, [this](bool isNavigating) -> void { _pixelSelectionTool.setEnabled(!isNavigating); @@ -126,6 +120,7 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : } }); + /* const auto zoomRectangleChanged = [this]() -> void { _pointRenderer.getNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); _densityRenderer.getNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); @@ -166,6 +161,7 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : update(); }); + */ _pointRenderer.getNavigator().initialize(this); _densityRenderer.getNavigator().initialize(this); @@ -642,6 +638,12 @@ bool ScatterplotWidget::getRandomizedDepthEnabled() const return _pointRenderer.getRandomizedDepthEnabled(); } +void ScatterplotWidget::updateNavigationActionVisibility() +{ + _pointRenderer.getNavigator().getNavigationAction().setVisible(getRenderMode() == ScatterplotWidget::SCATTERPLOT); + _densityRenderer.getNavigator().getNavigationAction().setVisible(getRenderMode() == ScatterplotWidget::DENSITY || getRenderMode() == ScatterplotWidget::LANDSCAPE); +} + void ScatterplotWidget::initializeGL() { initializeOpenGLFunctions(); diff --git a/src/ScatterplotWidget.h b/src/ScatterplotWidget.h index 5f0c1b4..e9cd515 100644 --- a/src/ScatterplotWidget.h +++ b/src/ScatterplotWidget.h @@ -1,7 +1,5 @@ #pragma once -#include "NavigationAction.h" - #include #include @@ -217,6 +215,9 @@ class ScatterplotWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_C */ bool getRandomizedDepthEnabled() const; + /** Toggles the point vs density and landscape renderer visibility based on the current render mode */ + void updateNavigationActionVisibility(); + protected: void initializeGL() Q_DECL_OVERRIDE; void resizeGL(int w, int h) Q_DECL_OVERRIDE; From 3f358b15107a9d4015a75046bab995c509b3c261 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 10:02:31 +0200 Subject: [PATCH 08/14] Work on incorporating the navigation action --- src/ScatterplotPlugin.cpp | 6 ------ src/ScatterplotWidget.cpp | 16 +++++++++------- src/ScatterplotWidget.h | 17 ++++++++++++++--- src/SettingsAction.cpp | 11 +++++++++++ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index dd20fba..9cc2805 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -69,8 +69,6 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : _dropWidget = new DropWidget(_scatterPlotWidget); - _scatterPlotWidget->getNavigationAction().setParent(this); - getWidget().setFocusPolicy(Qt::ClickFocus); _primaryToolbarAction.addAction(&_settingsAction.getDatasetsAction()); @@ -820,8 +818,6 @@ void ScatterplotPlugin::fromVariantMap(const QVariantMap& variantMap) _primaryToolbarAction.fromParentVariantMap(variantMap); _settingsAction.fromParentVariantMap(variantMap); - - _scatterPlotWidget->getNavigationAction().fromParentVariantMap(variantMap); } QVariantMap ScatterplotPlugin::toVariantMap() const @@ -831,8 +827,6 @@ QVariantMap ScatterplotPlugin::toVariantMap() const _primaryToolbarAction.insertIntoVariantMap(variantMap); _settingsAction.insertIntoVariantMap(variantMap); - _scatterPlotWidget->getNavigationAction().insertIntoVariantMap(variantMap); - return variantMap; } diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 73e12f7..62f88f9 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -43,7 +43,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : _backgroundColor(255, 255, 255, 255), _coloringMode(ColoringMode::Constant), _dataRectangleAction(this, "Data rectangle"), - _navigationAction(this, "Navigation"), _pixelSelectionTool(this), _samplerPixelSelectionTool(this), _pixelRatio(1.0), @@ -120,10 +119,13 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : } }); + connect(&getPointRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this]() -> void { update(); }); + connect(&getDensityRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this]() -> void { update(); }); + /* const auto zoomRectangleChanged = [this]() -> void { - _pointRenderer.getNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); - _densityRenderer.getNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); + _pointRenderer.getPointRendererNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); + _densityRenderer.getPointRendererNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); update(); }; @@ -132,10 +134,10 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); - connect(&_pointRenderer.getNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { + connect(&_pointRenderer.getPointRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); { - _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getNavigator().hasUserNavigated()); + _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getPointRendererNavigator().hasUserNavigated()); _navigationAction.getZoomRectangleAction().setLeft(currentZoomRectangleWorld.left()); _navigationAction.getZoomRectangleAction().setRight(currentZoomRectangleWorld.right()); @@ -147,10 +149,10 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : update(); }); - connect(&_densityRenderer.getNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { + connect(&_densityRenderer.getPointRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); { - _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getNavigator().hasUserNavigated()); + _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getPointRendererNavigator().hasUserNavigated()); _navigationAction.getZoomRectangleAction().setLeft(currentZoomRectangleWorld.left()); _navigationAction.getZoomRectangleAction().setRight(currentZoomRectangleWorld.right()); diff --git a/src/ScatterplotWidget.h b/src/ScatterplotWidget.h index e9cd515..cb81a8b 100644 --- a/src/ScatterplotWidget.h +++ b/src/ScatterplotWidget.h @@ -113,8 +113,6 @@ class ScatterplotWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_C return _dataRectangleAction.getBounds(); } - NavigationAction& getNavigationAction() { return _navigationAction; } - mv::Vector3f getColorMapRange() const; void setColorMapRange(const float& min, const float& max); @@ -249,6 +247,20 @@ class ScatterplotWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_C /** Assign a color map image to the point and density renderers */ void setColorMap(const QImage& colorMapImage); +public: // Navigators + + /** + * Get the navigator for the point renderer + * @return Reference to the navigator + */ + mv::Navigator2D& getPointRendererNavigator() { return _pointRenderer.getNavigator(); } + + /** + * Get the navigator for the density renderer + * @return Reference to the navigator + */ + mv::Navigator2D& getDensityRendererNavigator() { return _densityRenderer.getNavigator(); } + signals: void initialized(); void created(); @@ -290,7 +302,6 @@ private slots: QColor _backgroundColor; /** Background color */ ColoringMode _coloringMode; /** Type of point/density coloring */ DecimalRectangleAction _dataRectangleAction; /** Rectangle action for the bounds of the loaded data */ - NavigationAction _navigationAction; /** All navigation-related actions are grouped in this action */ QImage _colorMapImage; /** 1D/2D color map image */ PixelSelectionTool _pixelSelectionTool; /** 2D pixel selection tool */ PixelSelectionTool _samplerPixelSelectionTool; /** 2D pixel selection tool */ diff --git a/src/SettingsAction.cpp b/src/SettingsAction.cpp index ef66b79..740da78 100644 --- a/src/SettingsAction.cpp +++ b/src/SettingsAction.cpp @@ -6,6 +6,8 @@ #include +#include "ScatterplotWidget.h" + using namespace mv::gui; SettingsAction::SettingsAction(QObject* parent, const QString& title) : @@ -71,6 +73,12 @@ void SettingsAction::fromVariantMap(const QVariantMap& variantMap) _renderModeAction.fromParentVariantMap(variantMap); _selectionAction.fromParentVariantMap(variantMap); _miscellaneousAction.fromParentVariantMap(variantMap); + + if (variantMap.contains("PointRendererNavigation")) + _scatterplotPlugin->getScatterplotWidget().getPointRendererNavigator().getNavigationAction().fromVariantMap(variantMap["PointRendererNavigation"].toMap()); + + if (variantMap.contains("DensityRendererNavigation")) + _scatterplotPlugin->getScatterplotWidget().getDensityRendererNavigator().getNavigationAction().fromVariantMap(variantMap["DensityRendererNavigation"].toMap()); } QVariantMap SettingsAction::toVariantMap() const @@ -85,5 +93,8 @@ QVariantMap SettingsAction::toVariantMap() const _selectionAction.insertIntoVariantMap(variantMap); _miscellaneousAction.insertIntoVariantMap(variantMap); + variantMap["PointRendererNavigation"] = _scatterplotPlugin->getScatterplotWidget().getPointRendererNavigator().getNavigationAction().toVariantMap(); + variantMap["DensityRendererNavigation"] = _scatterplotPlugin->getScatterplotWidget().getDensityRendererNavigator().getNavigationAction().toVariantMap(); + return variantMap; } From 41a84b3c52e8d194b8b0d4f1bb866be646e51211 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 10:38:24 +0200 Subject: [PATCH 09/14] Work on navigation --- src/ScatterplotWidget.cpp | 43 --------------------------------------- 1 file changed, 43 deletions(-) diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 62f88f9..39bcb50 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -122,49 +122,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : connect(&getPointRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this]() -> void { update(); }); connect(&getDensityRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this]() -> void { update(); }); - /* - const auto zoomRectangleChanged = [this]() -> void { - _pointRenderer.getPointRendererNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); - _densityRenderer.getPointRendererNavigator().setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); - - update(); - }; - - zoomRectangleChanged(); - - connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); - - connect(&_pointRenderer.getPointRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { - disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); - { - _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getPointRendererNavigator().hasUserNavigated()); - - _navigationAction.getZoomRectangleAction().setLeft(currentZoomRectangleWorld.left()); - _navigationAction.getZoomRectangleAction().setRight(currentZoomRectangleWorld.right()); - _navigationAction.getZoomRectangleAction().setTop(currentZoomRectangleWorld.bottom()); - _navigationAction.getZoomRectangleAction().setBottom(currentZoomRectangleWorld.top()); - } - connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); - - update(); - }); - - connect(&_densityRenderer.getPointRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { - disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); - { - _navigationAction.getZoomDataExtentsAction().setEnabled(_pointRenderer.getPointRendererNavigator().hasUserNavigated()); - - _navigationAction.getZoomRectangleAction().setLeft(currentZoomRectangleWorld.left()); - _navigationAction.getZoomRectangleAction().setRight(currentZoomRectangleWorld.right()); - _navigationAction.getZoomRectangleAction().setTop(currentZoomRectangleWorld.bottom()); - _navigationAction.getZoomRectangleAction().setBottom(currentZoomRectangleWorld.top()); - } - connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); - - update(); - }); - */ - _pointRenderer.getNavigator().initialize(this); _densityRenderer.getNavigator().initialize(this); From 18aa5fae75fae926bb9b308ed541417b50634914 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 13:50:11 +0200 Subject: [PATCH 10/14] Adhere to core --- src/ScatterplotPlugin.cpp | 15 ++++++++++++--- src/ScatterplotWidget.cpp | 2 -- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 9cc2805..981a31f 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -246,8 +246,11 @@ void ScatterplotPlugin::init() layout->addWidget(_primaryToolbarAction.createWidget(&getWidget())); layout->addWidget(_scatterPlotWidget, 100); + auto navigationWidget = new QWidget(); auto navigationLayout = new QHBoxLayout(); + navigationLayout->setContentsMargins(4, 4, 4, 4); + navigationLayout->addStretch(1); { auto renderersNavigationGroupAction = new HorizontalGroupAction(this, "Renderers Navigation"); @@ -261,11 +264,12 @@ void ScatterplotPlugin::init() } navigationLayout->addStretch(1); - layout->addLayout(navigationLayout); + navigationWidget->setLayout(navigationLayout); + + layout->addWidget(navigationWidget); getWidget().setLayout(layout); - // Update the data when the scatter plot widget is initialized connect(_scatterPlotWidget, &ScatterplotWidget::initialized, this, &ScatterplotPlugin::updateData); connect(&_scatterPlotWidget->getPixelSelectionTool(), &PixelSelectionTool::areaChanged, [this]() { @@ -463,6 +467,8 @@ void ScatterplotPlugin::selectPoints() } } + _scatterPlotWidget->getPointRendererNavigator().getNavigationAction().getZoomSelectionAction().setEnabled(!targetSelectionIndices.empty()); + _positionDataset->setSelectionIndices(targetSelectionIndices); events().notifyDatasetDataSelectionChanged(_positionDataset->getSourceDataset()); @@ -597,7 +603,10 @@ void ScatterplotPlugin::positionDatasetChanged() _positionSourceDataset = _positionDataset->getSourceDataset(); _numPoints = _positionDataset->getNumPoints(); - + + _scatterPlotWidget->getPointRendererNavigator().resetView(true); + _scatterPlotWidget->getDensityRendererNavigator().resetView(true); + updateData(); } diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 39bcb50..083f8e0 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -249,8 +249,6 @@ PixelSelectionTool& ScatterplotWidget::getSamplerPixelSelectionTool() void ScatterplotWidget::computeDensity() { - qDebug() << "ScatterplotWidget::computeDensity()"; - emit densityComputationStarted(); { _densityRenderer.computeDensity(); From d7c66ca24533401e1331c4d545cfd5bc3404c6ad Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 7 Apr 2025 08:46:04 +0200 Subject: [PATCH 11/14] Adhere to latest core renderer upgrade --- src/ScatterplotPlugin.cpp | 65 +++++++++++++++++++++++++++++---------- src/ScatterplotPlugin.h | 3 +- src/ScatterplotWidget.cpp | 22 ++++++++----- src/SelectionAction.cpp | 5 ++- 4 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 981a31f..469b32b 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -55,17 +55,24 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : shortcuts.add({ QKeySequence(Qt::Key_R), "Selection", "Rectangle (default)" }); shortcuts.add({ QKeySequence(Qt::Key_L), "Selection", "Lasso" }); + shortcuts.add({ QKeySequence(Qt::Key_P), "Selection", "Polygon" }); shortcuts.add({ QKeySequence(Qt::Key_B), "Selection", "Circular brush (mouse wheel adjusts the radius)" }); shortcuts.add({ QKeySequence(Qt::SHIFT), "Selection", "Add to selection" }); shortcuts.add({ QKeySequence(Qt::CTRL), "Selection", "Remove from selection" }); + shortcuts.add({ QKeySequence(Qt::Key_A), "Selection", "Select all" }); + shortcuts.add({ QKeySequence(Qt::Key_E), "Selection", "Clear selection" }); + shortcuts.add({ QKeySequence(Qt::Key_I), "Selection", "Invert selection" }); shortcuts.add({ QKeySequence(Qt::Key_S), "Render", "Scatter mode (default)" }); shortcuts.add({ QKeySequence(Qt::Key_D), "Render", "Density mode" }); shortcuts.add({ QKeySequence(Qt::Key_C), "Render", "Contour mode" }); + shortcuts.add({ QKeySequence(Qt::Key_Plus), "Navigation", "Zoom in by 10%" }); + shortcuts.add({ QKeySequence(Qt::Key_Minus), "Navigation", "Zoom out by 10%" }); shortcuts.add({ QKeySequence(Qt::ALT), "Navigation", "Pan (LMB down)" }); shortcuts.add({ QKeySequence(Qt::ALT), "Navigation", "Zoom (mouse wheel)" }); shortcuts.add({ QKeySequence(Qt::Key_O), "Navigation", "Original view" }); + shortcuts.add({ QKeySequence(Qt::Key_B), "Navigation", "Zoom to selection" }); _dropWidget = new DropWidget(_scatterPlotWidget); @@ -257,8 +264,11 @@ void ScatterplotPlugin::init() renderersNavigationGroupAction->setShowLabels(false); - renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getPointRenderer().getNavigator().getNavigationAction())); - renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getDensityRenderer().getNavigator().getNavigationAction())); + renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getPointRendererNavigator().getNavigationAction())); + renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getDensityRendererNavigator().getNavigationAction())); + + _scatterPlotWidget->getPointRendererNavigator().getNavigationAction().setParent(&_settingsAction); + _scatterPlotWidget->getDensityRendererNavigator().getNavigationAction().setParent(&_settingsAction); navigationLayout->addWidget(renderersNavigationGroupAction->createWidget(&getWidget())); } @@ -295,6 +305,11 @@ void ScatterplotPlugin::init() getLearningCenterAction().getViewPluginOverlayWidget()->setTargetWidget(_scatterPlotWidget); + connect(&getScatterplotWidget().getPointRendererNavigator().getNavigationAction().getZoomSelectionAction(), &TriggerAction::triggered, this, [this]() -> void { + if (_selectionBoundaries.isValid()) + _scatterPlotWidget->getPointRendererNavigator().setZoomRectangleWorld(_selectionBoundaries); + }); + #ifdef VIEW_SAMPLING_HTML getSamplerAction().setHtmlViewGeneratorFunction([this](const ViewPluginSamplerAction::SampleContext& toolTipContext) -> QString { QStringList localPointIndices, globalPointIndices; @@ -400,27 +415,43 @@ void ScatterplotPlugin::selectPoints() const auto zoomRectangleWorld = navigator.getZoomRectangleWorld(); const auto screenRectangle = QRect(QPoint(), pointRenderer.getRenderSize()); + float boundaries[4]{ + std::numeric_limits::max(), + std::numeric_limits::lowest(), + std::numeric_limits::max(), + std::numeric_limits::lowest() + }; + // Go over all points in the dataset to see if they are selected for (std::uint32_t localPointIndex = 0; localPointIndex < _positions.size(); localPointIndex++) { - - // Compute the offset of the point in the world space - const auto pointOffsetWorld = QPointF(_positions[localPointIndex].x - zoomRectangleWorld.left(), _positions[localPointIndex].y - zoomRectangleWorld.top()); + const auto& point = _positions[localPointIndex]; - // Normalize it - const auto pointOffsetWorldNormalized = QPointF(pointOffsetWorld.x() / zoomRectangleWorld.width(), pointOffsetWorld.y() / zoomRectangleWorld.height()); + // Compute the offset of the point in the world space + const auto pointOffsetWorld = QPointF(point.x - zoomRectangleWorld.left(), point.y - zoomRectangleWorld.top()); - // Convert it to screen space - const auto pointOffsetScreen = QPoint(pointOffsetWorldNormalized.x() * screenRectangle.width(), screenRectangle.height() - pointOffsetWorldNormalized.y() * screenRectangle.height()); + // Normalize it + const auto pointOffsetWorldNormalized = QPointF(pointOffsetWorld.x() / zoomRectangleWorld.width(), pointOffsetWorld.y() / zoomRectangleWorld.height()); - // Continue to next point if the point is outside the screen - if (!screenRectangle.contains(pointOffsetScreen)) - continue; + // Convert it to screen space + const auto pointOffsetScreen = QPoint(pointOffsetWorldNormalized.x() * screenRectangle.width(), screenRectangle.height() - pointOffsetWorldNormalized.y() * screenRectangle.height()); - // If the corresponding pixel is not transparent, add the point to the selection - if (selectionAreaImage.pixelColor(pointOffsetScreen).alpha() > 0) - targetSelectionIndices.push_back(localGlobalIndices[localPointIndex]); + // Continue to next point if the point is outside the screen + if (!screenRectangle.contains(pointOffsetScreen)) + continue; + + // If the corresponding pixel is not transparent, add the point to the selection + if (selectionAreaImage.pixelColor(pointOffsetScreen).alpha() > 0) { + targetSelectionIndices.push_back(localGlobalIndices[localPointIndex]); + + boundaries[0] = std::min(boundaries[0], point.x); + boundaries[1] = std::max(boundaries[1], point.x); + boundaries[2] = std::min(boundaries[2], point.y); + boundaries[3] = std::max(boundaries[3], point.y); + } } + _selectionBoundaries = QRectF(boundaries[0], boundaries[2], boundaries[1] - boundaries[0], boundaries[3] - boundaries[2]); + switch (const auto selectionModifier = pixelSelectionTool.isAborted() ? PixelSelectionModifierType::Subtract : pixelSelectionTool.getModifier()) { case PixelSelectionModifierType::Replace: @@ -467,7 +498,9 @@ void ScatterplotPlugin::selectPoints() } } - _scatterPlotWidget->getPointRendererNavigator().getNavigationAction().getZoomSelectionAction().setEnabled(!targetSelectionIndices.empty()); + auto& navigationAction = _scatterPlotWidget->getPointRendererNavigator().getNavigationAction(); + + navigationAction.getZoomSelectionAction().setEnabled(!targetSelectionIndices.empty() && !navigationAction.getFreezeNavigation().isChecked()); _positionDataset->setSelectionIndices(targetSelectionIndices); diff --git a/src/ScatterplotPlugin.h b/src/ScatterplotPlugin.h index 16d279b..23b8250 100644 --- a/src/ScatterplotPlugin.h +++ b/src/ScatterplotPlugin.h @@ -111,14 +111,13 @@ class ScatterplotPlugin : public ViewPlugin private: mv::gui::DropWidget* _dropWidget; /** Widget for dropping datasets */ ScatterplotWidget* _scatterPlotWidget; /** The visualization widget */ - Dataset _positionDataset; /** Smart pointer to points dataset for point position */ Dataset _positionSourceDataset; /** Smart pointer to source of the points dataset for point position (if any) */ std::vector _positions; /** Point positions */ unsigned int _numPoints; /** Number of point positions */ - SettingsAction _settingsAction; /** Group action for all settings */ HorizontalToolbarAction _primaryToolbarAction; /** Horizontal toolbar for primary content */ + QRectF _selectionBoundaries; /** Boundaries of the selection */ static const std::int32_t LAZY_UPDATE_INTERVAL = 2; diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 083f8e0..11d1f6c 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -205,18 +205,24 @@ void ScatterplotWidget::setRenderMode(const RenderMode& renderMode) switch (_renderMode) { case ScatterplotWidget::SCATTERPLOT: - break; + { + getPointRendererNavigator().setEnabled(true); + getDensityRendererNavigator().setEnabled(false); + + break; + } case ScatterplotWidget::DENSITY: - computeDensity(); - break; - case ScatterplotWidget::LANDSCAPE: - computeDensity(); - break; + { + getPointRendererNavigator().setEnabled(false); + getDensityRendererNavigator().setEnabled(true); - default: - break; + computeDensity(); + _densityRenderer.getNavigator().resetView(); + + break; + } } update(); diff --git a/src/SelectionAction.cpp b/src/SelectionAction.cpp index 5a15802..1e71d63 100644 --- a/src/SelectionAction.cpp +++ b/src/SelectionAction.cpp @@ -4,9 +4,6 @@ #include -#include -#include - using namespace mv::gui; SelectionAction::SelectionAction(QObject* parent, const QString& title) : @@ -79,6 +76,8 @@ void SelectionAction::initialize(ScatterplotPlugin* scatterplotPlugin) PixelSelectionType::Sample }); + _samplerPixelSelectionAction.setShortcutsEnabled(false); + _displayModeAction.setCurrentIndex(static_cast(scatterplotPlugin->getScatterplotWidget().getSelectionDisplayMode())); _outlineScaleAction.setValue(100.0f * scatterplotPlugin->getScatterplotWidget().getSelectionOutlineScale()); _outlineOpacityAction.setValue(100.0f * scatterplotPlugin->getScatterplotWidget().getSelectionOutlineOpacity()); From a1a44c316b55b5375cc5eace968fdf88e1c5f5df Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 7 Apr 2025 13:55:02 +0200 Subject: [PATCH 12/14] Align density renderer with point renderer and fix shortcuts --- src/ScatterplotPlugin.cpp | 11 +++-------- src/ScatterplotWidget.cpp | 33 ++++++++++++--------------------- src/ScatterplotWidget.h | 3 --- 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 469b32b..20b573a 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -72,7 +72,8 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : shortcuts.add({ QKeySequence(Qt::ALT), "Navigation", "Pan (LMB down)" }); shortcuts.add({ QKeySequence(Qt::ALT), "Navigation", "Zoom (mouse wheel)" }); shortcuts.add({ QKeySequence(Qt::Key_O), "Navigation", "Original view" }); - shortcuts.add({ QKeySequence(Qt::Key_B), "Navigation", "Zoom to selection" }); + shortcuts.add({ QKeySequence(Qt::Key_H), "Navigation", "Zoom to selection" }); + shortcuts.add({ QKeySequence(Qt::Key_F), "Navigation", "Zoom to window" }); _dropWidget = new DropWidget(_scatterPlotWidget); @@ -260,15 +261,13 @@ void ScatterplotPlugin::init() navigationLayout->addStretch(1); { - auto renderersNavigationGroupAction = new HorizontalGroupAction(this, "Renderers Navigation"); + auto renderersNavigationGroupAction = new HorizontalGroupAction(this, "Navigation"); renderersNavigationGroupAction->setShowLabels(false); renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getPointRendererNavigator().getNavigationAction())); - renderersNavigationGroupAction->addAction(const_cast(&_scatterPlotWidget->getDensityRendererNavigator().getNavigationAction())); _scatterPlotWidget->getPointRendererNavigator().getNavigationAction().setParent(&_settingsAction); - _scatterPlotWidget->getDensityRendererNavigator().getNavigationAction().setParent(&_settingsAction); navigationLayout->addWidget(renderersNavigationGroupAction->createWidget(&getWidget())); } @@ -353,10 +352,6 @@ void ScatterplotPlugin::init() return pointIndicesTableWidget; }); #endif - - _scatterPlotWidget->updateNavigationActionVisibility(); - - connect(&_settingsAction.getRenderModeAction(), &OptionAction::currentIndexChanged, _scatterPlotWidget, &ScatterplotWidget::updateNavigationActionVisibility); } void ScatterplotPlugin::loadData(const Datasets& datasets) diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 11d1f6c..c4c85ad 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -128,6 +128,10 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : _samplerPixelSelectionTool.setEnabled(true); _samplerPixelSelectionTool.setMainColor(QColor(Qt::black)); _samplerPixelSelectionTool.setFixedBrushRadiusModifier(Qt::AltModifier); + + getPointRendererNavigator().setEnabled(true); + + _densityRenderer.setCustomNavigator(&getPointRendererNavigator()); } bool ScatterplotWidget::event(QEvent* event) @@ -199,27 +203,17 @@ void ScatterplotWidget::setRenderMode(const RenderMode& renderMode) emit renderModeChanged(_renderMode); - _pointRenderer.getNavigator().setEnabled(_renderMode == SCATTERPLOT); - _densityRenderer.getNavigator().setEnabled(_renderMode == DENSITY || _renderMode == LANDSCAPE); - switch (_renderMode) { case ScatterplotWidget::SCATTERPLOT: { - getPointRendererNavigator().setEnabled(true); - getDensityRendererNavigator().setEnabled(false); - break; } case ScatterplotWidget::DENSITY: case ScatterplotWidget::LANDSCAPE: { - getPointRendererNavigator().setEnabled(false); - getDensityRendererNavigator().setEnabled(true); - computeDensity(); - _densityRenderer.getNavigator().resetView(); break; } @@ -272,15 +266,18 @@ void ScatterplotWidget::setData(const std::vector* points) auto dataBounds = getDataBounds(*points); _pointRenderer.setDataBounds(QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight()))); - + _densityRenderer.setDataBounds(QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight()))); + _dataRectangleAction.setBounds(dataBounds); - dataBounds.ensureMinimumSize(1e-07f, 1e-07f); - dataBounds.makeSquare(); - dataBounds.expand(0.1f); + auto densityDataBounds = dataBounds; - _densityRenderer.setDataBounds(QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight()))); + //densityDataBounds.ensureMinimumSize(1e-07f, 1e-07f); + //densityDataBounds.makeSquare(); + //densityDataBounds.expand(0.1f); + _densityRenderer.setDensityComputationDataBounds(QRectF(QPointF(densityDataBounds.getLeft(), densityDataBounds.getBottom()), QSizeF(densityDataBounds.getWidth(), densityDataBounds.getHeight()))); + _pointRenderer.setData(*points); _densityRenderer.setData(points); @@ -601,12 +598,6 @@ bool ScatterplotWidget::getRandomizedDepthEnabled() const return _pointRenderer.getRandomizedDepthEnabled(); } -void ScatterplotWidget::updateNavigationActionVisibility() -{ - _pointRenderer.getNavigator().getNavigationAction().setVisible(getRenderMode() == ScatterplotWidget::SCATTERPLOT); - _densityRenderer.getNavigator().getNavigationAction().setVisible(getRenderMode() == ScatterplotWidget::DENSITY || getRenderMode() == ScatterplotWidget::LANDSCAPE); -} - void ScatterplotWidget::initializeGL() { initializeOpenGLFunctions(); diff --git a/src/ScatterplotWidget.h b/src/ScatterplotWidget.h index cb81a8b..cf0618a 100644 --- a/src/ScatterplotWidget.h +++ b/src/ScatterplotWidget.h @@ -213,9 +213,6 @@ class ScatterplotWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_C */ bool getRandomizedDepthEnabled() const; - /** Toggles the point vs density and landscape renderer visibility based on the current render mode */ - void updateNavigationActionVisibility(); - protected: void initializeGL() Q_DECL_OVERRIDE; void resizeGL(int w, int h) Q_DECL_OVERRIDE; From 40f1baf41c5b70f2456a6fae1ee6e3efd3ccc277 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 14 Apr 2025 13:58:03 +0200 Subject: [PATCH 13/14] Use constructor overload --- src/ScatterplotWidget.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index c4c85ad..ad1b72b 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -37,7 +37,8 @@ namespace } ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : - _densityRenderer(DensityRenderer::RenderMode::DENSITY), + _densityRenderer(DensityRenderer::RenderMode::DENSITY, this), + _pointRenderer(this), _isInitialized(false), _renderMode(SCATTERPLOT), _backgroundColor(255, 255, 255, 255), @@ -122,9 +123,6 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : connect(&getPointRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this]() -> void { update(); }); connect(&getDensityRendererNavigator(), &Navigator2D::zoomRectangleWorldChanged, this, [this]() -> void { update(); }); - _pointRenderer.getNavigator().initialize(this); - _densityRenderer.getNavigator().initialize(this); - _samplerPixelSelectionTool.setEnabled(true); _samplerPixelSelectionTool.setMainColor(QColor(Qt::black)); _samplerPixelSelectionTool.setFixedBrushRadiusModifier(Qt::AltModifier); From 83efa3e7f17d7b5270cfa41e064a50105eed5609 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 28 Apr 2025 10:50:00 +0200 Subject: [PATCH 14/14] Support older projects by resetting the view when the navigation action has been serialized from and some style improvement --- src/ScatterplotPlugin.cpp | 9 +++++++++ src/ScatterplotWidget.cpp | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 20b573a..3cf788a 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -855,6 +855,15 @@ void ScatterplotPlugin::fromVariantMap(const QVariantMap& variantMap) _primaryToolbarAction.fromParentVariantMap(variantMap); _settingsAction.fromParentVariantMap(variantMap); + + auto& pointRenderer = const_cast(_scatterPlotWidget->getPointRenderer()); + + if (pointRenderer.getNavigator().getNavigationAction().getSerializationCountFrom() == 0) { + qDebug() << "Resetting view"; + pointRenderer.getNavigator().resetView(true); + + _scatterPlotWidget->update(); + } } QVariantMap ScatterplotPlugin::toVariantMap() const diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index ad1b72b..5ad29d3 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -263,8 +263,10 @@ void ScatterplotWidget::setData(const std::vector* points) { auto dataBounds = getDataBounds(*points); - _pointRenderer.setDataBounds(QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight()))); - _densityRenderer.setDataBounds(QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight()))); + const auto dataBoundsRect = QRectF(QPointF(dataBounds.getLeft(), dataBounds.getBottom()), QSizeF(dataBounds.getWidth(), dataBounds.getHeight())); + + _pointRenderer.setDataBounds(dataBoundsRect); + _densityRenderer.setDataBounds(dataBoundsRect); _dataRectangleAction.setBounds(dataBounds);