From 32a52d1ed336535d79d6714312ea6d459cf98926 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 10:13:03 +0100 Subject: [PATCH 01/89] Remove unused image renderer and add two-dimensional renderer --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 4 ++-- ManiVault/src/renderers/{ImageRenderer.cpp => Renderer2D.cpp} | 0 ManiVault/src/renderers/{ImageRenderer.h => Renderer2D.h} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename ManiVault/src/renderers/{ImageRenderer.cpp => Renderer2D.cpp} (100%) rename ManiVault/src/renderers/{ImageRenderer.h => Renderer2D.h} (100%) diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index 180a10dcf..c47abc0b3 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -470,13 +470,13 @@ set(PUBLIC_RENDERERS_HEADERS src/renderers/Renderer.h src/renderers/PointRenderer.h src/renderers/DensityRenderer.h - src/renderers/ImageRenderer.h + src/renderers/Renderer2D.h ) set(PUBLIC_RENDERERS_SOURCES src/renderers/PointRenderer.cpp src/renderers/DensityRenderer.cpp - src/renderers/ImageRenderer.cpp + src/renderers/Renderer2D.cpp ) set(PUBLIC_RENDERERS_FILES diff --git a/ManiVault/src/renderers/ImageRenderer.cpp b/ManiVault/src/renderers/Renderer2D.cpp similarity index 100% rename from ManiVault/src/renderers/ImageRenderer.cpp rename to ManiVault/src/renderers/Renderer2D.cpp diff --git a/ManiVault/src/renderers/ImageRenderer.h b/ManiVault/src/renderers/Renderer2D.h similarity index 100% rename from ManiVault/src/renderers/ImageRenderer.h rename to ManiVault/src/renderers/Renderer2D.h From d0e88ff3b187d2cdf66ce4b0a4147a4079e65905 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 10:35:11 +0100 Subject: [PATCH 02/89] Work on Renderer2D class --- ManiVault/src/renderers/PointRenderer.h | 4 +-- ManiVault/src/renderers/Renderer.h | 4 ++- ManiVault/src/renderers/Renderer2D.cpp | 25 +++++----------- ManiVault/src/renderers/Renderer2D.h | 39 ++++++++++++++++++------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 36a5e124c..f69763f7c 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -4,7 +4,7 @@ #pragma once -#include "Renderer.h" +#include "Renderer2D.h" #include "graphics/Bounds.h" #include "graphics/BufferObject.h" @@ -127,7 +127,7 @@ namespace mv float _alpha = DEFAULT_ALPHA_VALUE; }; - class CORE_EXPORT PointRenderer : public Renderer + class CORE_EXPORT PointRenderer : public Renderer2D { public: void setData(const std::vector& points); diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index 3f0bef283..6d5b1624f 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -19,8 +19,10 @@ namespace mv { - class CORE_EXPORT Renderer : protected QOpenGLFunctions_3_3_Core + class CORE_EXPORT Renderer : protected QObject, protected QOpenGLFunctions_3_3_Core { + explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + virtual void init() = 0; virtual void resize(QSize renderSize) = 0; virtual void render() = 0; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index a6dc29b9c..aff3cc7a1 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -2,27 +2,18 @@ // A corresponding LICENSE file is located in the root directory of this source tree // Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) -#include "ImageRenderer.h" +#include "Renderer2D.h" namespace mv { - void ImageRenderer::init() - { - - } - - void ImageRenderer::resize(QSize renderSize) - { - - } - - void ImageRenderer::render() - { +Renderer2D::Renderer2D(QWidget* sourceWidget, QObject* parent) : + Renderer(parent), + _sourceWidget(sourceWidget) +{ + if (_sourceWidget) { + _sourceWidget->installEventFilter(this); } +} - void ImageRenderer::destroy() - { - - } } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 319dcb808..dfbc90d1f 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -8,14 +8,31 @@ namespace mv { - class CORE_EXPORT ImageRenderer : public Renderer - { - public: - void init() override; - void resize(QSize renderSize) override; - void render() override; - void destroy() override; - private: - ShaderProgram _shader; - }; -} // namespace mv + +/** + * Renderer 2D class + * + * Supports two-dimensional rendering: + * - Organizes panning and zooming + * - Sets up the matrix transformations + * - Renders 2D data + * + * @author Thomas Kroes + */ +class CORE_EXPORT Renderer2D : public Renderer +{ +public: + + /** + * Construct a new two-dimensional renderer + * + * @param sourceWidget If set, use this widget to do panning and zooming + * @param parent Pointer to the parent object + */ + explicit Renderer2D(QWidget* sourceWidget = nullptr, QObject* parent = nullptr); + +private: + QPointer _sourceWidget; /** Source widget for panning and zooming */ +}; + +} From 3c2c17ec95ec6bca0055c83c623bf17ba8bafc5b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 12:11:05 +0100 Subject: [PATCH 03/89] Work on Renderer2D class --- .../src/actions/PixelSelectionAction.cpp | 8 +- ManiVault/src/renderers/PointRenderer.h | 2 + ManiVault/src/renderers/Renderer.h | 4 +- ManiVault/src/renderers/Renderer2D.cpp | 280 +++++++++++++++++- ManiVault/src/renderers/Renderer2D.h | 144 +++++++++ 5 files changed, 432 insertions(+), 6 deletions(-) diff --git a/ManiVault/src/actions/PixelSelectionAction.cpp b/ManiVault/src/actions/PixelSelectionAction.cpp index be774cff0..2c6c5441f 100644 --- a/ManiVault/src/actions/PixelSelectionAction.cpp +++ b/ManiVault/src/actions/PixelSelectionAction.cpp @@ -417,15 +417,15 @@ QMenu* PixelSelectionAction::getContextMenu() bool PixelSelectionAction::eventFilter(QObject* object, QEvent* event) { if (!isEnabled()) - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); const auto keyEvent = dynamic_cast(event); if (!keyEvent) - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); if (keyEvent->isAutoRepeat()) - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); switch (keyEvent->type()) { @@ -471,7 +471,7 @@ bool PixelSelectionAction::eventFilter(QObject* object, QEvent* event) break; } - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); } void PixelSelectionAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index f69763f7c..86cc5452a 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -130,6 +130,8 @@ namespace mv class CORE_EXPORT PointRenderer : public Renderer2D { public: + using Renderer2D::Renderer2D; + void setData(const std::vector& points); void setHighlights(const std::vector& highlights, const std::int32_t& numSelectedPoints); void setFocusHighlights(const std::vector& focusHighlights, const std::int32_t& numberOfFocusHighlights); diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index 6d5b1624f..244bddb79 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -19,10 +19,12 @@ namespace mv { - class CORE_EXPORT Renderer : protected QObject, protected QOpenGLFunctions_3_3_Core + class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core { + public: explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + protected: virtual void init() = 0; virtual void resize(QSize renderSize) = 0; virtual void render() = 0; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index aff3cc7a1..f6209fe37 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -4,16 +4,294 @@ #include "Renderer2D.h" +#ifdef _DEBUG + //#define RENDERER_2D_VERBOSE +#endif + +#define RENDERER_2D_VERBOSE + namespace mv { Renderer2D::Renderer2D(QWidget* sourceWidget, QObject* parent) : Renderer(parent), - _sourceWidget(sourceWidget) + _sourceWidget(sourceWidget), + _isNavigating(false), + _isPanning(false), + _isZooming(false) { if (_sourceWidget) { _sourceWidget->installEventFilter(this); + _sourceWidget->setFocusPolicy(Qt::StrongFocus); + } +} + +bool Renderer2D::eventFilter(QObject* watched, QEvent* event) +{ + if (event->type() == QEvent::KeyPress) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + beginNavigation(); + + return Renderer::eventFilter(watched, event); + } + } + } + + if (event->type() == QEvent::KeyRelease) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + endNavigation(); + + return Renderer::eventFilter(watched, event); + } + } + } + + if (isNavigating()) { + + if (event->type() == QEvent::Wheel) { + if (auto* wheelEvent = dynamic_cast(event)) + zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); + } + + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + if (mouseEvent->button() == Qt::MiddleButton) + resetView(); + + if (mouseEvent->buttons() == Qt::LeftButton) + { + _sourceWidget->setCursor(Qt::ClosedHandCursor); + + _mousePositions << mouseEvent->pos(); + + _sourceWidget->update(); + } + } + } + + if (event->type() == QEvent::MouseButtonRelease) { + _sourceWidget->setCursor(Qt::ArrowCursor); + + _mousePositions.clear(); + + _sourceWidget->update(); + } + + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + _mousePositions << mouseEvent->pos(); + + 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; + + panBy(panVector); + } + } + } + } + + return Renderer::eventFilter(watched, event); +} + +void Renderer2D::zoomAround(const QPointF& center, float factor) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__ << center << factor; +#endif + + beginZooming(); + { + + } + endZooming(); + + // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); +} + +void Renderer2D::zoomToRectangle(const QRectF& zoomRectangle) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__ << zoomRectangle; +#endif + + beginZooming(); + { + + } + endZooming(); + + //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 Renderer2D::panBy(const QPointF& to) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__ << to; +#endif + + beginPanning(); + { + } + endPanning(); + + //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(); +} + +void Renderer2D::resetView() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif +} + +bool Renderer2D::isPanning() const +{ + return _isPanning; +} + +bool Renderer2D::isZooming() const +{ + return _isZooming; +} + +bool Renderer2D::isNavigating() const +{ + return _isNavigating; +} + +void Renderer2D::setIsPanning(bool isPanning) +{ + if (isPanning == _isPanning) + return; + + _isPanning = isPanning; + + emit isPanningChanged(_isPanning); +} + +void Renderer2D::setIsZooming(bool isZooming) +{ + if (isZooming == _isZooming) + return; + + _isZooming = isZooming; + + emit isZoomingChanged(_isZooming); +} + +void Renderer2D::setIsNavigating(bool isNavigating) +{ + if (isNavigating == _isNavigating) + return; + + _isNavigating = isNavigating; + + emit isNavigatingChanged(_isNavigating); +} + +void Renderer2D::beginPanning() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsPanning(true); + + emit panningStarted(); +} + +void Renderer2D::endPanning() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsPanning(false); + + emit panningEnded(); +} + +void Renderer2D::beginZooming() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsZooming(true); + + emit zoomingStarted(); +} + +void Renderer2D::endZooming() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsZooming(false); + + emit zoomingEnded(); +} + +void Renderer2D::beginNavigation() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsNavigating(true); + + emit navigationStarted(); +} + +void Renderer2D::endNavigation() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsNavigating(false); + + emit navigationEnded(); } } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index dfbc90d1f..841dbc405 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -21,6 +21,9 @@ namespace mv */ class CORE_EXPORT Renderer2D : public Renderer { + +Q_OBJECT + public: /** @@ -31,8 +34,149 @@ class CORE_EXPORT Renderer2D : public Renderer */ explicit Renderer2D(QWidget* sourceWidget = nullptr, QObject* parent = nullptr); + /** + * Watch \p watched for events + * + * @param watched Watched object + * @param event Event + * @return True if the event was handled, false otherwise + */ + bool eventFilter(QObject* watched, QEvent* event) override; + +public: // Navigation + + /** + * Zoom by \p factor around \p center + * + * @param at Point to zoom around + * @param factor Zoom factor + */ + void zoomAround(const QPointF& center, float factor); + + /** + * Zoom to \p zoomRectangle + * + * @param zoomRectangle Zoom to this rectangle + */ + void zoomToRectangle(const QRectF& zoomRectangle); + + /** + * Pan by \p to + * + * @param to Pan by this amount + */ + void panBy(const QPointF& to); + + /** Zoom to extents of the data bounds (with a margin around it) */ + void resetView(); + + /** + * Get whether the renderer is panning + * + * @return Boolean determining whether the renderer is panning + */ + bool isPanning() const; + + /** + * Get whether the renderer is zooming + * + * @return Boolean determining whether the renderer is zooming + */ + bool isZooming() const; + + /** + * Get whether the renderer is navigating + * + * @return Boolean determining whether the renderer is navigating + */ + bool isNavigating() const; + +protected: // Navigation + + /** + * Set whether the renderer is panning to \p isPanning + * + * @param isPanning Boolean determining whether the renderer is panning + */ + void setIsPanning(bool isPanning); + + /** + * Set whether the renderer is zooming to \p isZooming + * + * @param isZooming Boolean determining whether the renderer is zooming + */ + void setIsZooming(bool isZooming); + + /** + * Set whether the renderer is navigating to \p isNavigating + * + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void setIsNavigating(bool isNavigating); + + + /** Panning has begun */ + void beginPanning(); + + /** Panning has ended */ + void endPanning(); + + /** Zooming has begun */ + void beginZooming(); + + /** Zooming has ended */ + void endZooming(); + + /** Navigation has begun */ + void beginNavigation(); + + /** Navigation has ended */ + void endNavigation(); + +signals: + + /** Signals that panning has started */ + void panningStarted(); + + /** Signals that panning has ended */ + void panningEnded(); + + /** + * Signals that is panning changed to \p isPanning + * @param isPanning + */ + void isPanningChanged(bool isPanning); + + /** Signals that zooming has started */ + void zoomingStarted(); + + /** Signals that zooming has ended */ + void zoomingEnded(); + + /** + * Signals that is zooming changed to \p isZooming + * @param isZooming + */ + void isZoomingChanged(bool isZooming); + + /** Signals that navigation has started */ + void navigationStarted(); + + /** Signals that navigation has ended */ + void navigationEnded(); + + /** + * Signals that is navigating changed to \p isNavigating + * @param isNavigating + */ + void isNavigatingChanged(bool isNavigating); + private: QPointer _sourceWidget; /** Source widget for panning and zooming */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ }; } From d2afe65722cecd24f71262f64c1afe60545f7e06 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 13:10:52 +0100 Subject: [PATCH 04/89] Added a two-dimensional navigator class for two-dimensional renderers --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 2 + ManiVault/src/renderers/DensityRenderer.cpp | 3 +- ManiVault/src/renderers/DensityRenderer.h | 10 +- ManiVault/src/renderers/Navigator2D.cpp | 351 ++++++++++++++++++++ ManiVault/src/renderers/Navigator2D.h | 190 +++++++++++ ManiVault/src/renderers/PointRenderer.cpp | 89 +++-- ManiVault/src/renderers/PointRenderer.h | 5 - ManiVault/src/renderers/Renderer.h | 44 ++- ManiVault/src/renderers/Renderer2D.cpp | 271 +-------------- ManiVault/src/renderers/Renderer2D.h | 157 ++------- 10 files changed, 657 insertions(+), 465 deletions(-) create mode 100644 ManiVault/src/renderers/Navigator2D.cpp create mode 100644 ManiVault/src/renderers/Navigator2D.h diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index c47abc0b3..35ea7e91e 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -471,12 +471,14 @@ set(PUBLIC_RENDERERS_HEADERS src/renderers/PointRenderer.h src/renderers/DensityRenderer.h src/renderers/Renderer2D.h + src/renderers/Navigator2D.h ) set(PUBLIC_RENDERERS_SOURCES src/renderers/PointRenderer.cpp src/renderers/DensityRenderer.cpp src/renderers/Renderer2D.cpp + src/renderers/Navigator2D.cpp ) set(PUBLIC_RENDERERS_FILES diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index c7237ed8f..04f0f238e 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -9,8 +9,7 @@ namespace mv namespace gui { - DensityRenderer::DensityRenderer(RenderMode renderMode) - : + DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 8f5826940..3ab56fd63 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -4,7 +4,7 @@ #pragma once -#include "Renderer.h" +#include "Renderer2D.h" #include "graphics/Bounds.h" #include "graphics/Shader.h" @@ -21,7 +21,7 @@ namespace mv namespace gui { - class CORE_EXPORT DensityRenderer : public Renderer + class CORE_EXPORT DensityRenderer : public Renderer2D { public: @@ -50,8 +50,10 @@ namespace mv void init() override; void resize(QSize renderSize) override; - void render() override; - void destroy() override; + + void render() override; + + void destroy() override; void setColorMapRange(const float& min, const float& max); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp new file mode 100644 index 000000000..8e5739374 --- /dev/null +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "Navigator2D.h" +#include "Renderer2D.h" + +#ifdef _DEBUG + //#define NAVIGATOR_2D_VERBOSE +#endif + +#define NAVIGATOR_2D_VERBOSE + +namespace mv +{ + +Navigator2D::Navigator2D(QObject* parent) : + QObject(parent), + _initialized(false), + _isNavigating(false), + _isPanning(false), + _isZooming(false) +{ +} + +void Navigator2D::initialize(QWidget* sourceWidget, Renderer2D* renderer) +{ + Q_ASSERT(sourceWidget && renderer); + + if (sourceWidget && renderer) { + _sourceWidget = sourceWidget; + _renderer = renderer; + + _sourceWidget->installEventFilter(this); + _sourceWidget->setFocusPolicy(Qt::StrongFocus); + + _initialized = true; + } +} + +bool Navigator2D::eventFilter(QObject* watched, QEvent* event) +{ + if (!_initialized) + return false; + + if (event->type() == QEvent::KeyPress) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + beginNavigation(); + + return QObject::eventFilter(watched, event); + } + } + } + + if (event->type() == QEvent::KeyRelease) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + endNavigation(); + + return QObject::eventFilter(watched, event); + } + } + } + + if (isNavigating()) { + + if (event->type() == QEvent::Wheel) { + if (auto* wheelEvent = dynamic_cast(event)) + zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); + } + + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + if (mouseEvent->button() == Qt::MiddleButton) + resetView(); + + if (mouseEvent->buttons() == Qt::LeftButton) + { + _sourceWidget->setCursor(Qt::ClosedHandCursor); + + _mousePositions << mouseEvent->pos(); + + _sourceWidget->update(); + } + } + } + + if (event->type() == QEvent::MouseButtonRelease) { + _sourceWidget->setCursor(Qt::ArrowCursor); + + _mousePositions.clear(); + + _sourceWidget->update(); + } + + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + _mousePositions << mouseEvent->pos(); + + 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; + + panBy(panVector); + } + } + } + } + + return QObject::eventFilter(watched, event); +} + +void Navigator2D::zoomAround(const QPointF& center, float factor) +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << center << factor; +#endif + + beginZooming(); + { + + } + endZooming(); + + // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); +} + +void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << zoomRectangle; +#endif + + beginZooming(); + { + + } + endZooming(); + + //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 Navigator2D::panBy(const QPointF& to) +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << to; +#endif + + beginPanning(); + { + + } + endPanning(); + + //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(); +} + +void Navigator2D::resetView() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif +} + +bool Navigator2D::isPanning() const +{ + return _isPanning; +} + +bool Navigator2D::isZooming() const +{ + return _isZooming; +} + +bool Navigator2D::isNavigating() const +{ + return _isNavigating; +} + +void Navigator2D::setIsPanning(bool isPanning) +{ + if (!_initialized) + return; + + if (isPanning == _isPanning) + return; + + _isPanning = isPanning; + + emit isPanningChanged(_isPanning); +} + +void Navigator2D::setIsZooming(bool isZooming) +{ + if (!_initialized) + return; + + if (isZooming == _isZooming) + return; + + _isZooming = isZooming; + + emit isZoomingChanged(_isZooming); +} + +void Navigator2D::setIsNavigating(bool isNavigating) +{ + if (!_initialized) + return; + + if (isNavigating == _isNavigating) + return; + + _isNavigating = isNavigating; + + emit isNavigatingChanged(_isNavigating); +} + +void Navigator2D::beginPanning() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsPanning(true); + + emit panningStarted(); +} + +void Navigator2D::endPanning() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsPanning(false); + + emit panningEnded(); +} + +void Navigator2D::beginZooming() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsZooming(true); + + emit zoomingStarted(); +} + +void Navigator2D::endZooming() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsZooming(false); + + emit zoomingEnded(); +} + +void Navigator2D::beginNavigation() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsNavigating(true); + + emit navigationStarted(); +} + +void Navigator2D::endNavigation() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsNavigating(false); + + emit navigationEnded(); +} + +} diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h new file mode 100644 index 000000000..e23cbd38d --- /dev/null +++ b/ManiVault/src/renderers/Navigator2D.h @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include + +namespace mv +{ + +class Renderer2D; + +/** + * Navigator 2D class + * + * Orchestrates panning and zooming in a widget that displays 2D data using Renderer2D + * + * @author Thomas Kroes + */ +class CORE_EXPORT Navigator2D : public QObject +{ + +Q_OBJECT + +public: + + /** + * Construct a new two-dimensional navigator + * + * @param parent Pointer to the parent object + */ + explicit Navigator2D(QObject* parent = nullptr); + + /** + * Initializes the two-dimensional navigator with a \p widget and a \p renderer + * + * @param sourceWidget Pointer to the renderer widget + * @param renderer Pointer to the renderer + */ + void initialize(QWidget* sourceWidget, Renderer2D* renderer); + + /** + * Watch \p watched for events + * + * @param watched Watched object + * @param event Event + * @return True if the event was handled, false otherwise + */ + bool eventFilter(QObject* watched, QEvent* event) override; + +public: // Navigation + + /** + * Zoom by \p factor around \p center + * + * @param center Point to zoom around + * @param factor Zoom factor + */ + void zoomAround(const QPointF& center, float factor); + + /** + * Zoom to \p zoomRectangle + * + * @param zoomRectangle Zoom to this rectangle + */ + void zoomToRectangle(const QRectF& zoomRectangle); + + /** + * Pan by \p to + * + * @param to Pan by this amount + */ + void panBy(const QPointF& to); + + /** Zoom to extents of the data bounds (with a margin around it) */ + void resetView(); + + /** + * Get whether the renderer is panning + * + * @return Boolean determining whether the renderer is panning + */ + bool isPanning() const; + + /** + * Get whether the renderer is zooming + * + * @return Boolean determining whether the renderer is zooming + */ + bool isZooming() const; + + /** + * Get whether the renderer is navigating + * + * @return Boolean determining whether the renderer is navigating + */ + bool isNavigating() const; + +protected: // Navigation + + /** + * Set whether the renderer is panning to \p isPanning + * + * @param isPanning Boolean determining whether the renderer is panning + */ + void setIsPanning(bool isPanning); + + /** + * Set whether the renderer is zooming to \p isZooming + * + * @param isZooming Boolean determining whether the renderer is zooming + */ + void setIsZooming(bool isZooming); + + /** + * Set whether the renderer is navigating to \p isNavigating + * + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void setIsNavigating(bool isNavigating); + + + /** Panning has begun */ + void beginPanning(); + + /** Panning has ended */ + void endPanning(); + + /** Zooming has begun */ + void beginZooming(); + + /** Zooming has ended */ + void endZooming(); + + /** Navigation has begun */ + void beginNavigation(); + + /** Navigation has ended */ + void endNavigation(); + +signals: + + /** Signals that panning has started */ + void panningStarted(); + + /** Signals that panning has ended */ + void panningEnded(); + + /** + * Signals that is panning changed to \p isPanning + * @param isPanning + */ + void isPanningChanged(bool isPanning); + + /** Signals that zooming has started */ + void zoomingStarted(); + + /** Signals that zooming has ended */ + void zoomingEnded(); + + /** + * Signals that is zooming changed to \p isZooming + * @param isZooming + */ + void isZoomingChanged(bool isZooming); + + /** Signals that navigation has started */ + void navigationStarted(); + + /** Signals that navigation has ended */ + void navigationEnded(); + + /** + * Signals that is navigating changed to \p isNavigating + * @param isNavigating + */ + void isNavigatingChanged(bool isNavigating); + +private: + QPointer _sourceWidget; /** Source widget for panning and zooming */ + QPointer _renderer; /** Source widget for panning and zooming */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ +}; + +} diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index a2cdeb1c7..2381abbcf 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -369,11 +369,6 @@ namespace mv return _gpuPoints; } - QSize PointRenderer::getWindowsSize() const - { - return _windowSize; - } - std::int32_t PointRenderer::getNumSelectedPoints() const { return _numSelectedPoints; @@ -485,66 +480,56 @@ namespace mv } } - void PointRenderer::resize(QSize renderSize) - { - int w = renderSize.width(); - int h = renderSize.height(); - - _windowSize.setWidth(w); - _windowSize.setHeight(h); - } - void PointRenderer::render() { - int w = _windowSize.width(); - int h = _windowSize.height(); - int size = w < h ? w : h; + beginRender(); + { + // World to clip transformation + _orthoM = createProjectionMatrix(_boundsView); - glViewport(w / 2 - size / 2, h / 2 - size / 2, size, size); + _shader.bind(); - // World to clip transformation - _orthoM = createProjectionMatrix(_boundsView); + const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; + const auto size = std::max(getRenderSize().width(), getRenderSize().height()); - _shader.bind(); + _shader.uniform1f("pointSize", _pointSettings._pointSize); + _shader.uniform1f("pointSizeScale", absoluteRendering ? (1.0 / size) : 1.0f / size); - // Point size uniforms - bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; - _shader.uniform1f("pointSize", _pointSettings._pointSize); - _shader.uniform1f("pointSizeScale", absoluteRendering ? (1.0 / size) : 1.0f / size); + _shader.uniformMatrix3f("orthoM", _orthoM); + _shader.uniform1f("pointOpacity", _pointSettings._alpha); + _shader.uniform1i("scalarEffect", _pointEffect); - _shader.uniformMatrix3f("orthoM", _orthoM); - _shader.uniform1f("pointOpacity", _pointSettings._alpha); - _shader.uniform1i("scalarEffect", _pointEffect); - - _shader.uniform4f("dataBounds", _boundsData.getLeft(), _boundsData.getRight(), _boundsData.getBottom(), _boundsData.getTop()); + _shader.uniform4f("dataBounds", _boundsData.getLeft(), _boundsData.getRight(), _boundsData.getBottom(), _boundsData.getTop()); - _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); - _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); - _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); - _shader.uniform1i("selectionOutlineOverrideColor", _selectionOutlineOverrideColor); - _shader.uniform1f("selectionOutlineOpacity", _selectionOutlineOpacity); - _shader.uniform1i("selectionHaloEnabled", _selectionHaloEnabled); + _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); + _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); + _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); + _shader.uniform1i("selectionOutlineOverrideColor", _selectionOutlineOverrideColor); + _shader.uniform1f("selectionOutlineOpacity", _selectionOutlineOpacity); + _shader.uniform1i("selectionHaloEnabled", _selectionHaloEnabled); - _shader.uniform1i("randomizedDepthEnabled", _randomizedDepthEnabled); + _shader.uniform1i("randomizedDepthEnabled", _randomizedDepthEnabled); - _shader.uniform1i("hasHighlights", _gpuPoints.hasHighlights()); - _shader.uniform1i("hasFocusHighlights", _gpuPoints.hasFocusHighlights()); - _shader.uniform1i("hasScalars", _gpuPoints.hasColorScalars()); - _shader.uniform1i("hasColors", _gpuPoints.hasColors()); - _shader.uniform1i("hasSizes", _gpuPoints.hasSizeScalars()); - _shader.uniform1i("hasOpacities", _gpuPoints.hasOpacityScalars()); - _shader.uniform1i("numSelectedPoints", _numSelectedPoints); + _shader.uniform1i("hasHighlights", _gpuPoints.hasHighlights()); + _shader.uniform1i("hasFocusHighlights", _gpuPoints.hasFocusHighlights()); + _shader.uniform1i("hasScalars", _gpuPoints.hasColorScalars()); + _shader.uniform1i("hasColors", _gpuPoints.hasColors()); + _shader.uniform1i("hasSizes", _gpuPoints.hasSizeScalars()); + _shader.uniform1i("hasOpacities", _gpuPoints.hasOpacityScalars()); + _shader.uniform1i("numSelectedPoints", _numSelectedPoints); - if (_gpuPoints.hasColorScalars()) - _shader.uniform3f("colorMapRange", _gpuPoints.getColorMapRange()); + if (_gpuPoints.hasColorScalars()) + _shader.uniform3f("colorMapRange", _gpuPoints.getColorMapRange()); - if (_colormap.isCreated() && (_pointEffect == PointEffect::Color || _pointEffect == PointEffect::Color2D)) - { - _colormap.bind(0); - _shader.uniform1i("colormap", 0); - } + if (_colormap.isCreated() && (_pointEffect == PointEffect::Color || _pointEffect == PointEffect::Color2D)) + { + _colormap.bind(0); + _shader.uniform1i("colormap", 0); + } - _gpuPoints.draw(); + _gpuPoints.draw(); + } + endRender(); } void PointRenderer::destroy() diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 86cc5452a..20f42507c 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -166,7 +166,6 @@ namespace mv Matrix3f getProjectionMatrix() const; const PointArrayObject& getGpuPoints() const; - QSize getWindowsSize() const; std::int32_t getNumSelectedPoints() const; const PointSettings& getPointSettings() const; @@ -198,7 +197,6 @@ namespace mv void setRandomizedDepthEnabled(bool randomizedDepth); void init() override; - void resize(QSize renderSize) override; void render() override; void destroy() override; @@ -221,9 +219,6 @@ namespace mv /* Depth control */ bool _randomizedDepthEnabled = true; - /* Window properties */ - QSize _windowSize; - /* Rendering variables */ ShaderProgram _shader; diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index 244bddb79..f556b8b6f 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -19,15 +19,37 @@ namespace mv { - class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core - { - public: - explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} - - protected: - virtual void init() = 0; - virtual void resize(QSize renderSize) = 0; - virtual void render() = 0; - virtual void destroy() = 0; - }; + +class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core +{ +protected: + + /** + * Construct a new renderer + * @param parent Pointer to the parent object + */ + explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + + virtual void init() = 0; + virtual void resize(QSize renderSize) = 0; + + /** + * Get the render size + * @return Render size + */ + virtual QSize getRenderSize() const = 0; + + /** Begin rendering */ + virtual void beginRender() = 0; + + /** Render */ + virtual void render() = 0; + + /** End rendering */ + virtual void endRender() = 0; + + /** Destroy the renderer */ + virtual void destroy() = 0; +}; + } diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index f6209fe37..a7dd4ebb1 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -13,285 +13,42 @@ namespace mv { -Renderer2D::Renderer2D(QWidget* sourceWidget, QObject* parent) : - Renderer(parent), - _sourceWidget(sourceWidget), - _isNavigating(false), - _isPanning(false), - _isZooming(false) +Renderer2D::Renderer2D(QObject* parent) : + Renderer(parent) { - if (_sourceWidget) { - _sourceWidget->installEventFilter(this); - _sourceWidget->setFocusPolicy(Qt::StrongFocus); - } } -bool Renderer2D::eventFilter(QObject* watched, QEvent* event) +void Renderer2D::resize(QSize renderSize) { - if (event->type() == QEvent::KeyPress) { - if (const auto* keyEvent = dynamic_cast(event)) { - if (keyEvent->key() == Qt::Key_Alt) { - beginNavigation(); - - return Renderer::eventFilter(watched, event); - } - } - } - - if (event->type() == QEvent::KeyRelease) { - if (const auto* keyEvent = dynamic_cast(event)) { - if (keyEvent->key() == Qt::Key_Alt) { - endNavigation(); - - return Renderer::eventFilter(watched, event); - } - } - } - - if (isNavigating()) { - - if (event->type() == QEvent::Wheel) { - if (auto* wheelEvent = dynamic_cast(event)) - zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); - } - - if (event->type() == QEvent::MouseButtonPress) { - if (const auto* mouseEvent = dynamic_cast(event)) - { - if (mouseEvent->button() == Qt::MiddleButton) - resetView(); - - if (mouseEvent->buttons() == Qt::LeftButton) - { - _sourceWidget->setCursor(Qt::ClosedHandCursor); - - _mousePositions << mouseEvent->pos(); - - _sourceWidget->update(); - } - } - } - - if (event->type() == QEvent::MouseButtonRelease) { - _sourceWidget->setCursor(Qt::ArrowCursor); - - _mousePositions.clear(); - - _sourceWidget->update(); - } - - if (event->type() == QEvent::MouseMove) { - if (const auto* mouseEvent = dynamic_cast(event)) - { - _mousePositions << mouseEvent->pos(); - - 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; - - panBy(panVector); - } - } - } - } - - return Renderer::eventFilter(watched, event); -} - -void Renderer2D::zoomAround(const QPointF& center, float factor) -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__ << center << factor; -#endif - - beginZooming(); - { - - } - endZooming(); - - // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); -} - -void Renderer2D::zoomToRectangle(const QRectF& zoomRectangle) -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__ << zoomRectangle; -#endif - - beginZooming(); - { - - } - endZooming(); - - //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 Renderer2D::panBy(const QPointF& to) -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__ << to; -#endif - - beginPanning(); - { - - } - endPanning(); - - //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(); -} - -void Renderer2D::resetView() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif -} - -bool Renderer2D::isPanning() const -{ - return _isPanning; -} - -bool Renderer2D::isZooming() const -{ - return _isZooming; -} - -bool Renderer2D::isNavigating() const -{ - return _isNavigating; -} - -void Renderer2D::setIsPanning(bool isPanning) -{ - if (isPanning == _isPanning) - return; - - _isPanning = isPanning; - - emit isPanningChanged(_isPanning); + _renderSize = renderSize; } -void Renderer2D::setIsZooming(bool isZooming) +QSize Renderer2D::getRenderSize() const { - if (isZooming == _isZooming) - return; - - _isZooming = isZooming; - - emit isZoomingChanged(_isZooming); + return _renderSize; } -void Renderer2D::setIsNavigating(bool isNavigating) +Navigator2D& Renderer2D::getNavigator() { - if (isNavigating == _isNavigating) - return; - - _isNavigating = isNavigating; - - emit isNavigatingChanged(_isNavigating); + return _navigator; } -void Renderer2D::beginPanning() +void Renderer2D::beginRender() { #ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; + qDebug() << __FUNCTION__; #endif - setIsPanning(true); + const auto size = _renderSize.width() < _renderSize.height() ? _renderSize.width() : _renderSize.height(); - emit panningStarted(); + glViewport(_renderSize.width() / 2 - size / 2, _renderSize.height() / 2 - size / 2, size, size); } -void Renderer2D::endPanning() +void Renderer2D::endRender() { #ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; + qDebug() << __FUNCTION__; #endif - - setIsPanning(false); - - emit panningEnded(); -} - -void Renderer2D::beginZooming() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsZooming(true); - - emit zoomingStarted(); -} - -void Renderer2D::endZooming() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsZooming(false); - - emit zoomingEnded(); -} - -void Renderer2D::beginNavigation() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsNavigating(true); - - emit navigationStarted(); -} - -void Renderer2D::endNavigation() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsNavigating(false); - - emit navigationEnded(); } } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 841dbc405..de1999869 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -5,6 +5,7 @@ #pragma once #include "Renderer.h" +#include "Navigator2D.h" namespace mv { @@ -13,7 +14,7 @@ namespace mv * Renderer 2D class * * Supports two-dimensional rendering: - * - Organizes panning and zooming + * - Orchestrates panning and zooming using Navigator2D * - Sets up the matrix transformations * - Renders 2D data * @@ -29,154 +30,42 @@ Q_OBJECT /** * Construct a new two-dimensional renderer * - * @param sourceWidget If set, use this widget to do panning and zooming * @param parent Pointer to the parent object */ - explicit Renderer2D(QWidget* sourceWidget = nullptr, QObject* parent = nullptr); + explicit Renderer2D(QObject* parent = nullptr); - /** - * Watch \p watched for events - * - * @param watched Watched object - * @param event Event - * @return True if the event was handled, false otherwise - */ - bool eventFilter(QObject* watched, QEvent* event) override; - -public: // Navigation - - /** - * Zoom by \p factor around \p center - * - * @param at Point to zoom around - * @param factor Zoom factor - */ - void zoomAround(const QPointF& center, float factor); - - /** - * Zoom to \p zoomRectangle - * - * @param zoomRectangle Zoom to this rectangle - */ - void zoomToRectangle(const QRectF& zoomRectangle); - - /** - * Pan by \p to - * - * @param to Pan by this amount - */ - void panBy(const QPointF& to); - - /** Zoom to extents of the data bounds (with a margin around it) */ - void resetView(); - - /** - * Get whether the renderer is panning - * - * @return Boolean determining whether the renderer is panning - */ - bool isPanning() const; - - /** - * Get whether the renderer is zooming - * - * @return Boolean determining whether the renderer is zooming - */ - bool isZooming() const; - - /** - * Get whether the renderer is navigating - * - * @return Boolean determining whether the renderer is navigating - */ - bool isNavigating() const; - -protected: // Navigation - - /** - * Set whether the renderer is panning to \p isPanning - * - * @param isPanning Boolean determining whether the renderer is panning - */ - void setIsPanning(bool isPanning); + /** + * Resize the renderer to \p renderSize + * @param renderSize New size of the renderer + */ + void resize(QSize renderSize) override; /** - * Set whether the renderer is zooming to \p isZooming - * - * @param isZooming Boolean determining whether the renderer is zooming + * Get the render size + * @return Render size */ - void setIsZooming(bool isZooming); + QSize getRenderSize() const override; /** - * Set whether the renderer is navigating to \p isNavigating + * Get the 2D navigator * - * @param isNavigating Boolean determining whether the renderer is navigating - */ - void setIsNavigating(bool isNavigating); - - - /** Panning has begun */ - void beginPanning(); - - /** Panning has ended */ - void endPanning(); - - /** Zooming has begun */ - void beginZooming(); - - /** Zooming has ended */ - void endZooming(); - - /** Navigation has begun */ - void beginNavigation(); - - /** Navigation has ended */ - void endNavigation(); - -signals: - - /** Signals that panning has started */ - void panningStarted(); - - /** Signals that panning has ended */ - void panningEnded(); - - /** - * Signals that is panning changed to \p isPanning - * @param isPanning - */ - void isPanningChanged(bool isPanning); - - /** Signals that zooming has started */ - void zoomingStarted(); - - /** Signals that zooming has ended */ - void zoomingEnded(); - - /** - * Signals that is zooming changed to \p isZooming - * @param isZooming + * @return Reference to the 2D navigator */ - void isZoomingChanged(bool isZooming); + Navigator2D& getNavigator(); - /** Signals that navigation has started */ - void navigationStarted(); +protected: - /** Signals that navigation has ended */ - void navigationEnded(); + /** Begin rendering */ + void beginRender() override; - /** - * Signals that is navigating changed to \p isNavigating - * @param isNavigating - */ - void isNavigatingChanged(bool isNavigating); + /** End rendering */ + void endRender() override; private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + + friend class Navigator2D; }; } From 1768a0e3ce606a0a79e4738492e52e228e93883c Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 13:21:03 +0100 Subject: [PATCH 05/89] Work on two-dimensional navigator --- ManiVault/src/renderers/Navigator2D.cpp | 36 ++++++++++++------------- ManiVault/src/renderers/Navigator2D.h | 10 +++---- ManiVault/src/renderers/Renderer2D.cpp | 3 ++- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 8e5739374..d31e50d0f 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -14,8 +14,9 @@ namespace mv { -Navigator2D::Navigator2D(QObject* parent) : +Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : QObject(parent), + _renderer(renderer), _initialized(false), _isNavigating(false), _isPanning(false), @@ -23,13 +24,12 @@ Navigator2D::Navigator2D(QObject* parent) : { } -void Navigator2D::initialize(QWidget* sourceWidget, Renderer2D* renderer) +void Navigator2D::initialize(QWidget* sourceWidget) { - Q_ASSERT(sourceWidget && renderer); + Q_ASSERT(sourceWidget); - if (sourceWidget && renderer) { + if (sourceWidget) { _sourceWidget = sourceWidget; - _renderer = renderer; _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); @@ -102,9 +102,9 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) 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; + const auto& previousMousePosition = _mousePositions[_mousePositions.size() - 2]; + const auto& currentMousePosition = _mousePositions[_mousePositions.size() - 1]; + const auto panVector = currentMousePosition - previousMousePosition; panBy(panVector); } @@ -120,7 +120,7 @@ void Navigator2D::zoomAround(const QPointF& center, float factor) if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << center << factor; #endif @@ -138,7 +138,7 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << zoomRectangle; #endif @@ -163,7 +163,7 @@ void Navigator2D::panBy(const QPointF& to) if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << to; #endif @@ -205,7 +205,7 @@ void Navigator2D::resetView() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif } @@ -269,7 +269,7 @@ void Navigator2D::beginPanning() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -283,7 +283,7 @@ void Navigator2D::endPanning() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -297,7 +297,7 @@ void Navigator2D::beginZooming() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -311,7 +311,7 @@ void Navigator2D::endZooming() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -325,7 +325,7 @@ void Navigator2D::beginNavigation() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -339,7 +339,7 @@ void Navigator2D::endNavigation() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e23cbd38d..0033be899 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -28,17 +28,17 @@ Q_OBJECT /** * Construct a new two-dimensional navigator * + * @param renderer Reference to parent renderer * @param parent Pointer to the parent object */ - explicit Navigator2D(QObject* parent = nullptr); + explicit Navigator2D(Renderer2D& renderer, QObject* parent = nullptr); /** - * Initializes the two-dimensional navigator with a \p widget and a \p renderer + * Initializes the two-dimensional navigator with a \p sourceWidget * * @param sourceWidget Pointer to the renderer widget - * @param renderer Pointer to the renderer */ - void initialize(QWidget* sourceWidget, Renderer2D* renderer); + void initialize(QWidget* sourceWidget); /** * Watch \p watched for events @@ -179,7 +179,7 @@ Q_OBJECT private: QPointer _sourceWidget; /** Source widget for panning and zooming */ - QPointer _renderer; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ bool _initialized; /** Initialized flag */ QVector _mousePositions; /** Recorded mouse positions */ bool _isNavigating; /** Navigating flag */ diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index a7dd4ebb1..537319ca4 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -14,7 +14,8 @@ namespace mv { Renderer2D::Renderer2D(QObject* parent) : - Renderer(parent) + Renderer(parent), + _navigator(*this) { } From e0dbbd51fe4bc87b66aa7332c722612b2a9153aa Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 21 Mar 2025 09:38:16 +0100 Subject: [PATCH 06/89] Work on navigator --- ManiVault/res/shaders/PointPlot.vert | 2 +- ManiVault/src/graphics/Shader.cpp | 4 + ManiVault/src/graphics/Shader.h | 3 + ManiVault/src/renderers/Navigator2D.cpp | 64 ++++++++--- ManiVault/src/renderers/Navigator2D.h | 31 +++--- ManiVault/src/renderers/PointRenderer.cpp | 11 +- ManiVault/src/renderers/Renderer2D.cpp | 125 +++++++++++++++++++++- ManiVault/src/renderers/Renderer2D.h | 79 +++++++++++++- 8 files changed, 282 insertions(+), 37 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index bc000a036..613951f56 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -132,7 +132,7 @@ void main() vPosOrig = position; // Transform position to clip space - vec2 pos = (orthoM * vec3(position, 1)).xy; + vec2 pos = (orthoM * vec4(position, 0, 1)).xy; // Resize point quad according to properties vec2 scaledVertex = vertex * pointSize * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); diff --git a/ManiVault/src/graphics/Shader.cpp b/ManiVault/src/graphics/Shader.cpp index a6b718a7d..e32ed36b6 100644 --- a/ManiVault/src/graphics/Shader.cpp +++ b/ManiVault/src/graphics/Shader.cpp @@ -154,6 +154,10 @@ void ShaderProgram::uniformMatrix3f(const char* name, Matrix3f& m) { glUniformMatrix3fv(location(name), 1, false, m.toArray()); } +void ShaderProgram::uniformMatrix3f(const char* name, float* data) { + glUniformMatrix3fv(location(name), 1, false, data); +} + //void Shader::uniformMatrix4f(const char* name, Matrix4f& m) { // glUniformMatrix4fv(location(name), 1, false, m.toArray()); //} diff --git a/ManiVault/src/graphics/Shader.h b/ManiVault/src/graphics/Shader.h index 2ee0c49ce..0cf9e07d7 100644 --- a/ManiVault/src/graphics/Shader.h +++ b/ManiVault/src/graphics/Shader.h @@ -36,6 +36,9 @@ class CORE_EXPORT ShaderProgram : protected QOpenGLFunctions_3_3_Core void uniform3fv(const char* name, int count, Vector3f* v); void uniform4f(const char* name, float v0, float v1, float v2, float v3); void uniformMatrix3f(const char* name, Matrix3f& m); + + void uniformMatrix3f(const char* name, float* data); + //void uniformMatrix4f(const char* name, Matrix4f& m); void uniformMatrix4f(const char* name, const float* const m); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index d31e50d0f..a5cb95e60 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -20,7 +20,8 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _initialized(false), _isNavigating(false), _isPanning(false), - _isZooming(false) + _isZooming(false), + _zoomFactor(1.0f) { } @@ -66,18 +67,22 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (isNavigating()) { if (event->type() == QEvent::Wheel) { - if (auto* wheelEvent = dynamic_cast(event)) - zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); + if (auto* wheelEvent = dynamic_cast(event)) { + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + } } if (event->type() == QEvent::MouseButtonPress) { - if (const auto* mouseEvent = dynamic_cast(event)) - { + if (const auto* mouseEvent = dynamic_cast(event)) { if (mouseEvent->button() == Qt::MiddleButton) resetView(); - if (mouseEvent->buttons() == Qt::LeftButton) - { + if (mouseEvent->buttons() == Qt::LeftButton) { _sourceWidget->setCursor(Qt::ClosedHandCursor); _mousePositions << mouseEvent->pos(); @@ -96,12 +101,10 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) } if (event->type() == QEvent::MouseMove) { - if (const auto* mouseEvent = dynamic_cast(event)) - { + if (const auto* mouseEvent = dynamic_cast(event)) { _mousePositions << mouseEvent->pos(); - if (mouseEvent->buttons() == Qt::LeftButton && _mousePositions.size() >= 2) - { + 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; @@ -115,7 +118,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) return QObject::eventFilter(watched, event); } -void Navigator2D::zoomAround(const QPointF& center, float factor) +void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) return; @@ -126,7 +129,22 @@ void Navigator2D::zoomAround(const QPointF& center, float factor) beginZooming(); { + _zoomFactor *= factor; + + computeZoomRectangle(); + + /* + const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), center).toPointF(); + const auto v1 = _renderer.getZoomRectangle().topLeft() - p1; + const auto v2 = v1 / factor; + auto zoomRectangle = _renderer.getZoomRectangle(); + + zoomRectangle.setTopLeft(p1 + v2); + zoomRectangle.setSize(zoomRectangle.size() / factor); + + _renderer.setZoomRectangle(zoomRectangle); + */ } endZooming(); @@ -158,18 +176,29 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) //update(); } -void Navigator2D::panBy(const QPointF& to) +void Navigator2D::panBy(const QPointF& delta) { if (!_initialized) return; #ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << to; + qDebug() << __FUNCTION__ << delta; #endif beginPanning(); { + const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), QPoint()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), delta.toPoint()).toPointF(); + + //auto zoomRectangle = _renderer.getZoomRectangle(); + //zoomRectangle.setTopLeft(zoomRectangle.topLeft() + (p2 - p1)); + + //_renderer.setZoomRectangle(zoomRectangle); + + _panCoordinates -= QVector2D(p2 - p1) / _zoomFactor; + + computeZoomRectangle(); } endPanning(); @@ -348,4 +377,11 @@ void Navigator2D::endNavigation() emit navigationEnded(); } +void Navigator2D::computeZoomRectangle() const +{ + QRectF zoomRectangle(_panCoordinates.toPointF(), _zoomFactor * _renderer.getRenderSize()); + + _renderer.setZoomRectangle(zoomRectangle); +} + } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 0033be899..a4331a84d 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -5,6 +5,7 @@ #pragma once #include +#include namespace mv { @@ -35,7 +36,6 @@ Q_OBJECT /** * Initializes the two-dimensional navigator with a \p sourceWidget - * * @param sourceWidget Pointer to the renderer widget */ void initialize(QWidget* sourceWidget); @@ -53,46 +53,40 @@ Q_OBJECT /** * Zoom by \p factor around \p center - * * @param center Point to zoom around * @param factor Zoom factor */ - void zoomAround(const QPointF& center, float factor); + void zoomAround(const QPoint& center, float factor); /** * Zoom to \p zoomRectangle - * * @param zoomRectangle Zoom to this rectangle */ void zoomToRectangle(const QRectF& zoomRectangle); /** - * Pan by \p to - * - * @param to Pan by this amount + * Pan by \p delta + * @param delta Pan by this amount */ - void panBy(const QPointF& to); + void panBy(const QPointF& delta); /** Zoom to extents of the data bounds (with a margin around it) */ void resetView(); /** * Get whether the renderer is panning - * * @return Boolean determining whether the renderer is panning */ bool isPanning() const; /** * Get whether the renderer is zooming - * * @return Boolean determining whether the renderer is zooming */ bool isZooming() const; /** * Get whether the renderer is navigating - * * @return Boolean determining whether the renderer is navigating */ bool isNavigating() const; @@ -101,26 +95,22 @@ Q_OBJECT /** * Set whether the renderer is panning to \p isPanning - * * @param isPanning Boolean determining whether the renderer is panning */ void setIsPanning(bool isPanning); /** * Set whether the renderer is zooming to \p isZooming - * * @param isZooming Boolean determining whether the renderer is zooming */ void setIsZooming(bool isZooming); /** * Set whether the renderer is navigating to \p isNavigating - * * @param isNavigating Boolean determining whether the renderer is navigating */ void setIsNavigating(bool isNavigating); - /** Panning has begun */ void beginPanning(); @@ -139,6 +129,11 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); +private: + + /** Compute the zoom rectangle in world space */ + void computeZoomRectangle() const; + signals: /** Signals that panning has started */ @@ -161,7 +156,7 @@ Q_OBJECT /** * Signals that is zooming changed to \p isZooming - * @param isZooming + * @param isZooming Boolean determining whether the renderer is zooming */ void isZoomingChanged(bool isZooming); @@ -173,7 +168,7 @@ Q_OBJECT /** * Signals that is navigating changed to \p isNavigating - * @param isNavigating + * @param isNavigating Boolean determining whether the renderer is navigating */ void isNavigatingChanged(bool isNavigating); @@ -185,6 +180,8 @@ Q_OBJECT bool _isNavigating; /** Navigating flag */ bool _isPanning; /** Panning flag */ bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QVector2D _panCoordinates; /** Pan coordinates in world space */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 2381abbcf..4faa1b9d8 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -484,8 +484,17 @@ namespace mv { beginRender(); { + qDebug() << getZoomRectangle(); + + const float aspectRatio = getRenderSize().width() / static_cast(getRenderSize().height()); + + QMatrix4x4 projection; + + + projection.ortho(aspectRatio * getZoomRectangle().left(), aspectRatio * getZoomRectangle().right(), getZoomRectangle().bottom(), getZoomRectangle().top(), -1.f, 1.f); + // World to clip transformation - _orthoM = createProjectionMatrix(_boundsView); + _orthoM = createProjectionMatrix(Bounds(getZoomRectangle().left(), getZoomRectangle().right(), getZoomRectangle().top(), getZoomRectangle().bottom())); _shader.bind(); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 537319ca4..60f22d9c0 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -15,7 +15,8 @@ namespace mv Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), - _navigator(*this) + _navigator(*this), + _zoomRectangle(0, 0, 200, 100) { } @@ -42,7 +43,7 @@ void Renderer2D::beginRender() const auto size = _renderSize.width() < _renderSize.height() ? _renderSize.width() : _renderSize.height(); - glViewport(_renderSize.width() / 2 - size / 2, _renderSize.height() / 2 - size / 2, size, size); + glViewport(0, 0, _renderSize.width(), _renderSize.height()); } void Renderer2D::endRender() @@ -52,4 +53,124 @@ void Renderer2D::endRender() #endif } +QRectF Renderer2D::getZoomRectangle() const +{ + return _zoomRectangle; +} + +void Renderer2D::setZoomRectangle(const QRectF& zoomRectangle) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << zoomRectangle; +#endif + + if (zoomRectangle == _zoomRectangle) + return; + + const auto previousZoomRectangle = _zoomRectangle; + + _zoomRectangle = zoomRectangle; + + emit zoomRectangleChanged(previousZoomRectangle, _zoomRectangle); +} + +QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const +{ + return QVector3D(screenPoint.x(), getRenderSize().height() - screenPoint.y(), 0).unproject(modelViewMatrix, getProjectionMatrix(), QRect(0, 0, getRenderSize().width(), getRenderSize().height())); +} + +QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const +{ + const auto clipSpacePos = getProjectionMatrix() * (getViewMatrix() * QVector4D(position, 1.0)); + return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); +} + +QPoint Renderer2D::getWorldPositionToScreenPoint(const QVector3D& position) const +{ + const auto normalizedScreenPoint = QVector2D(1.0f, -1.0f) * getWorldPositionToNormalizedScreenPoint(position); + const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + + return (viewSize * ((QVector2D(1.0f, 1.0f) + normalizedScreenPoint) / 2.0f)).toPoint(); +} + +QVector2D Renderer2D::getScreenPointToNormalizedScreenPoint(const QVector2D& screenPoint) const +{ + const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + return QVector2D(-1.f, -1.f) + 2.f * (QVector2D(screenPoint.x(), getRenderSize().height() - screenPoint.y()) / viewSize); +} + +QMatrix4x4 Renderer2D::getScreenToNormalizedScreenMatrix() const +{ + QMatrix4x4 translate, scale; + + translate.translate(-1.0f, -1.0f, 0.0f); + scale.scale(2.0f / static_cast(getRenderSize().width()), 2.0f / static_cast(getRenderSize().height()), 1.0f); + + return translate * scale; +} + +QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const +{ + QMatrix4x4 translate, scale; + + const auto size = QSizeF(getRenderSize()); + const auto halfSize = 0.5f * size; + + scale.scale(halfSize.width(), halfSize.height(), 1.0f); + translate.translate(size.width(), 1, 0.0f); + + return translate * scale; +} + +QMatrix4x4 Renderer2D::getViewMatrix() const +{ + QMatrix4x4 lookAt, scale; + + // Construct look-at parameters + const auto eye = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 1); + const auto center = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 0); + const auto up = QVector3D(0, 1, 0); + + // Create look-at transformation matrix + lookAt.lookAt(eye, center, up); + + const auto viewerSize = getRenderSize(); + const auto factorX = static_cast(viewerSize.width()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.width()) : 1.0f); + const auto factorY = static_cast(viewerSize.height()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.height()) : 1.0f); + const auto scaleFactor = factorX < factorY ? factorX : factorY; + + const auto d = 1.0f - (2 * _zoomMargin) / std::max(viewerSize.width(), viewerSize.height()); + + // Create scale matrix + scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); + + // Return composite matrix of scale and look-at transformation matrix + return scale * lookAt; +} + +QMatrix4x4 Renderer2D::getProjectionMatrix() const +{ + // Compute half of the widget size + const auto halfSize = getRenderSize() / 2; + + QMatrix4x4 matrix; + + // Create an orthogonal transformation matrix + matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); + + return matrix; +} + +QRect Renderer2D::getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const +{ + // Compute screen bounding rectangle extremes + const auto topLeftScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.bottomLeft())); + const auto bottomRightScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.topRight())); + + return { + topLeftScreen, + bottomRightScreen + }; +} + } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index de1999869..32ccb415b 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -7,6 +7,8 @@ #include "Renderer.h" #include "Navigator2D.h" +#include + namespace mv { @@ -36,6 +38,7 @@ Q_OBJECT /** * Resize the renderer to \p renderSize + * * @param renderSize New size of the renderer */ void resize(QSize renderSize) override; @@ -53,6 +56,55 @@ Q_OBJECT */ Navigator2D& getNavigator(); +public: // Coordinate conversions + + /** + * Convert \p screenPoint to point in world coordinates using \p modelViewMatrix + * @param modelViewMatrix Model-view matrix + * @param screenPoint Point in screen coordinates [0..width, 0..height] + * @return Position in world coordinates + */ + QVector3D getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const; + + /** + * Convert \p position in world coordinates to point in normalized screen coordinates + * @param position Position in world coordinates + * @return Point in normalized screen coordinates [-1..1, -1..1] + */ + QVector2D getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const; + + /** + * Convert \p position in world coordinates to point in screen coordinates + * @param position Position in world coordinates + * @return Point in screen coordinates [0..width, 0..height] + */ + QPoint getWorldPositionToScreenPoint(const QVector3D& position) const; + + /** + * Convert \p screenPoint to point in normalized screen coordinates + * @param screenPoint Point in screen coordinates [0..width, 0..height] + * @return Point in normalized screen coordinates [-1..1, -1..1] + */ + QVector2D getScreenPointToNormalizedScreenPoint(const QVector2D& screenPoint) const; + + /** Returns the matrix that converts screen coordinates [0..width, 0..height] to normalized screen coordinates [-1..1, -1..1] */ + QMatrix4x4 getScreenToNormalizedScreenMatrix() const; + + /** Returns the matrix that converts normalized screen coordinates [-1..1, -1..1] to screen coordinates [0..width, 0..height] */ + QMatrix4x4 getNormalizedScreenToScreenMatrix() const; + + /** Returns the view matrix */ + QMatrix4x4 getViewMatrix() const; + + /** Returns the projection matrix */ + QMatrix4x4 getProjectionMatrix() const; + + /** + * Get screen bounding rectangle from world bounding rectangle + * @param worldBoundingRectangle World bounding rectangle + */ + QRect getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const; + protected: /** Begin rendering */ @@ -61,9 +113,32 @@ Q_OBJECT /** End rendering */ void endRender() override; + /** + * Get the zoom rectangle + * @return Zoom rectangle + */ + QRectF getZoomRectangle() const; + + /** + * Set the zoom rectangle to \p zoomRectangle + * @param zoomRectangle Zoom rectangle + */ + void setZoomRectangle(const QRectF& zoomRectangle); + +signals: + + /** + * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle + * @param previousZoomRectangle Previous zoom rectangle + * @param currentZoomRectangle Current zoom rectangle + */ + void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); + private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + float _zoomMargin = 100.f; /** Margin for zooming */ + QRectF _zoomRectangle; /** Zoom rectangle */ friend class Navigator2D; }; From 34765e662fff05de625c9c40763a2fcbfced1435 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 21 Mar 2025 17:51:34 +0100 Subject: [PATCH 07/89] Zooming around and panning is working properly --- ManiVault/res/shaders/PointPlot.frag | 3 + ManiVault/res/shaders/PointPlot.vert | 8 +- ManiVault/src/graphics/Shader.cpp | 6 +- ManiVault/src/graphics/Shader.h | 2 + ManiVault/src/renderers/Navigator2D.cpp | 116 +++++++++------------- ManiVault/src/renderers/Navigator2D.h | 40 +++++--- ManiVault/src/renderers/PointRenderer.cpp | 82 ++------------- ManiVault/src/renderers/PointRenderer.h | 31 +----- ManiVault/src/renderers/Renderer2D.cpp | 46 ++++----- ManiVault/src/renderers/Renderer2D.h | 20 +++- 10 files changed, 133 insertions(+), 221 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.frag b/ManiVault/res/shaders/PointPlot.frag index 7be52f9ce..e6263aa35 100644 --- a/ManiVault/res/shaders/PointPlot.frag +++ b/ManiVault/res/shaders/PointPlot.frag @@ -59,6 +59,9 @@ float normalize(float minPixelValue, float maxPixelValue, float pixelValue) void main() { + fragColor = vec4(1, 0, 0, 1); + return; + bool isSelectionHighlighted = vHighlight == 1; bool isFocusHighlighted = vFocusHighlight == 1; bool isHighlighted = isSelectionHighlighted || isFocusHighlighted; diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 613951f56..3561a62f4 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -16,7 +16,7 @@ uniform float pointSize; /** Point size */ uniform float pointSizeScale; /** Scale factor in absolute point size mode */ uniform int scalarEffect; uniform float pointOpacity; /** Point opacity */ -uniform mat3 orthoM; /** Projection matrix from bounds space to clip space */ +uniform mat4 mvp; /** Projection matrix from bounds space to clip space */ uniform bool hasHighlights; /** Whether a highlight buffer is used */ uniform bool hasFocusHighlights; /** Whether a focus highlight buffer is used */ uniform bool hasScalars; /** Whether a scalar buffer is used */ @@ -103,8 +103,6 @@ float floatConstruct( uint m ) { return f - 1.0; // Range [0:1] } - - // Pseudo-random value in half-open range [0:1]. float random( float x ) { return floatConstruct(hash(floatBitsToUint(x))); } float random( vec2 v ) { return floatConstruct(hash(floatBitsToUint(v))); } @@ -129,10 +127,8 @@ void main() if (hasOpacities) vOpacity = opacity; - vPosOrig = position; - // Transform position to clip space - vec2 pos = (orthoM * vec4(position, 0, 1)).xy; + vec2 pos = (mvp * vec4(position, 0, 1)).xy; // Resize point quad according to properties vec2 scaledVertex = vertex * pointSize * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); diff --git a/ManiVault/src/graphics/Shader.cpp b/ManiVault/src/graphics/Shader.cpp index e32ed36b6..0a1994dec 100644 --- a/ManiVault/src/graphics/Shader.cpp +++ b/ManiVault/src/graphics/Shader.cpp @@ -158,9 +158,9 @@ void ShaderProgram::uniformMatrix3f(const char* name, float* data) { glUniformMatrix3fv(location(name), 1, false, data); } -//void Shader::uniformMatrix4f(const char* name, Matrix4f& m) { -// glUniformMatrix4fv(location(name), 1, false, m.toArray()); -//} +void ShaderProgram::uniformMatrix4f(const char* name, float* data) { + glUniformMatrix4fv(location(name), 1, false, data); +} void ShaderProgram::uniformMatrix4f(const char* name, const float* const m) { diff --git a/ManiVault/src/graphics/Shader.h b/ManiVault/src/graphics/Shader.h index 0cf9e07d7..b8fb2648f 100644 --- a/ManiVault/src/graphics/Shader.h +++ b/ManiVault/src/graphics/Shader.h @@ -39,6 +39,8 @@ class CORE_EXPORT ShaderProgram : protected QOpenGLFunctions_3_3_Core void uniformMatrix3f(const char* name, float* data); + void uniformMatrix4f(const char* name, float* data); + //void uniformMatrix4f(const char* name, Matrix4f& m); void uniformMatrix4f(const char* name, const float* const m); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index a5cb95e60..88aa60cf2 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -21,7 +21,9 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isNavigating(false), _isPanning(false), _isZooming(false), - _zoomFactor(1.0f) + _zoomFactor(1.0f), + _zoomRectangleSize(1000, 1000), + _zoomRectangleMargin(0.f) { } @@ -109,7 +111,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) const auto& currentMousePosition = _mousePositions[_mousePositions.size() - 1]; const auto panVector = currentMousePosition - previousMousePosition; - panBy(panVector); + panBy(-panVector); } } } @@ -118,6 +120,40 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) return QObject::eventFilter(watched, event); } +QMatrix4x4 Navigator2D::getViewMatrix() const +{ + QMatrix4x4 lookAt, scale; + + // Construct look-at parameters + const auto eye = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 1); + const auto center = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 0); + const auto up = QVector3D(0, 1, 0); + + // Create look-at transformation matrix + lookAt.lookAt(eye, center, up); + + const auto viewerSize = _renderer.getRenderSize(); + const auto factorX = static_cast(viewerSize.width()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().width()) : 1.0f); + const auto factorY = static_cast(viewerSize.height()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().height()) : 1.0f); + const auto scaleFactor = factorX < factorY ? factorX : factorY; + + const auto d = 1.0f - (2 * _zoomRectangleMargin) / std::max(viewerSize.width(), viewerSize.height()); + + // Create scale matrix + scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); + + // Return composite matrix of scale and look-at transformation matrix + return scale * lookAt; +} + +QRectF Navigator2D::getZoomRectangle() const +{ + return { + _zoomRectangleTopLeft, + _zoomRectangleSize + }; +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) @@ -129,26 +165,16 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) beginZooming(); { - _zoomFactor *= factor; - - computeZoomRectangle(); - - /* - const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), center).toPointF(); - const auto v1 = _renderer.getZoomRectangle().topLeft() - p1; + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); + const auto v1 = getZoomRectangle().topLeft() - p1; const auto v2 = v1 / factor; - auto zoomRectangle = _renderer.getZoomRectangle(); + _zoomRectangleTopLeft = p1 + v2; + _zoomRectangleSize = getZoomRectangle().size() / factor; - zoomRectangle.setTopLeft(p1 + v2); - zoomRectangle.setSize(zoomRectangle.size() / factor); - - _renderer.setZoomRectangle(zoomRectangle); - */ + _renderer.setZoomRectangle(getZoomRectangle()); } endZooming(); - - // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); } void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) @@ -165,15 +191,6 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) } endZooming(); - - //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 Navigator2D::panBy(const QPointF& delta) @@ -187,46 +204,14 @@ void Navigator2D::panBy(const QPointF& delta) beginPanning(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), QPoint()).toPointF(); - const auto p2 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), delta.toPoint()).toPointF(); - - //auto zoomRectangle = _renderer.getZoomRectangle(); - - //zoomRectangle.setTopLeft(zoomRectangle.topLeft() + (p2 - p1)); - - //_renderer.setZoomRectangle(zoomRectangle); + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _panCoordinates -= QVector2D(p2 - p1) / _zoomFactor; + _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); - computeZoomRectangle(); + _renderer.setZoomRectangle(getZoomRectangle()); } endPanning(); - - //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(); } void Navigator2D::resetView() @@ -377,11 +362,4 @@ void Navigator2D::endNavigation() emit navigationEnded(); } -void Navigator2D::computeZoomRectangle() const -{ - QRectF zoomRectangle(_panCoordinates.toPointF(), _zoomFactor * _renderer.getRenderSize()); - - _renderer.setZoomRectangle(zoomRectangle); -} - } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index a4331a84d..d5f1c9918 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -6,6 +6,7 @@ #include #include +#include namespace mv { @@ -49,6 +50,18 @@ Q_OBJECT */ bool eventFilter(QObject* watched, QEvent* event) override; + /** + * Get the view matrix + * @return View matrix + */ + QMatrix4x4 getViewMatrix() const; + + /** + * Get the zoom rectangle + * @return Zoom rectangle + */ + QRectF getZoomRectangle() const; + public: // Navigation /** @@ -129,11 +142,6 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); -private: - - /** Compute the zoom rectangle in world space */ - void computeZoomRectangle() const; - signals: /** Signals that panning has started */ @@ -173,15 +181,19 @@ Q_OBJECT void isNavigatingChanged(bool isNavigating); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QVector2D _panCoordinates; /** Pan coordinates in world space */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + +private: + QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ + QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 4faa1b9d8..a7af9b170 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -10,24 +10,6 @@ namespace mv { namespace gui { - namespace - { - /** - * Builds an orthographic projection matrix that transforms the given bounds - * to the range [-1, 1] in both directions. - */ - Matrix3f createProjectionMatrix(const Bounds& bounds) - { - Matrix3f m; - m.setIdentity(); - m[0] = 2 / bounds.getWidth(); - m[4] = 2 / bounds.getHeight(); - m[6] = -((bounds.getRight() + bounds.getLeft()) / bounds.getWidth()); - m[7] = -((bounds.getTop() + bounds.getBottom()) / bounds.getHeight()); - return m; - } - } - void PointArrayObject::init() { initializeOpenGLFunctions(); @@ -328,42 +310,6 @@ namespace mv _colormap.loadFromImage(image); } - Bounds PointRenderer::getBounds() const - { - return getViewBounds(); - } - - Bounds PointRenderer::getViewBounds() const - { - return _boundsView; - } - - Bounds PointRenderer::getDataBounds() const - { - return _boundsData; - } - - void PointRenderer::setBounds(const Bounds& bounds) - { - setViewBounds(bounds); - setDataBounds(bounds); - } - - void PointRenderer::setViewBounds(const Bounds& boundsView) - { - _boundsView = boundsView; - } - - void PointRenderer::setDataBounds(const Bounds& boundsData) - { - _boundsData = boundsData; - } - - Matrix3f PointRenderer::getProjectionMatrix() const - { - return _orthoM; - } - const PointArrayObject& PointRenderer::getGpuPoints() const { return _gpuPoints; @@ -484,41 +430,29 @@ namespace mv { beginRender(); { - qDebug() << getZoomRectangle(); - - const float aspectRatio = getRenderSize().width() / static_cast(getRenderSize().height()); - - QMatrix4x4 projection; - + _shader.bind(); - projection.ortho(aspectRatio * getZoomRectangle().left(), aspectRatio * getZoomRectangle().right(), getZoomRectangle().bottom(), getZoomRectangle().top(), -1.f, 1.f); + QMatrix4x4 modelMatrix; - // World to clip transformation - _orthoM = createProjectionMatrix(Bounds(getZoomRectangle().left(), getZoomRectangle().right(), getZoomRectangle().top(), getZoomRectangle().bottom())); - - _shader.bind(); + modelMatrix.setToIdentity(); + const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; - const auto size = std::max(getRenderSize().width(), getRenderSize().height()); + const auto size = static_cast(std::max(getRenderSize().width(), getRenderSize().height())); _shader.uniform1f("pointSize", _pointSettings._pointSize); - _shader.uniform1f("pointSizeScale", absoluteRendering ? (1.0 / size) : 1.0f / size); - - _shader.uniformMatrix3f("orthoM", _orthoM); + _shader.uniform1f("pointSizeScale", absoluteRendering ? 1.0f / size : 1.0f / size); + _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); - - _shader.uniform4f("dataBounds", _boundsData.getLeft(), _boundsData.getRight(), _boundsData.getBottom(), _boundsData.getTop()); - + _shader.uniform4f("dataBounds", getDataRectangle().left(), getDataRectangle().right(), getDataRectangle().bottom(), getDataRectangle().top()); _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); _shader.uniform1i("selectionOutlineOverrideColor", _selectionOutlineOverrideColor); _shader.uniform1f("selectionOutlineOpacity", _selectionOutlineOpacity); _shader.uniform1i("selectionHaloEnabled", _selectionHaloEnabled); - _shader.uniform1i("randomizedDepthEnabled", _randomizedDepthEnabled); - _shader.uniform1i("hasHighlights", _gpuPoints.hasHighlights()); _shader.uniform1i("hasFocusHighlights", _gpuPoints.hasFocusHighlights()); _shader.uniform1i("hasScalars", _gpuPoints.hasColorScalars()); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 20f42507c..306f7f7d2 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -78,6 +78,9 @@ namespace mv } void draw(); + + void updateQuadVertices(const QSizeF& quadSize); + void destroy(); private: @@ -113,6 +116,8 @@ namespace mv bool _dirtySizeScalars = false; bool _dirtyOpacityScalars = false; bool _dirtyColors = false; + + BufferObject _quadBufferObject; }; struct CORE_EXPORT PointSettings @@ -145,26 +150,6 @@ namespace mv void setColormap(const QImage& image); - // Returns getViewBounds() - Bounds getBounds() const; - - // Retuns _boundsView - Bounds getViewBounds() const; - - // Returns _boundsData - Bounds getDataBounds() const; - - // Calls both setViewBounds() and setDataBounds() - void setBounds(const Bounds& bounds); - - // sets _boundsView, used for computing the projection matrix _orthoM - void setViewBounds(const Bounds& boundsView); - - // sets _boundsData, used for scaling the 2d _colormap - void setDataBounds(const Bounds& boundsData); - - Matrix3f getProjectionMatrix() const; - const PointArrayObject& getGpuPoints() const; std::int32_t getNumSelectedPoints() const; @@ -221,14 +206,8 @@ namespace mv /* Rendering variables */ ShaderProgram _shader; - PointArrayObject _gpuPoints; Texture2D _colormap; /** 2D colormap, sets point color based on point position */ - - Matrix3f _orthoM = {}; /** Projection matrix from bounds space to clip space */ - Bounds _boundsView = Bounds(-1, 1, -1, 1); /** Used for computing the projection matrix _orthoM */ - Bounds _boundsData = Bounds(-1, 1, -1, 1); /** Used for scaling the 2d _colormap */ - std::int32_t _numSelectedPoints = 0; /** Number of selected (highlighted points) */ std::int32_t _numberOfFocusHighlights = 0; /** Number of focus highlights */ }; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 60f22d9c0..3397e7f39 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -16,7 +16,7 @@ namespace mv Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), _navigator(*this), - _zoomRectangle(0, 0, 200, 100) + _zoomRectangle(0, 0, 2000, 1000) { } @@ -41,8 +41,6 @@ void Renderer2D::beginRender() qDebug() << __FUNCTION__; #endif - const auto size = _renderSize.width() < _renderSize.height() ? _renderSize.width() : _renderSize.height(); - glViewport(0, 0, _renderSize.width(), _renderSize.height()); } @@ -74,6 +72,19 @@ void Renderer2D::setZoomRectangle(const QRectF& zoomRectangle) emit zoomRectangleChanged(previousZoomRectangle, _zoomRectangle); } +QRectF Renderer2D::getDataRectangle() const +{ + return _dataRectangle; +} + +void Renderer2D::setDataRectangle(const QRectF& dataRectangle) +{ + if (dataRectangle == _dataRectangle) + return; + + _dataRectangle = dataRectangle; +} + QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const { return QVector3D(screenPoint.x(), getRenderSize().height() - screenPoint.y(), 0).unproject(modelViewMatrix, getProjectionMatrix(), QRect(0, 0, getRenderSize().width(), getRenderSize().height())); @@ -81,7 +92,7 @@ QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewM QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const { - const auto clipSpacePos = getProjectionMatrix() * (getViewMatrix() * QVector4D(position, 1.0)); + const auto clipSpacePos = getProjectionMatrix() * (_navigator.getViewMatrix() * QVector4D(position, 1.0)); return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); } @@ -122,30 +133,13 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const return translate * scale; } -QMatrix4x4 Renderer2D::getViewMatrix() const +float Renderer2D::getZoomPercentage() const { - QMatrix4x4 lookAt, scale; - - // Construct look-at parameters - const auto eye = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 1); - const auto center = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 0); - const auto up = QVector3D(0, 1, 0); - - // Create look-at transformation matrix - lookAt.lookAt(eye, center, up); - - const auto viewerSize = getRenderSize(); - const auto factorX = static_cast(viewerSize.width()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.width()) : 1.0f); - const auto factorY = static_cast(viewerSize.height()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.height()) : 1.0f); - const auto scaleFactor = factorX < factorY ? factorX : factorY; - - const auto d = 1.0f - (2 * _zoomMargin) / std::max(viewerSize.width(), viewerSize.height()); - - // Create scale matrix - scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); + const auto factorX = static_cast(getDataRectangle().width()) / static_cast(getZoomRectangle().width()); + const auto factorY = static_cast(getDataRectangle().height()) / static_cast(getZoomRectangle().height()); + const auto scaleFactor = factorX < factorY ? factorX : factorY; - // Return composite matrix of scale and look-at transformation matrix - return scale * lookAt; + return scaleFactor; } QMatrix4x4 Renderer2D::getProjectionMatrix() const diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 32ccb415b..dece49d36 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -93,10 +93,9 @@ Q_OBJECT /** Returns the matrix that converts normalized screen coordinates [-1..1, -1..1] to screen coordinates [0..width, 0..height] */ QMatrix4x4 getNormalizedScreenToScreenMatrix() const; - /** Returns the view matrix */ - QMatrix4x4 getViewMatrix() const; + float getZoomPercentage() const; - /** Returns the projection matrix */ +/** Returns the projection matrix */ QMatrix4x4 getProjectionMatrix() const; /** @@ -125,6 +124,20 @@ Q_OBJECT */ void setZoomRectangle(const QRectF& zoomRectangle); +public: + + /** + * Get data rectangle + * @return Data rectangle + */ + QRectF getDataRectangle() const; + + /** + * Set data rectangle to \p dataRectangle + * @param dataRectangle Data rectangle + */ + void setDataRectangle(const QRectF& dataRectangle); + signals: /** @@ -139,6 +152,7 @@ Q_OBJECT Navigator2D _navigator; /** 2D navigator */ float _zoomMargin = 100.f; /** Margin for zooming */ QRectF _zoomRectangle; /** Zoom rectangle */ + QRectF _dataRectangle; /** Data rectangle */ friend class Navigator2D; }; From d85253fc42e4b911e4a4a744659a16d3a0305ede Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 17:01:25 +0100 Subject: [PATCH 08/89] Work on Renderer2D --- ManiVault/res/shaders/PointPlot.vert | 38 +++++++++++++++++++---- ManiVault/src/renderers/Navigator2D.cpp | 7 +++++ ManiVault/src/renderers/Navigator2D.h | 6 ++++ ManiVault/src/renderers/PointRenderer.cpp | 6 +++- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 3561a62f4..9c20a5d34 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -11,9 +11,11 @@ #define EFFECT_OUTLINE 3 #define EFFECT_COLOR_2D 4 + + // Point properties -uniform float pointSize; /** Point size */ -uniform float pointSizeScale; /** Scale factor in absolute point size mode */ +uniform float pointSize; /** Point size in x- and y direction to account for anisotropy of the render canvas */ +uniform vec2 viewportSize; /** (width, height) of viewport */ uniform int scalarEffect; uniform float pointOpacity; /** Point opacity */ uniform mat4 mvp; /** Projection matrix from bounds space to clip space */ @@ -42,6 +44,9 @@ uniform float focusOutlineOpacity; /** Focus outline opacity */ uniform bool randomizedDepthEnabled; /** Whether to randomize the z-order */ +// Miscellaneous +uniform float windowAspectRatio; /** Window aspect ratio (width / height) */ + layout(location = 0) in vec2 vertex; /** Vertex input, always a [-1, 1] quad */ layout(location = 1) in vec2 position; /** 2-Dimensional positions of points */ layout(location = 2) in int highlight; /** Mask of highlights over the points */ @@ -113,7 +118,25 @@ void main() { // The texture coordinates match vertex coordinates vTexCoord = vertex; - + + // Convert quad size from pixels to normalized device coordinates (NDC) + vec2 pixelSize = vec2(pointSize) / viewportSize; + + // Apply aspect ratio correction + pixelSize.x *= viewportSize.y / viewportSize.x; // Correct X-axis scaling + + //vec2 scaledPos = vertex * pixelSize; + //vec2 finalPos = position + scaledPos; + + // Apply projection only to the instance position, NOT to the quad size + vec4 worldPos = mvp * vec4(position, 0.0, 1.0); + + // Keep quad size in screen-space (do NOT apply projection to `pixelSize`) + vec2 finalPos = worldPos.xy + (vertex * pixelSize); + + gl_Position = vec4(finalPos, 0.0, 1.0); + + /* // Selection and focus highlighting vHighlight = hasHighlights ? highlight : 0; vFocusHighlight = hasFocusHighlights ? focusHighlight : 0; @@ -126,16 +149,19 @@ void main() if (hasOpacities) vOpacity = opacity; - + + vec2 windowAspectRatioCorrection = vec2(windowAspectRatio, 1); + // Transform position to clip space vec2 pos = (mvp * vec4(position, 0, 1)).xy; // Resize point quad according to properties - vec2 scaledVertex = vertex * pointSize * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); + vec2 scaledVertex = vertex * pointSize * windowAspectRatioCorrection * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); if (hasSizes) - scaledVertex = vertex * size * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); + scaledVertex = vertex * size * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); // Move quad by position and output gl_Position = vec4(scaledVertex + pos, randomizedDepthEnabled ? random(pos) : 0, 1); + */ } diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 88aa60cf2..1a3a2db10 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -154,6 +154,11 @@ QRectF Navigator2D::getZoomRectangle() const }; } +float Navigator2D::getZoomFactor() const +{ + return _zoomFactor; +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) @@ -172,6 +177,8 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) _zoomRectangleTopLeft = p1 + v2; _zoomRectangleSize = getZoomRectangle().size() / factor; + _zoomFactor /= factor; + _renderer.setZoomRectangle(getZoomRectangle()); } endZooming(); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index d5f1c9918..233094eab 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -62,6 +62,12 @@ Q_OBJECT */ QRectF getZoomRectangle() const; + /** + * Get the zoom factor + * @return Zoom factor + */ + float getZoomFactor() const; + public: // Navigation /** diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index a7af9b170..3c13df967 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -439,9 +439,13 @@ namespace mv const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; const auto size = static_cast(std::max(getRenderSize().width(), getRenderSize().height())); + const auto windowAspectRatio = static_cast(getRenderSize().width()) / static_cast(getRenderSize().height()); + const auto pointSizeNDC = QVector2D(_pointSettings._pointSize / size, _pointSettings._pointSize / size); + + qDebug() << "zoom factor: " << getNavigator().getZoomFactor() << "windowAspectRatio" << windowAspectRatio; _shader.uniform1f("pointSize", _pointSettings._pointSize); - _shader.uniform1f("pointSizeScale", absoluteRendering ? 1.0f / size : 1.0f / size); + _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); From 6fdbd99774570a6f35cb99c6a84dbf8642ae41e1 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 17:15:41 +0100 Subject: [PATCH 09/89] Fixed point scaling --- ManiVault/res/shaders/PointPlot.vert | 20 +++++++++++++++++++- ManiVault/src/renderers/PointRenderer.cpp | 5 ----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 9c20a5d34..9832a4f28 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -116,6 +116,21 @@ float random( vec4 v ) { return floatConstruct(hash(floatBitsToUint(v))); } void main() { + // Convert quad size from pixels to normalized device coordinates (NDC) + vec2 pixelSize = vec2(pointSize) / viewportSize; + + // Apply projection only to the instance position, NOT to the quad size + vec4 worldPos = mvp * vec4(position, 0.0, 1.0); + + // Aspect ratio correction for screen-space scaling + vec2 aspectCorrection = vec2(1.0, 1.0); + + // Keep quad size in screen-space while maintaining correct aspect ratio + vec2 finalPos = worldPos.xy + (vertex * pixelSize * aspectCorrection); + + gl_Position = vec4(finalPos, 0.0, 1.0); // Convert to NDC [-1,1] + +/* // The texture coordinates match vertex coordinates vTexCoord = vertex; @@ -128,6 +143,9 @@ void main() //vec2 scaledPos = vertex * pixelSize; //vec2 finalPos = position + scaledPos; + // Aspect ratio correction for screen-space scaling + vec2 aspectCorrection = vec2(viewportSize.y / viewportSize.x, 1.0f); + // Apply projection only to the instance position, NOT to the quad size vec4 worldPos = mvp * vec4(position, 0.0, 1.0); @@ -135,7 +153,7 @@ void main() vec2 finalPos = worldPos.xy + (vertex * pixelSize); gl_Position = vec4(finalPos, 0.0, 1.0); - +*/ /* // Selection and focus highlighting vHighlight = hasHighlights ? highlight : 0; diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 3c13df967..eb9535d87 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -438,11 +438,6 @@ namespace mv const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; - const auto size = static_cast(std::max(getRenderSize().width(), getRenderSize().height())); - const auto windowAspectRatio = static_cast(getRenderSize().width()) / static_cast(getRenderSize().height()); - const auto pointSizeNDC = QVector2D(_pointSettings._pointSize / size, _pointSettings._pointSize / size); - - qDebug() << "zoom factor: " << getNavigator().getZoomFactor() << "windowAspectRatio" << windowAspectRatio; _shader.uniform1f("pointSize", _pointSettings._pointSize); _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); From 07ceb6f29cee09e9822ca6be39e4c0fed1f55867 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 17:41:16 +0100 Subject: [PATCH 10/89] Fixed shaders --- ManiVault/res/shaders/PointPlot.frag | 3 -- ManiVault/res/shaders/PointPlot.vert | 72 ++++++++-------------------- 2 files changed, 21 insertions(+), 54 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.frag b/ManiVault/res/shaders/PointPlot.frag index e6263aa35..7be52f9ce 100644 --- a/ManiVault/res/shaders/PointPlot.frag +++ b/ManiVault/res/shaders/PointPlot.frag @@ -59,9 +59,6 @@ float normalize(float minPixelValue, float maxPixelValue, float pixelValue) void main() { - fragColor = vec4(1, 0, 0, 1); - return; - bool isSelectionHighlighted = vHighlight == 1; bool isFocusHighlighted = vFocusHighlight == 1; bool isHighlighted = isSelectionHighlighted || isFocusHighlighted; diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 9832a4f28..c2cc95ef9 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -116,70 +116,40 @@ float random( vec4 v ) { return floatConstruct(hash(floatBitsToUint(v))); } void main() { + // Use normalized quad vertices as texture coordinates + vTexCoord = vertex; + // Convert quad size from pixels to normalized device coordinates (NDC) vec2 pixelSize = vec2(pointSize) / viewportSize; // Apply projection only to the instance position, NOT to the quad size vec4 worldPos = mvp * vec4(position, 0.0, 1.0); - - // Aspect ratio correction for screen-space scaling - vec2 aspectCorrection = vec2(1.0, 1.0); - - // Keep quad size in screen-space while maintaining correct aspect ratio - vec2 finalPos = worldPos.xy + (vertex * pixelSize * aspectCorrection); - - gl_Position = vec4(finalPos, 0.0, 1.0); // Convert to NDC [-1,1] -/* - // The texture coordinates match vertex coordinates - vTexCoord = vertex; + vec2 scaledVertex; - // Convert quad size from pixels to normalized device coordinates (NDC) - vec2 pixelSize = vec2(pointSize) / viewportSize; - - // Apply aspect ratio correction - pixelSize.x *= viewportSize.y / viewportSize.x; // Correct X-axis scaling + if (hasSizes) + scaledVertex = vertex * (vec2(size) / viewportSize); + else + scaledVertex = vertex * pixelSize; + + // Scale the vertex based on the selection display mode + scaledVertex *= ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); - //vec2 scaledPos = vertex * pixelSize; - //vec2 finalPos = position + scaledPos; - - // Aspect ratio correction for screen-space scaling - vec2 aspectCorrection = vec2(viewportSize.y / viewportSize.x, 1.0f); + // Keep quad size in screen-space while maintaining correct aspect ratio + vec2 finalPos = worldPos.xy + scaledVertex; - // Apply projection only to the instance position, NOT to the quad size - vec4 worldPos = mvp * vec4(position, 0.0, 1.0); + // Compute random depth + float depth = randomizedDepthEnabled ? random(worldPos.xy) : 0; - // Keep quad size in screen-space (do NOT apply projection to `pixelSize`) - vec2 finalPos = worldPos.xy + (vertex * pixelSize); + // Set the final position + gl_Position = vec4(finalPos, depth, 1.0); // Convert to NDC [-1,1] - gl_Position = vec4(finalPos, 0.0, 1.0); -*/ - /* - // Selection and focus highlighting - vHighlight = hasHighlights ? highlight : 0; + vHighlight = hasHighlights ? highlight : 0; vFocusHighlight = hasFocusHighlights ? focusHighlight : 0; - - vScalar = hasScalars ? (scalar - colorMapRange.x) / colorMapRange.z : 0; - - vColor = hasColors ? color : vec3(0.5); - - vOpacity = pointOpacity; + vScalar = hasScalars ? (scalar - colorMapRange.x) / colorMapRange.z : 0; + vColor = hasColors ? color : vec3(0.5); + vOpacity = pointOpacity; if (hasOpacities) vOpacity = opacity; - - vec2 windowAspectRatioCorrection = vec2(windowAspectRatio, 1); - - // Transform position to clip space - vec2 pos = (mvp * vec4(position, 0, 1)).xy; - - // Resize point quad according to properties - vec2 scaledVertex = vertex * pointSize * windowAspectRatioCorrection * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); - - if (hasSizes) - scaledVertex = vertex * size * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); - - // Move quad by position and output - gl_Position = vec4(scaledVertex + pos, randomizedDepthEnabled ? random(pos) : 0, 1); - */ } From d74a7a391c74d0f137644fe6a163ca95dcf5297b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 18:19:32 +0100 Subject: [PATCH 11/89] Relative point sizing works --- ManiVault/res/shaders/PointPlot.vert | 17 ++++++++++------- ManiVault/src/renderers/PointRenderer.cpp | 3 ++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index c2cc95ef9..d0a1d4588 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -15,6 +15,7 @@ // Point properties uniform float pointSize; /** Point size in x- and y direction to account for anisotropy of the render canvas */ +uniform bool pointSizeAbsolute; /** Whether the point size is in world or screen coordinates */ uniform vec2 viewportSize; /** (width, height) of viewport */ uniform int scalarEffect; uniform float pointOpacity; /** Point opacity */ @@ -120,17 +121,16 @@ void main() vTexCoord = vertex; // Convert quad size from pixels to normalized device coordinates (NDC) - vec2 pixelSize = vec2(pointSize) / viewportSize; + vec2 pixelSize = vec2(hasSizes ? size : pointSize) / viewportSize; +// if (!pointSizeAbsolute) +// pixelSize /= viewportSize; + // Apply projection only to the instance position, NOT to the quad size vec4 worldPos = mvp * vec4(position, 0.0, 1.0); - vec2 scaledVertex; - - if (hasSizes) - scaledVertex = vertex * (vec2(size) / viewportSize); - else - scaledVertex = vertex * pixelSize; + // Compute the scaled vertex position + vec2 scaledVertex = vertex * pixelSize; // Scale the vertex based on the selection display mode scaledVertex *= ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); @@ -138,6 +138,9 @@ void main() // Keep quad size in screen-space while maintaining correct aspect ratio vec2 finalPos = worldPos.xy + scaledVertex; +// if (pointSizeAbsolute) +// finalPos = (mvp * vec4(position + scaledVertex, 0.0, 1.0)).xy; + // Compute random depth float depth = randomizedDepthEnabled ? random(worldPos.xy) : 0; diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index eb9535d87..b7ae1ac14 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -437,9 +437,10 @@ namespace mv modelMatrix.setToIdentity(); const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; - const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; + const bool pointSizeAbsolute = _pointSettings._scalingMode == PointScaling::Absolute; _shader.uniform1f("pointSize", _pointSettings._pointSize); + _shader.uniform1i("pointSizeAbsolute", pointSizeAbsolute); _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); From f0f37b1513a2cf20c88613c326ec3ab8792917eb Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 20:54:42 +0100 Subject: [PATCH 12/89] Navigation is more or less working properly now, end of day commit --- ManiVault/res/shaders/DensityDraw.frag | 3 + ManiVault/res/shaders/PointPlot.vert | 2 - ManiVault/res/shaders/Quad.vert | 4 +- ManiVault/src/renderers/DensityRenderer.cpp | 46 ++++---- ManiVault/src/renderers/DensityRenderer.h | 3 - ManiVault/src/renderers/Navigator2D.cpp | 111 ++++++++++++++++---- ManiVault/src/renderers/Navigator2D.h | 75 ++++++++++--- ManiVault/src/renderers/PointRenderer.cpp | 2 +- ManiVault/src/renderers/PointRenderer.h | 2 - ManiVault/src/renderers/Renderer2D.cpp | 55 +++++----- ManiVault/src/renderers/Renderer2D.h | 49 +++------ 11 files changed, 226 insertions(+), 126 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index d21a90ea8..30e8c189d 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -14,4 +14,7 @@ out vec4 fragColor; void main() { float f = 1 - (texture(tex, pass_texCoord).r * norm); fragColor = vec4(vec3(f), 1); + + if (pass_texCoord.x < 0.1 || pass_texCoord.x > 0.9) + fragColor = vec4(1,0,0,1); } diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index d0a1d4588..c263861d4 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -11,8 +11,6 @@ #define EFFECT_OUTLINE 3 #define EFFECT_COLOR_2D 4 - - // Point properties uniform float pointSize; /** Point size in x- and y direction to account for anisotropy of the render canvas */ uniform bool pointSizeAbsolute; /** Whether the point size is in world or screen coordinates */ diff --git a/ManiVault/res/shaders/Quad.vert b/ManiVault/res/shaders/Quad.vert index 2fd2f7175..14cbb55e2 100644 --- a/ManiVault/res/shaders/Quad.vert +++ b/ManiVault/res/shaders/Quad.vert @@ -4,9 +4,11 @@ #version 330 core +uniform mat4 mvp; + out vec2 pass_texCoord; void main() { pass_texCoord = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2); - gl_Position = vec4(pass_texCoord * 2 - 1, 0, 1); + gl_Position = mvp * vec4(pass_texCoord * 2 - 1, 0, 1); } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 04f0f238e..382200f72 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -12,7 +12,7 @@ namespace mv DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { - + getNavigator().setZoomRectangle(QRectF(0, 0, 1, 1)); } DensityRenderer::~DensityRenderer() @@ -88,29 +88,23 @@ namespace mv _densityComputation.init(QOpenGLContext::currentContext()); } - void DensityRenderer::resize(QSize renderSize) - { - int w = renderSize.width(); - int h = renderSize.height(); - - _windowSize.setWidth(w); - _windowSize.setHeight(h); - } - void DensityRenderer::render() { - glViewport(0, 0, _windowSize.width(), _windowSize.height()); - - int w = _windowSize.width(); - int h = _windowSize.height(); - int size = w < h ? w : h; - glViewport(w / 2 - size / 2, h / 2 - size / 2, size, size); - - // Draw density or isolines map - switch (_renderMode) { - case DENSITY: drawDensity(); break; - case LANDSCAPE: drawLandscape(); break; + beginRender(); + { + switch (_renderMode) { + case DENSITY: { + drawDensity(); + break; + } + + case LANDSCAPE: { + drawLandscape(); + break; + } + } } + endRender(); } void DensityRenderer::destroy() @@ -138,6 +132,16 @@ namespace mv _shaderDensityDraw.bind(); _densityComputation.getDensityTexture().bind(0); + + QMatrix4x4 modelMatrix; + + modelMatrix.setToIdentity(); + + const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; + + qDebug() << mvp; + + _shaderDensityDraw.uniformMatrix4f("mvp", mvp.data()); _shaderDensityDraw.uniform1i("tex", 0); _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 3ab56fd63..f2568a85f 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -49,7 +49,6 @@ namespace mv void setColormap(const QImage& image); void init() override; - void resize(QSize renderSize) override; void render() override; @@ -64,8 +63,6 @@ namespace mv void drawFullscreenQuad(); private: - QSize _windowSize; - bool _isSelecting = false; bool _hasColorMap = false; diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 1a3a2db10..fa96a18db 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -17,13 +17,15 @@ namespace mv Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : QObject(parent), _renderer(renderer), + _enabled(false), _initialized(false), _isNavigating(false), _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleSize(1000, 1000), - _zoomRectangleMargin(0.f) + _zoomRectangleSize(1, 1), + _zoomRectangleMargin(0.f), + _userHasNavigated() { } @@ -43,7 +45,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) bool Navigator2D::eventFilter(QObject* watched, QEvent* event) { - if (!_initialized) + if (!_initialized || !_enabled) return false; if (event->type() == QEvent::KeyPress) { @@ -154,11 +156,40 @@ QRectF Navigator2D::getZoomRectangle() const }; } +void Navigator2D::setZoomRectangle(const QRectF& zoomRectangle) +{ +#ifdef NAVIGATOR_2D_VERBOSE + qDebug() << __FUNCTION__ << zoomRectangle; +#endif + + const auto previousZoomRectangle = getZoomRectangle(); + + _zoomRectangleTopLeft = zoomRectangle.topLeft(); + _zoomRectangleSize = zoomRectangle.size(); + + emit zoomRectangleChanged(previousZoomRectangle, getZoomRectangle()); +} + float Navigator2D::getZoomFactor() const { return _zoomFactor; } +bool Navigator2D::isEnabled() const +{ + return _enabled; +} + +void Navigator2D::setEnabled(bool enabled) +{ + if (enabled == _enabled) + return; + + _enabled = enabled; + + emit enabledChanged(_enabled); +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) @@ -170,16 +201,20 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) beginZooming(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - const auto v1 = getZoomRectangle().topLeft() - p1; - const auto v2 = v1 / factor; + beginChangeZoomRectangle(); + { + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); + const auto v1 = getZoomRectangle().topLeft() - p1; + const auto v2 = v1 / factor; - _zoomRectangleTopLeft = p1 + v2; - _zoomRectangleSize = getZoomRectangle().size() / factor; + _zoomRectangleTopLeft = p1 + v2; + _zoomRectangleSize = getZoomRectangle().size() / factor; - _zoomFactor /= factor; + _zoomFactor /= factor; - _renderer.setZoomRectangle(getZoomRectangle()); + setZoomRectangle(getZoomRectangle()); + } + endChangeZoomRectangle(); } endZooming(); } @@ -195,7 +230,10 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) beginZooming(); { - + beginChangeZoomRectangle(); + { + } + endChangeZoomRectangle(); } endZooming(); } @@ -211,24 +249,42 @@ void Navigator2D::panBy(const QPointF& delta) beginPanning(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); - const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); + beginChangeZoomRectangle(); + { + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); - - _renderer.setZoomRectangle(getZoomRectangle()); + _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); + } + endChangeZoomRectangle(); } endPanning(); } -void Navigator2D::resetView() +void Navigator2D::resetView(bool force /*= true*/) { if (!_initialized) return; #ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__; + qDebug() << __FUNCTION__ << force; #endif + + beginZooming(); + { + beginChangeZoomRectangle(); + { + setZoomRectangle(_renderer.getDataBounds()); + + if (!_userHasNavigated || force) { + setZoomRectangle(_renderer.getDataBounds()); + + _userHasNavigated = false; + } + } + endChangeZoomRectangle(); + } + endZooming(); } bool Navigator2D::isPanning() const @@ -246,6 +302,11 @@ bool Navigator2D::isNavigating() const return _isNavigating; } +bool Navigator2D::hasUserNavigated() const +{ + return _userHasNavigated; +} + void Navigator2D::setIsPanning(bool isPanning) { if (!_initialized) @@ -296,6 +357,8 @@ void Navigator2D::beginPanning() setIsPanning(true); + _userHasNavigated = true; + emit panningStarted(); } @@ -324,6 +387,8 @@ void Navigator2D::beginZooming() setIsZooming(true); + _userHasNavigated = true; + emit zoomingStarted(); } @@ -369,4 +434,14 @@ void Navigator2D::endNavigation() emit navigationEnded(); } +void Navigator2D::beginChangeZoomRectangle() +{ + _previousZoomRectangle = getZoomRectangle(); +} + +void Navigator2D::endChangeZoomRectangle() +{ + emit zoomRectangleChanged(_previousZoomRectangle, getZoomRectangle()); +} + } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 233094eab..6cd1771a6 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -62,12 +62,30 @@ Q_OBJECT */ QRectF getZoomRectangle() const; + /** + * Set the zoom rectangle to \p zoomRectangle + * @param zoomRectangle Zoom rectangle + */ + void setZoomRectangle(const QRectF& zoomRectangle); + /** * Get the zoom factor * @return Zoom factor */ float getZoomFactor() const; + /** + * Get whether the navigator is enabled + * @return Boolean determining whether the navigator is enabled + */ + bool isEnabled() const; + + /** + * Set enabled to \p enabled + * @param enabled Boolean determining whether the navigator is enabled + */ + void setEnabled(bool enabled); + public: // Navigation /** @@ -89,8 +107,11 @@ Q_OBJECT */ void panBy(const QPointF& delta); - /** Zoom to extents of the data bounds (with a margin around it) */ - void resetView(); + /** + * Reset the view + * @param force Force reset event when the user has navigated + */ + void resetView(bool force = true); /** * Get whether the renderer is panning @@ -110,6 +131,12 @@ Q_OBJECT */ bool isNavigating() const; + /** + * Get whether the user has navigated + * @return Boolean determining whether the user has navigated + */ + bool hasUserNavigated() const; + protected: // Navigation /** @@ -148,6 +175,12 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); + /** Begin changing the zoom rectangle */ + void beginChangeZoomRectangle(); + + /** End changing the zoom rectangle */ + void endChangeZoomRectangle(); + signals: /** Signals that panning has started */ @@ -186,20 +219,34 @@ Q_OBJECT */ void isNavigatingChanged(bool isNavigating); -private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ + /** + * Signals that enabled changed to \p enabled + * @param enabled Boolean determining whether the navigator is enabled + */ + void enabledChanged(bool enabled); + + /** + * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle + * @param previousZoomRectangle Previous zoom rectangle + * @param currentZoomRectangle Current zoom rectangle + */ + void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); private: - QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ - QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ + QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ + QRectF _previousZoomRectangle; /** Previous zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index b7ae1ac14..1f30937fb 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -445,7 +445,7 @@ namespace mv _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); - _shader.uniform4f("dataBounds", getDataRectangle().left(), getDataRectangle().right(), getDataRectangle().bottom(), getDataRectangle().top()); + _shader.uniform4f("dataBounds", getDataBounds().left(), getDataBounds().right(), getDataBounds().bottom(), getDataBounds().top()); _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 306f7f7d2..b0ca4c781 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -79,8 +79,6 @@ namespace mv void draw(); - void updateQuadVertices(const QSizeF& quadSize); - void destroy(); private: diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 3397e7f39..4b944d7bd 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -16,7 +16,7 @@ namespace mv Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), _navigator(*this), - _zoomRectangle(0, 0, 2000, 1000) + _zoomMargin(10.f) { } @@ -35,6 +35,11 @@ Navigator2D& Renderer2D::getNavigator() return _navigator; } +const Navigator2D& Renderer2D::getNavigator() const +{ + return _navigator; +} + void Renderer2D::beginRender() { #ifdef RENDERER_2D_VERBOSE @@ -51,38 +56,24 @@ void Renderer2D::endRender() #endif } -QRectF Renderer2D::getZoomRectangle() const +QRectF Renderer2D::getDataBounds() const { - return _zoomRectangle; + return _dataBounds; } -void Renderer2D::setZoomRectangle(const QRectF& zoomRectangle) +void Renderer2D::setDataBounds(const QRectF& dataBounds) { #ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCTION__ << zoomRectangle; + qDebug() << __FUNCTION__ << dataBounds; #endif - if (zoomRectangle == _zoomRectangle) - return; - - const auto previousZoomRectangle = _zoomRectangle; - - _zoomRectangle = zoomRectangle; - - emit zoomRectangleChanged(previousZoomRectangle, _zoomRectangle); -} - -QRectF Renderer2D::getDataRectangle() const -{ - return _dataRectangle; -} - -void Renderer2D::setDataRectangle(const QRectF& dataRectangle) -{ - if (dataRectangle == _dataRectangle) + if (dataBounds == _dataBounds) return; - _dataRectangle = dataRectangle; + _dataBounds = dataBounds; + + //if (!_navigator.hasUserNavigated()) + getNavigator().setZoomRectangle(_dataBounds);//.marginsAdded({ _zoomMargin, _zoomMargin, _zoomMargin, _zoomMargin })); } QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const @@ -93,13 +84,14 @@ QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewM QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const { const auto clipSpacePos = getProjectionMatrix() * (_navigator.getViewMatrix() * QVector4D(position, 1.0)); + return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); } QPoint Renderer2D::getWorldPositionToScreenPoint(const QVector3D& position) const { - const auto normalizedScreenPoint = QVector2D(1.0f, -1.0f) * getWorldPositionToNormalizedScreenPoint(position); - const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + const auto normalizedScreenPoint = QVector2D(1.0f, -1.0f) * getWorldPositionToNormalizedScreenPoint(position); + const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); return (viewSize * ((QVector2D(1.0f, 1.0f) + normalizedScreenPoint) / 2.0f)).toPoint(); } @@ -107,6 +99,7 @@ QPoint Renderer2D::getWorldPositionToScreenPoint(const QVector3D& position) cons QVector2D Renderer2D::getScreenPointToNormalizedScreenPoint(const QVector2D& screenPoint) const { const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + return QVector2D(-1.f, -1.f) + 2.f * (QVector2D(screenPoint.x(), getRenderSize().height() - screenPoint.y()) / viewSize); } @@ -124,7 +117,7 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const { QMatrix4x4 translate, scale; - const auto size = QSizeF(getRenderSize()); + const auto size = QSizeF(getRenderSize()); const auto halfSize = 0.5f * size; scale.scale(halfSize.width(), halfSize.height(), 1.0f); @@ -135,8 +128,8 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const float Renderer2D::getZoomPercentage() const { - const auto factorX = static_cast(getDataRectangle().width()) / static_cast(getZoomRectangle().width()); - const auto factorY = static_cast(getDataRectangle().height()) / static_cast(getZoomRectangle().height()); + const auto factorX = static_cast(getDataBounds().width()) / static_cast(getNavigator().getZoomRectangle().width()); + const auto factorY = static_cast(getDataBounds().height()) / static_cast(getNavigator().getZoomRectangle().height()); const auto scaleFactor = factorX < factorY ? factorX : factorY; return scaleFactor; @@ -158,8 +151,8 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QRect Renderer2D::getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const { // Compute screen bounding rectangle extremes - const auto topLeftScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.bottomLeft())); - const auto bottomRightScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.topRight())); + const auto topLeftScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.bottomLeft())); + const auto bottomRightScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.topRight())); return { topLeftScreen, diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index dece49d36..c66a210e5 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -51,11 +51,16 @@ Q_OBJECT /** * Get the 2D navigator - * * @return Reference to the 2D navigator */ Navigator2D& getNavigator(); + /** + * Get the 2D navigator + * @return Reference to the 2D navigator + */ + const Navigator2D& getNavigator() const; + public: // Coordinate conversions /** @@ -112,47 +117,25 @@ Q_OBJECT /** End rendering */ void endRender() override; - /** - * Get the zoom rectangle - * @return Zoom rectangle - */ - QRectF getZoomRectangle() const; - - /** - * Set the zoom rectangle to \p zoomRectangle - * @param zoomRectangle Zoom rectangle - */ - void setZoomRectangle(const QRectF& zoomRectangle); - public: /** - * Get data rectangle - * @return Data rectangle + * Get data bounds + * @return Data bounds */ - QRectF getDataRectangle() const; - - /** - * Set data rectangle to \p dataRectangle - * @param dataRectangle Data rectangle - */ - void setDataRectangle(const QRectF& dataRectangle); - -signals: + QRectF getDataBounds() const; /** - * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle - * @param previousZoomRectangle Previous zoom rectangle - * @param currentZoomRectangle Current zoom rectangle + * Set data bounds to \p dataBounds + * @param dataBounds Data bounds */ - void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); + void setDataBounds(const QRectF& dataBounds); private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ - float _zoomMargin = 100.f; /** Margin for zooming */ - QRectF _zoomRectangle; /** Zoom rectangle */ - QRectF _dataRectangle; /** Data rectangle */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + float _zoomMargin; /** Margin for zooming */ + QRectF _dataBounds; /** Bounds of the data */ friend class Navigator2D; }; From b924d507e498d66bbf20ea686ece86e13a5f2d8b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 26 Mar 2025 07:55:01 +0100 Subject: [PATCH 13/89] Fixes problem with data bounds --- ManiVault/src/renderers/DensityRenderer.cpp | 2 +- ManiVault/src/renderers/Navigator2D.cpp | 81 ++++++++++++--------- ManiVault/src/renderers/Navigator2D.h | 56 +++++++------- ManiVault/src/renderers/PointRenderer.cpp | 2 +- ManiVault/src/renderers/Renderer2D.cpp | 8 +- 5 files changed, 79 insertions(+), 70 deletions(-) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 382200f72..1c871fca6 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -12,7 +12,7 @@ namespace mv DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { - getNavigator().setZoomRectangle(QRectF(0, 0, 1, 1)); + getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); } DensityRenderer::~DensityRenderer() diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index fa96a18db..17070b836 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -23,7 +23,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleSize(1, 1), + _zoomRectangleWorldSize(1, 1), _zoomRectangleMargin(0.f), _userHasNavigated() { @@ -126,17 +126,23 @@ QMatrix4x4 Navigator2D::getViewMatrix() const { QMatrix4x4 lookAt, scale; + const auto zoomRectangle = getZoomRectangleWorld(); + // Construct look-at parameters - const auto eye = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 1); - const auto center = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 0); + const auto eye = QVector3D(zoomRectangle.center().x(), zoomRectangle.center().y(), 1); + const auto center = QVector3D(zoomRectangle.center().x(), zoomRectangle.center().y(), 0); const auto up = QVector3D(0, 1, 0); +#ifdef NAVIGATOR_2D_VERBOSE + qDebug() << __FUNCTION__ << getZoomRectangleWorld() << _renderer.getDataBounds() << eye; +#endif + // Create look-at transformation matrix lookAt.lookAt(eye, center, up); const auto viewerSize = _renderer.getRenderSize(); - const auto factorX = static_cast(viewerSize.width()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().width()) : 1.0f); - const auto factorY = static_cast(viewerSize.height()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().height()) : 1.0f); + const auto factorX = static_cast(viewerSize.width()) / (zoomRectangle.isValid() ? static_cast(zoomRectangle.width()) : 1.0f); + const auto factorY = static_cast(viewerSize.height()) / (zoomRectangle.isValid() ? static_cast(zoomRectangle.height()) : 1.0f); const auto scaleFactor = factorX < factorY ? factorX : factorY; const auto d = 1.0f - (2 * _zoomRectangleMargin) / std::max(viewerSize.width(), viewerSize.height()); @@ -148,26 +154,26 @@ QMatrix4x4 Navigator2D::getViewMatrix() const return scale * lookAt; } -QRectF Navigator2D::getZoomRectangle() const +QRectF Navigator2D::getZoomRectangleWorld() const { return { - _zoomRectangleTopLeft, - _zoomRectangleSize + _zoomRectangleWorldTopLeft, + _zoomRectangleWorldSize }; } -void Navigator2D::setZoomRectangle(const QRectF& zoomRectangle) +void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) { #ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << zoomRectangle; + qDebug() << __FUNCTION__ << zoomRectangleWorld; #endif - const auto previousZoomRectangle = getZoomRectangle(); + const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - _zoomRectangleTopLeft = zoomRectangle.topLeft(); - _zoomRectangleSize = zoomRectangle.size(); + _zoomRectangleWorldTopLeft = zoomRectangleWorld.topLeft(); + _zoomRectangleWorldSize = zoomRectangleWorld.size(); - emit zoomRectangleChanged(previousZoomRectangle, getZoomRectangle()); + emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } float Navigator2D::getZoomFactor() const @@ -201,20 +207,20 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) beginZooming(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - const auto v1 = getZoomRectangle().topLeft() - p1; + const auto v1 = getZoomRectangleWorld().topLeft() - p1; const auto v2 = v1 / factor; - _zoomRectangleTopLeft = p1 + v2; - _zoomRectangleSize = getZoomRectangle().size() / factor; + _zoomRectangleWorldTopLeft = p1 + v2; + _zoomRectangleWorldSize = getZoomRectangleWorld().size() / factor; _zoomFactor /= factor; - setZoomRectangle(getZoomRectangle()); + setZoomRectangleWorld(getZoomRectangleWorld()); } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endZooming(); } @@ -230,10 +236,10 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) beginZooming(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endZooming(); } @@ -249,14 +255,14 @@ void Navigator2D::panBy(const QPointF& delta) beginPanning(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); + _zoomRectangleWorldTopLeft = getZoomRectangleWorld().topLeft() + (p2 - p1); } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endPanning(); } @@ -272,17 +278,20 @@ void Navigator2D::resetView(bool force /*= true*/) beginZooming(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { - setZoomRectangle(_renderer.getDataBounds()); + _zoomRectangleWorldTopLeft = _renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + _zoomRectangleWorldSize = _renderer.getDataBounds().size(); + + setZoomRectangleWorld(getZoomRectangleWorld()); - if (!_userHasNavigated || force) { - setZoomRectangle(_renderer.getDataBounds()); + // if (!_userHasNavigated || force) { + // setZoomRectangleWorld(_renderer.getDataBounds()); - _userHasNavigated = false; - } + // _userHasNavigated = false; + //} } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endZooming(); } @@ -434,14 +443,14 @@ void Navigator2D::endNavigation() emit navigationEnded(); } -void Navigator2D::beginChangeZoomRectangle() +void Navigator2D::beginChangeZoomRectangleWorld() { - _previousZoomRectangle = getZoomRectangle(); + _previousZoomRectangleWorld = getZoomRectangleWorld(); } -void Navigator2D::endChangeZoomRectangle() +void Navigator2D::endChangeZoomRectangleWorld() { - emit zoomRectangleChanged(_previousZoomRectangle, getZoomRectangle()); + emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 6cd1771a6..b1f05fd8d 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -57,16 +57,16 @@ Q_OBJECT QMatrix4x4 getViewMatrix() const; /** - * Get the zoom rectangle - * @return Zoom rectangle + * Get the world zoom rectangle + * @return Zoom rectangle in world coordinates */ - QRectF getZoomRectangle() const; + QRectF getZoomRectangleWorld() const; /** - * Set the zoom rectangle to \p zoomRectangle - * @param zoomRectangle Zoom rectangle + * Set the world zoom rectangle to \p zoomRectangleWorld + * @param zoomRectangleWorld Zoom rectangle in world coordinates */ - void setZoomRectangle(const QRectF& zoomRectangle); + void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); /** * Get the zoom factor @@ -175,11 +175,11 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); - /** Begin changing the zoom rectangle */ - void beginChangeZoomRectangle(); + /** Begin changing the zoom rectangle in world coordinates */ + void beginChangeZoomRectangleWorld(); - /** End changing the zoom rectangle */ - void endChangeZoomRectangle(); + /** End changing the zoom rectangle in world coordinates */ + void endChangeZoomRectangleWorld(); signals: @@ -226,27 +226,27 @@ Q_OBJECT void enabledChanged(bool enabled); /** - * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle - * @param previousZoomRectangle Previous zoom rectangle - * @param currentZoomRectangle Current zoom rectangle + * Signals that the zoom rectangle in world coordinates has changed from \p previousZoomRectangleWorld to \p currentZoomRectangleWorld + * @param previousZoomRectangleWorld Previous world zoom rectangle + * @param currentZoomRectangleWorld Current world zoom rectangle */ - void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); + void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _enabled; /** Enabled flag */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ - QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ - QRectF _previousZoomRectangle; /** Previous zoom rectangle */ - bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomRectangleWorldTopLeft; /** Zoom rectangle top-left in world coordinates */ + QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ + QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 1f30937fb..f13305bb0 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -436,7 +436,7 @@ namespace mv modelMatrix.setToIdentity(); - const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; + const auto mvp = QMatrix4x4(getProjectionMatrix())* getNavigator().getViewMatrix()* modelMatrix; const bool pointSizeAbsolute = _pointSettings._scalingMode == PointScaling::Absolute; _shader.uniform1f("pointSize", _pointSettings._pointSize); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 4b944d7bd..d5bb7a9ca 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -72,8 +72,7 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) _dataBounds = dataBounds; - //if (!_navigator.hasUserNavigated()) - getNavigator().setZoomRectangle(_dataBounds);//.marginsAdded({ _zoomMargin, _zoomMargin, _zoomMargin, _zoomMargin })); + getNavigator().resetView(); } QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const @@ -128,8 +127,8 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const float Renderer2D::getZoomPercentage() const { - const auto factorX = static_cast(getDataBounds().width()) / static_cast(getNavigator().getZoomRectangle().width()); - const auto factorY = static_cast(getDataBounds().height()) / static_cast(getNavigator().getZoomRectangle().height()); + const auto factorX = static_cast(getDataBounds().width()) / static_cast(getNavigator().getZoomRectangleWorld().width()); + const auto factorY = static_cast(getDataBounds().height()) / static_cast(getNavigator().getZoomRectangleWorld().height()); const auto scaleFactor = factorX < factorY ? factorX : factorY; return scaleFactor; @@ -144,6 +143,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const // Create an orthogonal transformation matrix matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); + //matrix.ortho(-halfSize.width(), halfSize.width(), -getRenderSize().height(), 0, -1000.0f, +1000.0f); return matrix; } From bd50e838fcb40c5221a804e1fe8225d69a55b0da Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 26 Mar 2025 08:00:58 +0100 Subject: [PATCH 14/89] Fixes issues with randomized depth --- ManiVault/res/shaders/PointPlot.vert | 2 +- ManiVault/src/renderers/Navigator2D.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index c263861d4..d77921631 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -140,7 +140,7 @@ void main() // finalPos = (mvp * vec4(position + scaledVertex, 0.0, 1.0)).xy; // Compute random depth - float depth = randomizedDepthEnabled ? random(worldPos.xy) : 0; + float depth = randomizedDepthEnabled ? random(vec2(gl_InstanceID, 0)) : 0; // Set the final position gl_Position = vec4(finalPos, depth, 1.0); // Convert to NDC [-1,1] diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 17070b836..75ffa67dd 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -280,8 +280,8 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - _zoomRectangleWorldTopLeft = _renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); - _zoomRectangleWorldSize = _renderer.getDataBounds().size(); + _zoomRectangleWorldTopLeft = _renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + _zoomRectangleWorldSize = _renderer.getDataBounds().size(); setZoomRectangleWorld(getZoomRectangleWorld()); From 7efd06535ea0cb43da628327a47cd6b71882a157 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 10:49:14 +0100 Subject: [PATCH 15/89] Working on selections --- .../src/actions/DecimalRectangleAction.cpp | 15 +++++++++++ .../src/actions/DecimalRectangleAction.h | 12 +++++++++ ManiVault/src/renderers/Navigator2D.cpp | 25 +++++++++++++++++-- ManiVault/src/renderers/Navigator2D.h | 6 +++++ ManiVault/src/renderers/Renderer2D.cpp | 10 +++++--- ManiVault/src/renderers/Renderer2D.h | 3 +-- 6 files changed, 63 insertions(+), 8 deletions(-) diff --git a/ManiVault/src/actions/DecimalRectangleAction.cpp b/ManiVault/src/actions/DecimalRectangleAction.cpp index e6ca6f347..a64dc110c 100644 --- a/ManiVault/src/actions/DecimalRectangleAction.cpp +++ b/ManiVault/src/actions/DecimalRectangleAction.cpp @@ -21,6 +21,21 @@ DecimalRectangleAction::DecimalRectangleAction(QObject * parent, const QString& connect(&getRangeAction(Axis::Y), &DecimalRangeAction::rangeChanged, this, [this]() -> void { _rectangleChanged(); }); } +QRectF DecimalRectangleAction::toRectF() const +{ + return { + getLeft(), + getTop(), + getWidth(), + getHeight() + }; +} + +void DecimalRectangleAction::setRectF(const QRectF& rectangle) +{ + setRectangle(rectangle.left(), rectangle.right(), rectangle.bottom(), rectangle.top()); +} + void DecimalRectangleAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) { auto publicDecimalRectangleAction = dynamic_cast(publicAction); diff --git a/ManiVault/src/actions/DecimalRectangleAction.h b/ManiVault/src/actions/DecimalRectangleAction.h index a6660f1d4..87732ba2e 100644 --- a/ManiVault/src/actions/DecimalRectangleAction.h +++ b/ManiVault/src/actions/DecimalRectangleAction.h @@ -44,6 +44,18 @@ class CORE_EXPORT DecimalRectangleAction : public RectangleAction(_zoomRectangleWorldSize.width()) / aspectRatio); + } + else { + _zoomRectangleWorldSize.setWidth(static_cast(_zoomRectangleWorldSize.height()) * aspectRatio); + } + + const auto zoomRectangleWorldCenter = _renderer.getDataBounds().center(); + const auto zoomRectangleWorldOffset = QPointF(_zoomRectangleWorldSize.width() / 2.f, _zoomRectangleWorldSize.height() / 2.f); + + _zoomRectangleWorldTopLeft = zoomRectangleWorldCenter - zoomRectangleWorldOffset + QPoint(0, -_renderer.getDataBounds().height());//_renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + setZoomRectangleWorld(getZoomRectangleWorld()); // if (!_userHasNavigated || force) { diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index b1f05fd8d..c8b09f7c8 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -68,6 +68,12 @@ Q_OBJECT */ void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); + /** + * Get the zoom rectangle margin + * @return Zoom rectangle margin + */ + float getZoomRectangleMargin() const; + /** * Get the zoom factor * @return Zoom factor diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index d5bb7a9ca..16309d4ee 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -8,15 +8,14 @@ //#define RENDERER_2D_VERBOSE #endif -#define RENDERER_2D_VERBOSE +//#define RENDERER_2D_VERBOSE namespace mv { Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), - _navigator(*this), - _zoomMargin(10.f) + _navigator(*this) { } @@ -71,7 +70,10 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) return; _dataBounds = dataBounds; - + + //_dataBounds.setWidth(std::max(_dataBounds.width(), 0.00000001)); + //_dataBounds.setHeight(std::max(_dataBounds.height(), 0.0001)); + getNavigator().resetView(); } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index c66a210e5..061cf506f 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -100,7 +100,7 @@ Q_OBJECT float getZoomPercentage() const; -/** Returns the projection matrix */ + /** Returns the projection matrix */ QMatrix4x4 getProjectionMatrix() const; /** @@ -134,7 +134,6 @@ Q_OBJECT private: QSize _renderSize; /** Size of the renderer canvas */ Navigator2D _navigator; /** 2D navigator */ - float _zoomMargin; /** Margin for zooming */ QRectF _dataBounds; /** Bounds of the data */ friend class Navigator2D; From 362c2aed1f57af7a1f2bac4d6a6320a067b1e85e Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:02:23 +0100 Subject: [PATCH 16/89] Zoom around also working now --- ManiVault/src/renderers/Navigator2D.cpp | 43 ++++++++----------------- ManiVault/src/renderers/Navigator2D.h | 4 +-- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index abf954142..b489d28fb 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -24,7 +24,6 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleWorldSize(1, 1), _zoomRectangleMargin(0.f), _userHasNavigated() { @@ -157,9 +156,12 @@ QMatrix4x4 Navigator2D::getViewMatrix() const QRectF Navigator2D::getZoomRectangleWorld() const { + const auto zoomRectangleWorldSize = _renderer.getRenderSize().toSizeF() / _zoomFactor; + const auto zoomRectangleWorldTopLeft = _zoomCenterWorld - QPointF(.5f * static_cast(zoomRectangleWorldSize.width()), .5f * static_cast(zoomRectangleWorldSize.height())); + return { - _zoomRectangleWorldTopLeft, - _zoomRectangleWorldSize + zoomRectangleWorldTopLeft, + zoomRectangleWorldSize }; } @@ -171,8 +173,8 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - _zoomRectangleWorldTopLeft = zoomRectangleWorld.topLeft(); - _zoomRectangleWorldSize = zoomRectangleWorld.size(); + _zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); + _zoomCenterWorld = zoomRectangleWorld.center(); emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } @@ -215,14 +217,10 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) { beginChangeZoomRectangleWorld(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - const auto v1 = getZoomRectangleWorld().topLeft() - p1; - const auto v2 = v1 / factor; - - _zoomRectangleWorldTopLeft = p1 + v2; - _zoomRectangleWorldSize = getZoomRectangleWorld().size() / factor; + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - _zoomFactor /= factor; + _zoomFactor /= factor; + _zoomCenterWorld = p1 + (getZoomRectangleWorld().center() - p1) * factor; setZoomRectangleWorld(getZoomRectangleWorld()); } @@ -266,7 +264,7 @@ void Navigator2D::panBy(const QPointF& delta) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomRectangleWorldTopLeft = getZoomRectangleWorld().topLeft() + (p2 - p1); + _zoomCenterWorld = getZoomRectangleWorld().center() + (p2 - p1); } endChangeZoomRectangleWorld(); } @@ -286,23 +284,8 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - - _zoomRectangleWorldSize = _renderer.getDataBounds().size(); - - const auto size = QSizeF(_renderer.getRenderSize().width(), _renderer.getRenderSize().height()); - const auto aspectRatio = size.width() / size.height(); - - if (aspectRatio < 1) { - _zoomRectangleWorldSize.setHeight(static_cast(_zoomRectangleWorldSize.width()) / aspectRatio); - } - else { - _zoomRectangleWorldSize.setWidth(static_cast(_zoomRectangleWorldSize.height()) * aspectRatio); - } - - const auto zoomRectangleWorldCenter = _renderer.getDataBounds().center(); - const auto zoomRectangleWorldOffset = QPointF(_zoomRectangleWorldSize.width() / 2.f, _zoomRectangleWorldSize.height() / 2.f); - - _zoomRectangleWorldTopLeft = zoomRectangleWorldCenter - zoomRectangleWorldOffset + QPoint(0, -_renderer.getDataBounds().height());//_renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + _zoomFactor = _renderer.getDataBounds().width() / _renderer.getRenderSize().width(); + _zoomCenterWorld = _renderer.getDataBounds().center(); setZoomRectangleWorld(getZoomRectangleWorld()); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index c8b09f7c8..b270ed41f 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -248,8 +248,8 @@ Q_OBJECT bool _isPanning; /** Panning flag */ bool _isZooming; /** Zooming flag */ float _zoomFactor; /** Zoom factor */ - QPointF _zoomRectangleWorldTopLeft; /** Zoom rectangle top-left in world coordinates */ - QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ + QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ + //QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ float _zoomRectangleMargin; /** Zoom rectangle margin */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ From 9626ca40e2e9652fb8273f139d8d783d6bb9ad9b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:28:10 +0100 Subject: [PATCH 17/89] Zoom around and panning is working nicely again --- ManiVault/src/renderers/Navigator2D.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index b489d28fb..ffa29d25e 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -156,7 +156,7 @@ QMatrix4x4 Navigator2D::getViewMatrix() const QRectF Navigator2D::getZoomRectangleWorld() const { - const auto zoomRectangleWorldSize = _renderer.getRenderSize().toSizeF() / _zoomFactor; + const auto zoomRectangleWorldSize = _renderer.getRenderSize().toSizeF() * _zoomFactor; const auto zoomRectangleWorldTopLeft = _zoomCenterWorld - QPointF(.5f * static_cast(zoomRectangleWorldSize.width()), .5f * static_cast(zoomRectangleWorldSize.height())); return { @@ -173,8 +173,8 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - _zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); - _zoomCenterWorld = zoomRectangleWorld.center(); + //_zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); + //_zoomCenterWorld = zoomRectangleWorld.center(); emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } @@ -220,8 +220,9 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); _zoomFactor /= factor; - _zoomCenterWorld = p1 + (getZoomRectangleWorld().center() - p1) * factor; + _zoomCenterWorld = p1 + (_zoomCenterWorld - p1) / factor; + qDebug() << "-----------" << _zoomFactor << _zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); } endChangeZoomRectangleWorld(); @@ -284,8 +285,10 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - _zoomFactor = _renderer.getDataBounds().width() / _renderer.getRenderSize().width(); - _zoomCenterWorld = _renderer.getDataBounds().center(); + _zoomFactor = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); + _zoomCenterWorld = _renderer.getDataBounds().center(); + + qDebug() << _renderer.getDataBounds() << _renderer.getRenderSize() << _zoomFactor << _zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); From 79f0025a87ffc9bab95079cc33eace30b18ffb15 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:40:04 +0100 Subject: [PATCH 18/89] Fixed zoom to extents --- ManiVault/src/renderers/Navigator2D.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index ffa29d25e..44c099079 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -222,7 +222,6 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) _zoomFactor /= factor; _zoomCenterWorld = p1 + (_zoomCenterWorld - p1) / factor; - qDebug() << "-----------" << _zoomFactor << _zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); } endChangeZoomRectangleWorld(); @@ -285,10 +284,11 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - _zoomFactor = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); - _zoomCenterWorld = _renderer.getDataBounds().center(); + const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - qDebug() << _renderer.getDataBounds() << _renderer.getRenderSize() << _zoomFactor << _zoomCenterWorld; + _zoomFactor = std::max(zoomFactorX, zoomFactorY); + _zoomCenterWorld = _renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height()); setZoomRectangleWorld(getZoomRectangleWorld()); From 06453e8be20af179c48493f92118f1c915f44c69 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:47:17 +0100 Subject: [PATCH 19/89] Work on margins --- ManiVault/src/renderers/Navigator2D.cpp | 6 +++--- ManiVault/src/renderers/Renderer2D.cpp | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 44c099079..38209f197 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -24,7 +24,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleMargin(0.f), + _zoomRectangleMargin(50.f), _userHasNavigated() { } @@ -145,7 +145,7 @@ QMatrix4x4 Navigator2D::getViewMatrix() const const auto factorY = static_cast(viewerSize.height()) / (zoomRectangle.isValid() ? static_cast(zoomRectangle.height()) : 1.0f); const auto scaleFactor = factorX < factorY ? factorX : factorY; - const auto d = 1.0f - (2 * _zoomRectangleMargin) / std::max(viewerSize.width(), viewerSize.height()); + const auto d = 1.0f - (2 * 0) / std::max(viewerSize.width(), viewerSize.height()); // Create scale matrix scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); @@ -287,7 +287,7 @@ void Navigator2D::resetView(bool force /*= true*/) const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY); + _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; _zoomCenterWorld = _renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height()); setZoomRectangleWorld(getZoomRectangleWorld()); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 16309d4ee..cd9eb4712 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -145,7 +145,6 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const // Create an orthogonal transformation matrix matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); - //matrix.ortho(-halfSize.width(), halfSize.width(), -getRenderSize().height(), 0, -1000.0f, +1000.0f); return matrix; } From f4b126b2b0bca025f545624bdad1904384189c6a Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 28 Mar 2025 11:13:38 +0100 Subject: [PATCH 20/89] Made some fixes in the view plugin sampler action --- .../src/actions/ViewPluginSamplerAction.cpp | 10 +- ManiVault/src/renderers/DensityRenderer.cpp | 269 +++++++++--------- ManiVault/src/renderers/DensityRenderer.h | 12 +- ManiVault/src/renderers/PointRenderer.cpp | 9 +- ManiVault/src/renderers/Renderer2D.cpp | 22 ++ ManiVault/src/renderers/Renderer2D.h | 33 ++- 6 files changed, 192 insertions(+), 163 deletions(-) diff --git a/ManiVault/src/actions/ViewPluginSamplerAction.cpp b/ManiVault/src/actions/ViewPluginSamplerAction.cpp index 983e06c92..606c84e84 100644 --- a/ManiVault/src/actions/ViewPluginSamplerAction.cpp +++ b/ManiVault/src/actions/ViewPluginSamplerAction.cpp @@ -442,20 +442,26 @@ bool ViewPluginSamplerAction::eventFilter(QObject* target, QEvent* event) case QEvent::MouseButtonPress: { - _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(false); + _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(false); + _toolTipLabel.hide(); break; } case QEvent::MouseButtonRelease: case QEvent::Enter: { - _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(getSamplingMode() == SamplingMode::FocusRegion && getEnabledAction().isChecked() && canView()); + const auto enabled = getSamplingMode() == SamplingMode::FocusRegion && getEnabledAction().isChecked() && canView(); + + _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(enabled); + _toolTipLabel.setVisible(enabled); + break; } case QEvent::Leave: { _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(false); + _toolTipLabel.hide(); break; } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 1c871fca6..4ae25c47b 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -4,177 +4,166 @@ #include "DensityRenderer.h" -namespace mv +namespace mv::gui { - namespace gui - { - - DensityRenderer::DensityRenderer(RenderMode renderMode) : - _renderMode(renderMode) - { - getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); - } - - DensityRenderer::~DensityRenderer() - { - // Delete objects - _densityComputation.cleanup(); - } - - void DensityRenderer::setRenderMode(RenderMode renderMode) - { - _renderMode = renderMode; - } - - // Points need to be passed as a pointer as we need to store them locally in order - // to be able to recompute the densities when parameters change. - void DensityRenderer::setData(const std::vector* points) - { - _densityComputation.setData(points); - } - - void DensityRenderer::setWeights(const std::vector* weights) - { - _densityComputation.setWeights(weights); - } - void DensityRenderer::setBounds(const Bounds& bounds) - { - _densityComputation.setBounds(bounds.getLeft(), bounds.getRight(), bounds.getBottom(), bounds.getTop()); - } +DensityRenderer::DensityRenderer(RenderMode renderMode) : + _renderMode(renderMode) +{ + getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); +} - void DensityRenderer::setSigma(const float sigma) - { - _densityComputation.setSigma(sigma); - } +DensityRenderer::~DensityRenderer() +{ + // Delete objects + _densityComputation.cleanup(); +} - void DensityRenderer::computeDensity() - { - _densityComputation.compute(); - } +void DensityRenderer::setRenderMode(RenderMode renderMode) +{ + _renderMode = renderMode; +} - float DensityRenderer::getMaxDensity() const - { - return _densityComputation.getMaxDensity(); - } +// Points need to be passed as a pointer as we need to store them locally in order +// to be able to recompute the densities when parameters change. +void DensityRenderer::setData(const std::vector* points) +{ + _densityComputation.setData(points); +} - mv::Vector3f DensityRenderer::getColorMapRange() const - { - return Vector3f(0.0f, _densityComputation.getMaxDensity(), _densityComputation.getMaxDensity()); - } +void DensityRenderer::setWeights(const std::vector* weights) +{ + _densityComputation.setWeights(weights); +} - void DensityRenderer::setColormap(const QImage& image) - { - _colormap.loadFromImage(image); - _hasColorMap = true; - } +void DensityRenderer::setSigma(const float sigma) +{ + _densityComputation.setSigma(sigma); +} - void DensityRenderer::init() - { - initializeOpenGLFunctions(); +void DensityRenderer::computeDensity() +{ + _densityComputation.compute(); +} - // Create a simple VAO for full-screen quad rendering - glGenVertexArrays(1, &_quad); +float DensityRenderer::getMaxDensity() const +{ + return _densityComputation.getMaxDensity(); +} - // Load the necessary shaders for density drawing - bool loaded = true; - loaded &= _shaderDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/DensityDraw.frag"); - loaded &= _shaderIsoDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/IsoDensityDraw.frag"); - if (!loaded) { - qDebug() << "Failed to load one of the Density shaders"; - } +mv::Vector3f DensityRenderer::getColorMapRange() const +{ + return Vector3f(0.0f, _densityComputation.getMaxDensity(), _densityComputation.getMaxDensity()); +} - // Initialize the density computation - _densityComputation.init(QOpenGLContext::currentContext()); - } +void DensityRenderer::setColormap(const QImage& image) +{ + _colormap.loadFromImage(image); + _hasColorMap = true; +} - void DensityRenderer::render() - { - beginRender(); - { - switch (_renderMode) { - case DENSITY: { - drawDensity(); - break; - } - - case LANDSCAPE: { - drawLandscape(); - break; - } - } - } - endRender(); - } +void DensityRenderer::init() +{ + initializeOpenGLFunctions(); - void DensityRenderer::destroy() - { - _shaderDensityDraw.destroy(); - _shaderIsoDensityDraw.destroy(); - _densityComputation.cleanup(); - _colormap.destroy(); + // Create a simple VAO for full-screen quad rendering + glGenVertexArrays(1, &_quad); - glDeleteVertexArrays(1, &_quad); - } + // Load the necessary shaders for density drawing + bool loaded = true; + loaded &= _shaderDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/DensityDraw.frag"); + loaded &= _shaderIsoDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/IsoDensityDraw.frag"); + if (!loaded) { + qDebug() << "Failed to load one of the Density shaders"; + } - void DensityRenderer::drawFullscreenQuad() - { - glBindVertexArray(_quad); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); - } + // Initialize the density computation + _densityComputation.init(QOpenGLContext::currentContext()); +} - void DensityRenderer::drawDensity() - { - float maxDensity = _densityComputation.getMaxDensity(); - if (maxDensity <= 0) { return; } +void DensityRenderer::render() +{ + beginRender(); + { + switch (_renderMode) { + case DENSITY: { + drawDensity(); + break; + } + + case LANDSCAPE: { + drawLandscape(); + break; + } + } + } + endRender(); +} + +void DensityRenderer::destroy() +{ + _shaderDensityDraw.destroy(); + _shaderIsoDensityDraw.destroy(); + _densityComputation.cleanup(); + _colormap.destroy(); - _shaderDensityDraw.bind(); + glDeleteVertexArrays(1, &_quad); +} - _densityComputation.getDensityTexture().bind(0); +void DensityRenderer::drawFullscreenQuad() +{ + glBindVertexArray(_quad); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); +} - QMatrix4x4 modelMatrix; +void DensityRenderer::drawDensity() +{ + const auto maxDensity = _densityComputation.getMaxDensity(); - modelMatrix.setToIdentity(); + if (maxDensity <= 0) { + return; + } - const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; + _shaderDensityDraw.bind(); - qDebug() << mvp; + _densityComputation.getDensityTexture().bind(0); - _shaderDensityDraw.uniformMatrix4f("mvp", mvp.data()); - _shaderDensityDraw.uniform1i("tex", 0); - _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); + _shaderDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); + _shaderDensityDraw.uniform1i("tex", 0); + _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); - drawFullscreenQuad(); - } + drawFullscreenQuad(); +} - void DensityRenderer::drawLandscape() - { - if (!_hasColorMap) - return; +void DensityRenderer::drawLandscape() +{ + if (!_hasColorMap) + return; - float maxDensity = _densityComputation.getMaxDensity(); - if (maxDensity <= 0) { return; } + const auto maxDensity = _densityComputation.getMaxDensity(); - _shaderIsoDensityDraw.bind(); + if (maxDensity <= 0) { + return; + } - _densityComputation.getDensityTexture().bind(0); - _shaderIsoDensityDraw.uniform1i("tex", 0); + _shaderIsoDensityDraw.bind(); - _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); - _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); + _densityComputation.getDensityTexture().bind(0); + _shaderIsoDensityDraw.uniform1i("tex", 0); - _colormap.bind(1); - _shaderIsoDensityDraw.uniform1i("colormap", 1); + _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); + _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); - drawFullscreenQuad(); - } + _colormap.bind(1); + _shaderIsoDensityDraw.uniform1i("colormap", 1); - void DensityRenderer::setColorMapRange(const float& min, const float& max) - { - _colorMapRange = Vector3f(min, max, max - min); - } + drawFullscreenQuad(); +} - } // namespace gui +void DensityRenderer::setColorMapRange(const float& min, const float& max) +{ + _colorMapRange = Vector3f(min, max, max - min); +} -} // namespace mv +} diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index f2568a85f..2d3e16f37 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -6,7 +6,6 @@ #include "Renderer2D.h" -#include "graphics/Bounds.h" #include "graphics/Shader.h" #include "graphics/Texture.h" #include "graphics/Vector2f.h" @@ -14,12 +13,8 @@ #include "util/MeanShift.h" -#include - -namespace mv +namespace mv::gui { - namespace gui - { class CORE_EXPORT DensityRenderer : public Renderer2D { @@ -35,7 +30,6 @@ namespace mv void setRenderMode(RenderMode renderMode); void setData(const std::vector* data); void setWeights(const std::vector* weights); - void setBounds(const Bounds& bounds); void setSigma(const float sigma); void computeDensity(); float getMaxDensity() const; @@ -78,6 +72,4 @@ namespace mv GLuint _quad = 0; }; - } // namespace gui - -} // namespace mv +} diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index f13305bb0..1f15a5ea7 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -431,18 +431,13 @@ namespace mv beginRender(); { _shader.bind(); - - QMatrix4x4 modelMatrix; - - modelMatrix.setToIdentity(); - - const auto mvp = QMatrix4x4(getProjectionMatrix())* getNavigator().getViewMatrix()* modelMatrix; + const bool pointSizeAbsolute = _pointSettings._scalingMode == PointScaling::Absolute; _shader.uniform1f("pointSize", _pointSettings._pointSize); _shader.uniform1i("pointSizeAbsolute", pointSizeAbsolute); _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); - _shader.uniformMatrix4f("mvp", mvp.data()); + _shader.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); _shader.uniform4f("dataBounds", getDataBounds().left(), getDataBounds().right(), getDataBounds().bottom(), getDataBounds().top()); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index cd9eb4712..50ae9e190 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -46,6 +46,8 @@ void Renderer2D::beginRender() #endif glViewport(0, 0, _renderSize.width(), _renderSize.height()); + + updateModelViewProjectionMatrix(); } void Renderer2D::endRender() @@ -77,6 +79,11 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) getNavigator().resetView(); } +void Renderer2D::updateModelViewProjectionMatrix() +{ + _modelViewProjectionMatrix = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * _modelMatrix; +} + QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const { return QVector3D(screenPoint.x(), getRenderSize().height() - screenPoint.y(), 0).unproject(modelViewMatrix, getProjectionMatrix(), QRect(0, 0, getRenderSize().width(), getRenderSize().height())); @@ -161,4 +168,19 @@ QRect Renderer2D::getScreenRectangleFromWorldRectangle(const QRectF& worldBoundi }; } +QMatrix4x4 Renderer2D::getModelMatrix() const +{ + return _modelMatrix; +} + +void Renderer2D::setModelMatrix(const QMatrix4x4& modelMatrix) +{ + _modelMatrix = modelMatrix; +} + +QMatrix4x4 Renderer2D::getModelViewProjectionMatrix() const +{ + return _modelViewProjectionMatrix; +} + } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 061cf506f..ae76f98bd 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -109,9 +109,27 @@ Q_OBJECT */ QRect getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const; + /** + * Get model matrix + * @return Model matrix + */ + QMatrix4x4 getModelMatrix() const; + + /** + * Set model matrix to \p modelMatrix + * @param modelMatrix Model matrix + */ + void setModelMatrix(const QMatrix4x4& modelMatrix); + + /** + * Get model-view-projection matrix + * @return Model-view-projection matrix + */ + QMatrix4x4 getModelViewProjectionMatrix() const; + protected: - /** Begin rendering */ + /** Begin rendering (sets up the OpenGL viewport and computes the model-view-projection matrix) */ void beginRender() override; /** End rendering */ @@ -132,9 +150,16 @@ Q_OBJECT void setDataBounds(const QRectF& dataBounds); private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ - QRectF _dataBounds; /** Bounds of the data */ + + /** Update the model-view-projection matrix */ + void updateModelViewProjectionMatrix(); + +private: + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + QRectF _dataBounds; /** Bounds of the data */ + QMatrix4x4 _modelMatrix; + QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ friend class Navigator2D; }; From 6e729cf25cc1b9eb9ea991ef5bcf2bcec8179020 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 31 Mar 2025 13:32:12 +0200 Subject: [PATCH 21/89] Work on density renderer --- ManiVault/res/shaders/DensityDraw.frag | 13 +- ManiVault/res/shaders/IsoDensityDraw.frag | 12 +- ManiVault/res/shaders/Quad.vert | 10 +- ManiVault/src/renderers/DensityRenderer.cpp | 130 +++++-- ManiVault/src/renderers/DensityRenderer.h | 123 ++++--- ManiVault/src/renderers/Navigator2D.cpp | 64 +++- ManiVault/src/renderers/Navigator2D.h | 360 ++++++++++---------- ManiVault/src/renderers/Renderer2D.h | 3 +- 8 files changed, 443 insertions(+), 272 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index 30e8c189d..34a8f1b7b 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -7,14 +7,13 @@ uniform sampler2D tex; uniform float norm; -in vec2 pass_texCoord; +in vec2 outUv; out vec4 fragColor; void main() { - float f = 1 - (texture(tex, pass_texCoord).r * norm); - fragColor = vec4(vec3(f), 1); - - if (pass_texCoord.x < 0.1 || pass_texCoord.x > 0.9) - fragColor = vec4(1,0,0,1); -} + float f = 1 - (texture(tex, outUv).r * norm); + fragColor = vec4(vec3(f, 0, 1), 1); + //fragColor = vec4(outUv.y, 0, 0, 1); + //fragColor = vec4(1, 0, 0, 1); +} \ No newline at end of file diff --git a/ManiVault/res/shaders/IsoDensityDraw.frag b/ManiVault/res/shaders/IsoDensityDraw.frag index a34e3c3e2..8f0650894 100644 --- a/ManiVault/res/shaders/IsoDensityDraw.frag +++ b/ManiVault/res/shaders/IsoDensityDraw.frag @@ -10,7 +10,7 @@ uniform sampler2D densityMap; uniform vec2 renderParams; uniform vec3 colorMapRange; -in vec2 pass_texCoord; +in vec2 uv; out vec4 fragColor; @@ -22,7 +22,7 @@ float getNormalizedDensity(vec2 uv) } void main() { - float density = getNormalizedDensity(pass_texCoord); + float density = getNormalizedDensity(uv); if (density < renderParams.y) discard; @@ -40,10 +40,10 @@ void main() { // Central differences to find out if we draw the iso contour instead of the color vec4 neighborDensities; - neighborDensities.x = getNormalizedDensity(pass_texCoord + texelSize.xz); - neighborDensities.y = getNormalizedDensity(pass_texCoord - texelSize.xz); - neighborDensities.z = getNormalizedDensity(pass_texCoord + texelSize.zy); - neighborDensities.w = getNormalizedDensity(pass_texCoord - texelSize.zy); + neighborDensities.x = getNormalizedDensity(uv + texelSize.xz); + neighborDensities.y = getNormalizedDensity(uv - texelSize.xz); + neighborDensities.z = getNormalizedDensity(uv + texelSize.zy); + neighborDensities.w = getNormalizedDensity(uv - texelSize.zy); ivec4 stepId = min(ivec4(floor(neighborDensities * vec4(numSteps+1))), ivec4(numSteps)); isBoundary = (any(notEqual(stepId.xxx, stepId.yzw))); diff --git a/ManiVault/res/shaders/Quad.vert b/ManiVault/res/shaders/Quad.vert index 14cbb55e2..80d4b7ea3 100644 --- a/ManiVault/res/shaders/Quad.vert +++ b/ManiVault/res/shaders/Quad.vert @@ -4,11 +4,15 @@ #version 330 core +layout(location = 0) in vec2 vertex; +layout(location = 1) in vec2 uv; + uniform mat4 mvp; -out vec2 pass_texCoord; +out vec2 outUv; void main() { - pass_texCoord = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2); - gl_Position = mvp * vec4(pass_texCoord * 2 - 1, 0, 1); + gl_Position = mvp * vec4(vertex, 0, 1); + + outUv = (vertex / vec2(128.f, 128.f)); } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 4ae25c47b..55d8e0a2a 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -4,6 +4,12 @@ #include "DensityRenderer.h" +#ifdef _DEBUG + //#define DENSITY_RENDERER_VERBOSE +#endif + +#define DENSITY_RENDERER_VERBOSE + namespace mv::gui { @@ -19,6 +25,24 @@ DensityRenderer::~DensityRenderer() _densityComputation.cleanup(); } +void DensityRenderer::resize(QSize renderSize) +{ + Renderer2D::resize(renderSize); + + updateQuad(); +} + +void DensityRenderer::setDataBounds(const QRectF& dataBounds) +{ + Renderer2D::setDataBounds(dataBounds); + + qDebug() << "DensityRenderer::setDataBounds()" << dataBounds; + _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); + //_densityComputation.setBounds(-100, -100, -100, 100); + + updateQuad(); +} + void DensityRenderer::setRenderMode(RenderMode renderMode) { _renderMode = renderMode; @@ -53,7 +77,11 @@ float DensityRenderer::getMaxDensity() const mv::Vector3f DensityRenderer::getColorMapRange() const { - return Vector3f(0.0f, _densityComputation.getMaxDensity(), _densityComputation.getMaxDensity()); + return { + 0.0f, + _densityComputation.getMaxDensity(), + _densityComputation.getMaxDensity() + }; } void DensityRenderer::setColormap(const QImage& image) @@ -66,14 +94,14 @@ void DensityRenderer::init() { initializeOpenGLFunctions(); - // Create a simple VAO for full-screen quad rendering - glGenVertexArrays(1, &_quad); + updateQuad(); // Load the necessary shaders for density drawing bool loaded = true; loaded &= _shaderDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/DensityDraw.frag"); loaded &= _shaderIsoDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/IsoDensityDraw.frag"); - if (!loaded) { + + if (!loaded) { qDebug() << "Failed to load one of the Density shaders"; } @@ -83,6 +111,7 @@ void DensityRenderer::init() void DensityRenderer::render() { + qDebug() << "DensityRenderer::render()" << _renderMode; beginRender(); { switch (_renderMode) { @@ -107,33 +136,84 @@ void DensityRenderer::destroy() _densityComputation.cleanup(); _colormap.destroy(); - glDeleteVertexArrays(1, &_quad); + glDeleteVertexArrays(1, &_VAO); + glDeleteVertexArrays(1, &_VBO); + glDeleteVertexArrays(1, &_EBO); } -void DensityRenderer::drawFullscreenQuad() +void DensityRenderer::updateQuad() { - glBindVertexArray(_quad); - glDrawArrays(GL_TRIANGLES, 0, 3); + const float width = 128.f;//static_cast(getDataBounds().width()); + const float height = 128.f;//static_cast(getDataBounds().height()); + + float vertices[] = { + 0.f, 0.f, 0.0f, 0.0f, + width, 0.f, 1.0f, 0.0f, + width, height, 1.0f, 1.0f, + 0.f, height, 0.0f, 1.0f + }; + + unsigned int indices[] = { + 0, 1, 2, + 2, 3, 0 + }; + + glGenVertexArrays(1, &_VAO); + glGenBuffers(1, &_VBO); + glGenBuffers(1, &_EBO); + + glBindVertexArray(_VAO); + + glBindBuffer(GL_ARRAY_BUFFER, _VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + // Position attribute + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // Texture coordinates attribute + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + glEnableVertexAttribArray(1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void DensityRenderer::drawQuad() +{ + glBindVertexArray(_VAO); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); } void DensityRenderer::drawDensity() { +#ifdef DENSITY_RENDERER_VERBOSE + qDebug() << __FUNCTION__; +#endif + const auto maxDensity = _densityComputation.getMaxDensity(); if (maxDensity <= 0) { return; } - _shaderDensityDraw.bind(); + qDebug() << "DensityRenderer::drawDensity()" << maxDensity; - _densityComputation.getDensityTexture().bind(0); + _shaderDensityDraw.bind(); + { + _densityComputation.getDensityTexture().bind(0); - _shaderDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); - _shaderDensityDraw.uniform1i("tex", 0); - _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); + _shaderDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); + _shaderDensityDraw.uniform1i("tex", 0); + _shaderDensityDraw.uniform1f("norm", 1.0f / maxDensity); - drawFullscreenQuad(); + drawQuad(); + } + //_shaderDensityDraw.release(); } void DensityRenderer::drawLandscape() @@ -141,6 +221,10 @@ void DensityRenderer::drawLandscape() if (!_hasColorMap) return; +#ifdef DENSITY_RENDERER_VERBOSE + qDebug() << __FUNCTION__; +#endif + const auto maxDensity = _densityComputation.getMaxDensity(); if (maxDensity <= 0) { @@ -148,17 +232,21 @@ void DensityRenderer::drawLandscape() } _shaderIsoDensityDraw.bind(); + { + _densityComputation.getDensityTexture().bind(0); - _densityComputation.getDensityTexture().bind(0); - _shaderIsoDensityDraw.uniform1i("tex", 0); + _shaderIsoDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); + _shaderIsoDensityDraw.uniform1i("tex", 0); + _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); + _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); - _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); - _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); + _colormap.bind(1); - _colormap.bind(1); - _shaderIsoDensityDraw.uniform1i("colormap", 1); + _shaderIsoDensityDraw.uniform1i("colormap", 1); - drawFullscreenQuad(); + drawQuad(); + } + _shaderIsoDensityDraw.release(); } void DensityRenderer::setColorMapRange(const float& min, const float& max) diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 2d3e16f37..09f4c9e51 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -16,60 +16,75 @@ namespace mv::gui { - class CORE_EXPORT DensityRenderer : public Renderer2D - { - - public: - enum RenderMode { - DENSITY, LANDSCAPE - }; - - DensityRenderer(RenderMode renderMode); - ~DensityRenderer() override; - - void setRenderMode(RenderMode renderMode); - void setData(const std::vector* data); - void setWeights(const std::vector* weights); - void setSigma(const float sigma); - void computeDensity(); - float getMaxDensity() const; - Vector3f getColorMapRange() const; - - /** - * Loads a colormap from an image and loads as - *the current colormap for the landscape view. - * @param image Color map image - */ - void setColormap(const QImage& image); - - void init() override; - - void render() override; - - void destroy() override; - - void setColorMapRange(const float& min, const float& max); - - private: - void drawDensity(); - void drawLandscape(); - - void drawFullscreenQuad(); - - private: - bool _isSelecting = false; - bool _hasColorMap = false; - - ShaderProgram _shaderDensityDraw; - ShaderProgram _shaderIsoDensityDraw; - DensityComputation _densityComputation; - Texture2D _colormap; - - Vector3f _colorMapRange; - - RenderMode _renderMode; +class CORE_EXPORT DensityRenderer : public Renderer2D +{ - GLuint _quad = 0; - }; +public: + enum RenderMode { + DENSITY, LANDSCAPE + }; + + DensityRenderer(RenderMode renderMode); + ~DensityRenderer() override; + + /** + * Resize the renderer to \p renderSize + * @param renderSize New size of the renderer + */ + void resize(QSize renderSize) override; + + /** + * Set data bounds to \p dataBounds + * @param dataBounds Data bounds + */ + void setDataBounds(const QRectF& dataBounds) override; + + void setRenderMode(RenderMode renderMode); + void setData(const std::vector* data); + void setWeights(const std::vector* weights); + void setSigma(const float sigma); + void computeDensity(); + float getMaxDensity() const; + Vector3f getColorMapRange() const; + + /** + * Loads a colormap from an image and loads as + *the current colormap for the landscape view. + * @param image Color map image + */ + void setColormap(const QImage& image); + + void init() override; + + void render() override; + + void destroy() override; + + void setColorMapRange(const float& min, const float& max); + +private: + void drawDensity(); + void drawLandscape(); + + void updateQuad(); + void drawQuad(); + +private: + bool _isSelecting = false; + bool _hasColorMap = false; + + ShaderProgram _shaderDensityDraw; + ShaderProgram _shaderIsoDensityDraw; + DensityComputation _densityComputation; + Texture2D _colormap; + + Vector3f _colorMapRange; + + RenderMode _renderMode; + + GLuint _VAO = 0; + GLuint _VBO = 0; + GLuint _EBO = 0; +}; } diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 38209f197..277dcc5a1 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -39,6 +39,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); + connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); + + _updateNavigationTimer.start(16); + _initialized = true; } } @@ -220,9 +224,8 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); _zoomFactor /= factor; - _zoomCenterWorld = p1 + (_zoomCenterWorld - p1) / factor; - setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomCenterWorld(p1 + (_zoomCenterWorld - p1) / factor); } endChangeZoomRectangleWorld(); } @@ -264,13 +267,32 @@ void Navigator2D::panBy(const QPointF& delta) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomCenterWorld = getZoomRectangleWorld().center() + (p2 - p1); + setZoomCenterWorld(getZoomRectangleWorld().center() + (p2 - p1)); } endChangeZoomRectangleWorld(); } endPanning(); } +void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) +{ + if (zoomCenterWorld == _zoomCenterWorld) + return; + + _zoomCenterWorld = zoomCenterWorld; + + setZoomRectangleWorld(getZoomRectangleWorld()); + + //if (std::isnan(zoomCenterWorld.x()) || std::isnan(zoomCenterWorld.y())) + // return; + + //_zoomCenterHistory.push_front(zoomCenterWorld); + + //if (_zoomCenterHistory.size() > Navigator2D::maxZoomHistorySize) { + // _zoomCenterHistory.pop_back(); + //} +} + void Navigator2D::resetView(bool force /*= true*/) { if (!_initialized) @@ -287,10 +309,11 @@ void Navigator2D::resetView(bool force /*= true*/) const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - _zoomCenterWorld = _renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height()); + _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; + + setZoomCenterWorld(_renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height())); - setZoomRectangleWorld(getZoomRectangleWorld()); + // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); @@ -460,4 +483,33 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } +void Navigator2D::updateNavigation() +{ + + //QPointF smoothedZoomCenterWorld{}; + + //if (!_zoomCenterHistory.empty()) { + // QPointF sum(0, 0); + + // for (const auto& zoomCenterWorld : _zoomCenterHistory) { + // sum += zoomCenterWorld; + // } + + // smoothedZoomCenterWorld = sum / static_cast(_zoomCenterHistory.size()); + + // _zoomCenterWorld += (smoothedZoomCenterWorld - _zoomCenterWorld); + + // if (!_zoomCenterHistory.empty()) { + // _zoomCenterHistory.pop_back(); + // } + //} + + //qDebug() << __FUNCTION__ << _zoomCenterWorld; + + + + + //setZoomRectangleWorld(getZoomRectangleWorld()); +} + } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index b270ed41f..e38e14917 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -5,7 +5,7 @@ #pragma once #include -#include +#include #include namespace mv @@ -22,237 +22,251 @@ class Renderer2D; */ class CORE_EXPORT Navigator2D : public QObject { - -Q_OBJECT + Q_OBJECT public: - /** - * Construct a new two-dimensional navigator - * - * @param renderer Reference to parent renderer - * @param parent Pointer to the parent object - */ - explicit Navigator2D(Renderer2D& renderer, QObject* parent = nullptr); + /** + * Construct a new two-dimensional navigator + * + * @param renderer Reference to parent renderer + * @param parent Pointer to the parent object + */ + explicit Navigator2D(Renderer2D& renderer, QObject* parent = nullptr); - /** - * Initializes the two-dimensional navigator with a \p sourceWidget - * @param sourceWidget Pointer to the renderer widget - */ - void initialize(QWidget* sourceWidget); + /** + * Initializes the two-dimensional navigator with a \p sourceWidget + * @param sourceWidget Pointer to the renderer widget + */ + void initialize(QWidget* sourceWidget); - /** - * Watch \p watched for events - * - * @param watched Watched object - * @param event Event - * @return True if the event was handled, false otherwise - */ - bool eventFilter(QObject* watched, QEvent* event) override; + /** + * Watch \p watched for events + * + * @param watched Watched object + * @param event Event + * @return True if the event was handled, false otherwise + */ + bool eventFilter(QObject* watched, QEvent* event) override; - /** - * Get the view matrix - * @return View matrix - */ - QMatrix4x4 getViewMatrix() const; + /** + * Get the view matrix + * @return View matrix + */ + QMatrix4x4 getViewMatrix() const; - /** - * Get the world zoom rectangle - * @return Zoom rectangle in world coordinates - */ - QRectF getZoomRectangleWorld() const; + /** + * Get the world zoom rectangle + * @return Zoom rectangle in world coordinates + */ + QRectF getZoomRectangleWorld() const; - /** - * Set the world zoom rectangle to \p zoomRectangleWorld - * @param zoomRectangleWorld Zoom rectangle in world coordinates - */ - void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); + /** + * Set the world zoom rectangle to \p zoomRectangleWorld + * @param zoomRectangleWorld Zoom rectangle in world coordinates + */ + void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); - /** - * Get the zoom rectangle margin - * @return Zoom rectangle margin - */ - float getZoomRectangleMargin() const; + /** + * Get the zoom rectangle margin + * @return Zoom rectangle margin + */ + float getZoomRectangleMargin() const; /** - * Get the zoom factor + * Get the zoom factor * @return Zoom factor */ float getZoomFactor() const; - /** - * Get whether the navigator is enabled - * @return Boolean determining whether the navigator is enabled - */ - bool isEnabled() const; + /** + * Get whether the navigator is enabled + * @return Boolean determining whether the navigator is enabled + */ + bool isEnabled() const; - /** - * Set enabled to \p enabled - * @param enabled Boolean determining whether the navigator is enabled - */ - void setEnabled(bool enabled); + /** + * Set enabled to \p enabled + * @param enabled Boolean determining whether the navigator is enabled + */ + void setEnabled(bool enabled); public: // Navigation - /** - * Zoom by \p factor around \p center - * @param center Point to zoom around - * @param factor Zoom factor - */ - void zoomAround(const QPoint& center, float factor); + /** + * Zoom by \p factor around \p center + * @param center Point to zoom around + * @param factor Zoom factor + */ + void zoomAround(const QPoint& center, float factor); - /** - * Zoom to \p zoomRectangle - * @param zoomRectangle Zoom to this rectangle - */ - void zoomToRectangle(const QRectF& zoomRectangle); + /** + * Zoom to \p zoomRectangle + * @param zoomRectangle Zoom to this rectangle + */ + void zoomToRectangle(const QRectF& zoomRectangle); - /** - * Pan by \p delta - * @param delta Pan by this amount - */ - void panBy(const QPointF& delta); + /** + * Pan by \p delta + * @param delta Pan by this amount + */ + void panBy(const QPointF& delta); /** - * Reset the view - * @param force Force reset event when the user has navigated + * Set the zoom center in world coordinates to \p zoomCenterWorld + * @param zoomCenterWorld Zoom center in world coordinates */ - void resetView(bool force = true); + void setZoomCenterWorld(const QPointF& zoomCenterWorld); - /** - * Get whether the renderer is panning - * @return Boolean determining whether the renderer is panning - */ - bool isPanning() const; + /** + * Reset the view + * @param force Force reset event when the user has navigated + */ + void resetView(bool force = true); - /** - * Get whether the renderer is zooming - * @return Boolean determining whether the renderer is zooming - */ - bool isZooming() const; + /** + * Get whether the renderer is panning + * @return Boolean determining whether the renderer is panning + */ + bool isPanning() const; - /** - * Get whether the renderer is navigating - * @return Boolean determining whether the renderer is navigating - */ - bool isNavigating() const; + /** + * Get whether the renderer is zooming + * @return Boolean determining whether the renderer is zooming + */ + bool isZooming() const; - /** - * Get whether the user has navigated - * @return Boolean determining whether the user has navigated - */ - bool hasUserNavigated() const; + /** + * Get whether the renderer is navigating + * @return Boolean determining whether the renderer is navigating + */ + bool isNavigating() const; + + /** + * Get whether the user has navigated + * @return Boolean determining whether the user has navigated + */ + bool hasUserNavigated() const; protected: // Navigation - /** - * Set whether the renderer is panning to \p isPanning - * @param isPanning Boolean determining whether the renderer is panning - */ - void setIsPanning(bool isPanning); + /** + * Set whether the renderer is panning to \p isPanning + * @param isPanning Boolean determining whether the renderer is panning + */ + void setIsPanning(bool isPanning); - /** - * Set whether the renderer is zooming to \p isZooming - * @param isZooming Boolean determining whether the renderer is zooming - */ - void setIsZooming(bool isZooming); + /** + * Set whether the renderer is zooming to \p isZooming + * @param isZooming Boolean determining whether the renderer is zooming + */ + void setIsZooming(bool isZooming); - /** - * Set whether the renderer is navigating to \p isNavigating - * @param isNavigating Boolean determining whether the renderer is navigating - */ - void setIsNavigating(bool isNavigating); + /** + * Set whether the renderer is navigating to \p isNavigating + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void setIsNavigating(bool isNavigating); + + /** Panning has begun */ + void beginPanning(); - /** Panning has begun */ - void beginPanning(); + /** Panning has ended */ + void endPanning(); - /** Panning has ended */ - void endPanning(); + /** Zooming has begun */ + void beginZooming(); - /** Zooming has begun */ - void beginZooming(); + /** Zooming has ended */ + void endZooming(); - /** Zooming has ended */ - void endZooming(); + /** Navigation has begun */ + void beginNavigation(); - /** Navigation has begun */ - void beginNavigation(); + /** Navigation has ended */ + void endNavigation(); - /** Navigation has ended */ - void endNavigation(); + /** Begin changing the zoom rectangle in world coordinates */ + void beginChangeZoomRectangleWorld(); - /** Begin changing the zoom rectangle in world coordinates */ - void beginChangeZoomRectangleWorld(); + /** End changing the zoom rectangle in world coordinates */ + void endChangeZoomRectangleWorld(); - /** End changing the zoom rectangle in world coordinates */ - void endChangeZoomRectangleWorld(); +private: + + /** Smoothes the navigation over time */ + void updateNavigation(); signals: - /** Signals that panning has started */ - void panningStarted(); + /** Signals that panning has started */ + void panningStarted(); - /** Signals that panning has ended */ - void panningEnded(); + /** Signals that panning has ended */ + void panningEnded(); - /** - * Signals that is panning changed to \p isPanning - * @param isPanning - */ - void isPanningChanged(bool isPanning); + /** + * Signals that is panning changed to \p isPanning + * @param isPanning + */ + void isPanningChanged(bool isPanning); - /** Signals that zooming has started */ - void zoomingStarted(); + /** Signals that zooming has started */ + void zoomingStarted(); - /** Signals that zooming has ended */ - void zoomingEnded(); + /** Signals that zooming has ended */ + void zoomingEnded(); - /** - * Signals that is zooming changed to \p isZooming - * @param isZooming Boolean determining whether the renderer is zooming - */ - void isZoomingChanged(bool isZooming); + /** + * Signals that is zooming changed to \p isZooming + * @param isZooming Boolean determining whether the renderer is zooming + */ + void isZoomingChanged(bool isZooming); - /** Signals that navigation has started */ - void navigationStarted(); + /** Signals that navigation has started */ + void navigationStarted(); - /** Signals that navigation has ended */ - void navigationEnded(); + /** Signals that navigation has ended */ + void navigationEnded(); - /** - * Signals that is navigating changed to \p isNavigating - * @param isNavigating Boolean determining whether the renderer is navigating - */ - void isNavigatingChanged(bool isNavigating); + /** + * Signals that is navigating changed to \p isNavigating + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void isNavigatingChanged(bool isNavigating); /** - * Signals that enabled changed to \p enabled - * @param enabled Boolean determining whether the navigator is enabled + * Signals that enabled changed to \p enabled + * @param enabled Boolean determining whether the navigator is enabled */ void enabledChanged(bool enabled); - /** - * Signals that the zoom rectangle in world coordinates has changed from \p previousZoomRectangleWorld to \p currentZoomRectangleWorld - * @param previousZoomRectangleWorld Previous world zoom rectangle - * @param currentZoomRectangleWorld Current world zoom rectangle - */ - void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); + /** + * Signals that the zoom rectangle in world coordinates has changed from \p previousZoomRectangleWorld to \p currentZoomRectangleWorld + * @param previousZoomRectangleWorld Previous world zoom rectangle + * @param currentZoomRectangleWorld Current world zoom rectangle + */ + void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _enabled; /** Enabled flag */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ - //QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ - QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ - bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ + QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + QTimer _updateNavigationTimer; /** Timer for updating the navigation */ + QList _zoomCenterHistory; /** History of zoom centers */ + QList _zoomFactorHistory; /** History of zoom factors */ + + static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ }; } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index ae76f98bd..49dfe1a44 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -38,7 +38,6 @@ Q_OBJECT /** * Resize the renderer to \p renderSize - * * @param renderSize New size of the renderer */ void resize(QSize renderSize) override; @@ -147,7 +146,7 @@ Q_OBJECT * Set data bounds to \p dataBounds * @param dataBounds Data bounds */ - void setDataBounds(const QRectF& dataBounds); + virtual void setDataBounds(const QRectF& dataBounds); private: From b3aa704a890b61731351dc9407c3fa2580a9b899 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 31 Mar 2025 14:28:36 +0200 Subject: [PATCH 22/89] Work on density renderer --- ManiVault/res/shaders/DensityDraw.frag | 4 +--- ManiVault/src/renderers/DensityRenderer.cpp | 9 ++++----- ManiVault/src/util/DensityComputation.h | 3 +++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index 34a8f1b7b..d1b6db1be 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -13,7 +13,5 @@ out vec4 fragColor; void main() { float f = 1 - (texture(tex, outUv).r * norm); - fragColor = vec4(vec3(f, 0, 1), 1); - //fragColor = vec4(outUv.y, 0, 0, 1); - //fragColor = vec4(1, 0, 0, 1); + fragColor = vec4(vec3(f), 1); } \ No newline at end of file diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 55d8e0a2a..0de7d8185 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -143,9 +143,10 @@ void DensityRenderer::destroy() void DensityRenderer::updateQuad() { - const float width = 128.f;//static_cast(getDataBounds().width()); - const float height = 128.f;//static_cast(getDataBounds().height()); - + const auto textureSize = _densityComputation.getDensityTextureSize().toSizeF(); + const auto width = static_cast(textureSize.width()); + const auto height = static_cast(textureSize.height()); + float vertices[] = { 0.f, 0.f, 0.0f, 0.0f, width, 0.f, 1.0f, 0.0f, @@ -201,8 +202,6 @@ void DensityRenderer::drawDensity() return; } - qDebug() << "DensityRenderer::drawDensity()" << maxDensity; - _shaderDensityDraw.bind(); { _densityComputation.getDensityTexture().bind(0); diff --git a/ManiVault/src/util/DensityComputation.h b/ManiVault/src/util/DensityComputation.h index af1d283e4..4ae580e28 100644 --- a/ManiVault/src/util/DensityComputation.h +++ b/ManiVault/src/util/DensityComputation.h @@ -43,6 +43,9 @@ class CORE_EXPORT DensityComputation : protected QOpenGLFunctions_3_3_Core void compute(); + + QSize getDensityTextureSize() const { return QSize(RESOLUTION, RESOLUTION); } + private: bool hasData() const; float calculateMaxKDE(); From 5655aa3612363c886b3b25b25cb019c1f69948df Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 31 Mar 2025 15:24:37 +0200 Subject: [PATCH 23/89] Landscape rendering is working --- ManiVault/res/shaders/DensityDraw.frag | 4 ++-- ManiVault/res/shaders/IsoDensityDraw.frag | 16 ++++++++-------- ManiVault/res/shaders/Quad.vert | 4 ++-- ManiVault/src/renderers/DensityRenderer.cpp | 5 +++-- ManiVault/src/renderers/Renderer2D.cpp | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index d1b6db1be..b1855dca3 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -7,11 +7,11 @@ uniform sampler2D tex; uniform float norm; -in vec2 outUv; +in vec2 passUv; out vec4 fragColor; void main() { - float f = 1 - (texture(tex, outUv).r * norm); + float f = 1 - (texture(tex, passUv).r * norm); fragColor = vec4(vec3(f), 1); } \ No newline at end of file diff --git a/ManiVault/res/shaders/IsoDensityDraw.frag b/ManiVault/res/shaders/IsoDensityDraw.frag index 8f0650894..3299a083e 100644 --- a/ManiVault/res/shaders/IsoDensityDraw.frag +++ b/ManiVault/res/shaders/IsoDensityDraw.frag @@ -10,19 +10,19 @@ uniform sampler2D densityMap; uniform vec2 renderParams; uniform vec3 colorMapRange; -in vec2 uv; +in vec2 passUv; out vec4 fragColor; // Get the normalized density from the color map range -float getNormalizedDensity(vec2 uv) +float getNormalizedDensity(vec2 passUv) { - float density = texture(densityMap, uv).r; + float density = texture(densityMap, passUv).r; return (density - colorMapRange.x) / colorMapRange.z; } void main() { - float density = getNormalizedDensity(uv); + float density = getNormalizedDensity(passUv); if (density < renderParams.y) discard; @@ -40,10 +40,10 @@ void main() { // Central differences to find out if we draw the iso contour instead of the color vec4 neighborDensities; - neighborDensities.x = getNormalizedDensity(uv + texelSize.xz); - neighborDensities.y = getNormalizedDensity(uv - texelSize.xz); - neighborDensities.z = getNormalizedDensity(uv + texelSize.zy); - neighborDensities.w = getNormalizedDensity(uv - texelSize.zy); + neighborDensities.x = getNormalizedDensity(passUv + texelSize.xz); + neighborDensities.y = getNormalizedDensity(passUv - texelSize.xz); + neighborDensities.z = getNormalizedDensity(passUv + texelSize.zy); + neighborDensities.w = getNormalizedDensity(passUv - texelSize.zy); ivec4 stepId = min(ivec4(floor(neighborDensities * vec4(numSteps+1))), ivec4(numSteps)); isBoundary = (any(notEqual(stepId.xxx, stepId.yzw))); diff --git a/ManiVault/res/shaders/Quad.vert b/ManiVault/res/shaders/Quad.vert index 80d4b7ea3..92b6da723 100644 --- a/ManiVault/res/shaders/Quad.vert +++ b/ManiVault/res/shaders/Quad.vert @@ -9,10 +9,10 @@ layout(location = 1) in vec2 uv; uniform mat4 mvp; -out vec2 outUv; +out vec2 passUv; void main() { gl_Position = mvp * vec4(vertex, 0, 1); - outUv = (vertex / vec2(128.f, 128.f)); + passUv = uv; } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 0de7d8185..5b66a6d95 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -41,6 +41,8 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) //_densityComputation.setBounds(-100, -100, -100, 100); updateQuad(); + + getNavigator().setZoomRectangleWorld(dataBounds); } void DensityRenderer::setRenderMode(RenderMode renderMode) @@ -111,7 +113,6 @@ void DensityRenderer::init() void DensityRenderer::render() { - qDebug() << "DensityRenderer::render()" << _renderMode; beginRender(); { switch (_renderMode) { @@ -212,7 +213,7 @@ void DensityRenderer::drawDensity() drawQuad(); } - //_shaderDensityDraw.release(); + _shaderDensityDraw.release(); } void DensityRenderer::drawLandscape() diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 50ae9e190..a404b641f 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -151,7 +151,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QMatrix4x4 matrix; // Create an orthogonal transformation matrix - matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); + matrix.ortho(-halfSize.width(), halfSize.width(),0, 2 * halfSize.height(), -1000.0f, +1000.0f); return matrix; } From 06e521fe6d8acf2b073ecff45bbd3a6f72503101 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 09:36:33 +0200 Subject: [PATCH 24/89] Add generic naviagtion action and use it in the navigator 2D action --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 2 + ManiVault/src/actions/NavigationAction.cpp | 55 +++++++++++++++++++ ManiVault/src/actions/NavigationAction.h | 58 +++++++++++++++++++++ ManiVault/src/renderers/DensityRenderer.cpp | 2 - ManiVault/src/renderers/Navigator2D.cpp | 34 ++++++++---- ManiVault/src/renderers/Navigator2D.h | 17 +++++- 6 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 ManiVault/src/actions/NavigationAction.cpp create mode 100644 ManiVault/src/actions/NavigationAction.h diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index 35ea7e91e..61448982a 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -353,6 +353,7 @@ set(PUBLIC_ACTIONS_INTERNAL_HEADERS src/actions/PaletteColorRoleAction.h src/actions/ColorSchemeAction.h src/actions/EditColorSchemeAction.h + src/actions/NavigationAction.h ) set(PUBLIC_ACTIONS_INTERNAL_SOURCES @@ -391,6 +392,7 @@ set(PUBLIC_ACTIONS_INTERNAL_SOURCES src/actions/PaletteColorRoleAction.cpp src/actions/ColorSchemeAction.cpp src/actions/EditColorSchemeAction.cpp + src/actions/NavigationAction.cpp ) set(PUBLIC_ACTIONS_INTERNAL_FILES diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp new file mode 100644 index 000000000..3f6047ac1 --- /dev/null +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "NavigationAction.h" + +#ifdef _DEBUG + //#define NAVIGATION_ACTION_VERBOSE +#endif + +//#define NAVIGATION_ACTION_VERBOSE + +namespace mv::gui +{ + +NavigationAction::NavigationAction(QObject* parent, const QString& title) : + HorizontalGroupAction(parent, title), + _zoomOutAction(this, "Zoom out"), + _zoomPercentageAction(this, "Zoom Percentage", 10.0f, 1000.0f, 100.0f, 1), + _zoomInAction(this, "Zoom In"), + _zoomExtentsAction(this, "Zoom All"), + _zoomSelectionAction(this, "Zoom Around Selection"), + _zoomRegionAction(this, "Zoom Region") +{ + setShowLabels(false); + + _zoomOutAction.setToolTip("Zoom out by 10% (-)"); + _zoomPercentageAction.setToolTip("Zoom in/out (+)"); + _zoomInAction.setToolTip("Zoom in by 10%"); + _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); + _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); + + _zoomOutAction.setIconByName("search-minus"); + _zoomInAction.setIconByName("search-plus"); + _zoomExtentsAction.setIconByName("compress"); + _zoomSelectionAction.setIconByName("search-location"); + + _zoomOutAction.setShortcut(QKeySequence("-")); + _zoomInAction.setShortcut(QKeySequence("+")); + _zoomExtentsAction.setShortcut(QKeySequence("z")); + _zoomSelectionAction.setShortcut(QKeySequence("d")); + + _zoomSelectionAction.setEnabled(false); + + _zoomPercentageAction.setSuffix("%"); + _zoomPercentageAction.setUpdateDuringDrag(false); + + addAction(&_zoomOutAction, gui::TriggerAction::Icon); + addAction(&_zoomPercentageAction); + addAction(&_zoomInAction, gui::TriggerAction::Icon); + addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); + addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); +} + +} diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h new file mode 100644 index 000000000..a33fa20b6 --- /dev/null +++ b/ManiVault/src/actions/NavigationAction.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include +#include +#include +#include + +#include + +namespace mv::gui +{ + +/** + * Navigation action class + * + * Provides actions for navigating in a renderer (the business logic + * should be handled elsewhere depending on the renderer). + * + * Note: This action is developed for internal use only + * + * @author Thomas Kroes + */ +class CORE_EXPORT NavigationAction : public HorizontalGroupAction +{ + Q_OBJECT + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + */ + Q_INVOKABLE NavigationAction(QObject* parent, const QString& title); + +public: // Action getters + + TriggerAction& getZoomOutAction() { return _zoomOutAction; } + DecimalAction& getZoomPercentageAction() { return _zoomPercentageAction; } + TriggerAction& getZoomInAction() { return _zoomInAction; } + TriggerAction& getZoomExtentsAction() { return _zoomExtentsAction; } + TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } + TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } + +private: + TriggerAction _zoomOutAction; /** Zoom out action */ + DecimalAction _zoomPercentageAction; /** Zoom action */ + TriggerAction _zoomInAction; /** Zoom in action */ + TriggerAction _zoomExtentsAction; /** Zoom extents action */ + TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ + TriggerAction _zoomRegionAction; /** Zoom to region action */ +}; + +} diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 5b66a6d95..ffe8d9f39 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -36,9 +36,7 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); - qDebug() << "DensityRenderer::setDataBounds()" << dataBounds; _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); - //_densityComputation.setBounds(-100, -100, -100, 100); updateQuad(); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 277dcc5a1..6d5964d17 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -12,6 +12,8 @@ //#define NAVIGATOR_2D_VERBOSE +using namespace mv::gui; + namespace mv { @@ -25,7 +27,8 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isZooming(false), _zoomFactor(1.0f), _zoomRectangleMargin(50.f), - _userHasNavigated() + _userHasNavigated(), + _navigationAction(this, "Navigation") { } @@ -33,18 +36,21 @@ void Navigator2D::initialize(QWidget* sourceWidget) { Q_ASSERT(sourceWidget); - if (sourceWidget) { - _sourceWidget = sourceWidget; + if (!sourceWidget) + return; - _sourceWidget->installEventFilter(this); - _sourceWidget->setFocusPolicy(Qt::StrongFocus); + _sourceWidget = sourceWidget; - connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); + _sourceWidget->installEventFilter(this); + _sourceWidget->setFocusPolicy(Qt::StrongFocus); - _updateNavigationTimer.start(16); + connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); - _initialized = true; - } + _updateNavigationTimer.start(16); + + connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + + _initialized = true; } bool Navigator2D::eventFilter(QObject* watched, QEvent* event) @@ -208,6 +214,16 @@ void Navigator2D::setEnabled(bool enabled) emit enabledChanged(_enabled); } +gui::NavigationAction& Navigator2D::getNavigationAction() +{ + return _navigationAction; +} + +const gui::NavigationAction& Navigator2D::getNavigationAction() const +{ + return _navigationAction; +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e38e14917..e045e1991 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include #include @@ -91,6 +93,18 @@ class CORE_EXPORT Navigator2D : public QObject */ void setEnabled(bool enabled); + /** + * Get the navigation action + * @return Reference to the navigation action + */ + gui::NavigationAction& getNavigationAction(); + + /** + * Get the navigation action + * @return Reference to the navigation action + */ + const gui::NavigationAction& getNavigationAction() const; + public: // Navigation /** @@ -263,8 +277,7 @@ class CORE_EXPORT Navigator2D : public QObject QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ QTimer _updateNavigationTimer; /** Timer for updating the navigation */ - QList _zoomCenterHistory; /** History of zoom centers */ - QList _zoomFactorHistory; /** History of zoom factors */ + gui::NavigationAction _navigationAction; /** Navigation group action */ static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ }; From 3345fa26a1134be12b4688d9da51138ba4f2204e Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 10:02:44 +0200 Subject: [PATCH 25/89] Work on navigation action --- ManiVault/src/actions/NavigationAction.cpp | 25 +++++++++++++++++++++- ManiVault/src/actions/NavigationAction.h | 18 ++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 3f6047ac1..d91842340 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -10,6 +10,8 @@ //#define NAVIGATION_ACTION_VERBOSE +using namespace mv::util; + namespace mv::gui { @@ -20,7 +22,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomInAction(this, "Zoom In"), _zoomExtentsAction(this, "Zoom All"), _zoomSelectionAction(this, "Zoom Around Selection"), - _zoomRegionAction(this, "Zoom Region") + _zoomRegionAction(this, "Zoom Region"), + _zoomRectangleAction(this, "Zoom Rectangle") { setShowLabels(false); @@ -45,6 +48,10 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomPercentageAction.setSuffix("%"); _zoomPercentageAction.setUpdateDuringDrag(false); + _zoomRectangleAction.setToolTip("Extents of the current view"); + _zoomRectangleAction.setIcon(combineIcons(StyledIcon("expand"), StyledIcon("ellipsis-h"))); + _zoomRectangleAction.setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup); + addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); @@ -52,4 +59,20 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); } +void NavigationAction::fromVariantMap(const QVariantMap& variantMap) +{ + HorizontalGroupAction::fromVariantMap(variantMap); + + _zoomRectangleAction.fromParentVariantMap(variantMap); +} + +QVariantMap NavigationAction::toVariantMap() const +{ + auto variantMap = HorizontalGroupAction::toVariantMap(); + + _zoomRectangleAction.insertIntoVariantMap(variantMap); + + return variantMap; +} + } diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index a33fa20b6..7450269a2 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -37,6 +38,21 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction */ Q_INVOKABLE NavigationAction(QObject* parent, const QString& title); +public: // Serialization + + /** + * Load navigation action from variant map + * @param variantMap Variant map representation of the navigation 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 TriggerAction& getZoomOutAction() { return _zoomOutAction; } @@ -45,6 +61,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction& getZoomExtentsAction() { return _zoomExtentsAction; } TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } + DecimalRectangleAction& getZoomRectangleAction() { return _zoomRectangleAction; } private: TriggerAction _zoomOutAction; /** Zoom out action */ @@ -53,6 +70,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction _zoomExtentsAction; /** Zoom extents action */ TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ TriggerAction _zoomRegionAction; /** Zoom to region action */ + DecimalRectangleAction _zoomRectangleAction; /** Rectangle action for setting the current zoom bounds */ }; } From 610254bfa9feb7c128bf8964d511e7357d1c1489 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 10:38:13 +0200 Subject: [PATCH 26/89] Work on navigation action --- ManiVault/src/renderers/Navigator2D.cpp | 81 ++++++++++++++----------- ManiVault/src/renderers/Navigator2D.h | 12 ++-- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 6d5964d17..65276e5e5 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -44,11 +44,38 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); - connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); + connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + + const auto zoomRectangleChanged = [this]() -> void { + setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); + }; - _updateNavigationTimer.start(16); + zoomRectangleChanged(); - connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + + connect(this, &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { + disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); + { + _navigationAction.getZoomExtentsAction().setEnabled(hasUserNavigated()); + + _navigationAction.getZoomRectangleAction().setLeft(static_cast(currentZoomRectangleWorld.left())); + _navigationAction.getZoomRectangleAction().setRight(static_cast(currentZoomRectangleWorld.right())); + _navigationAction.getZoomRectangleAction().setTop(static_cast(currentZoomRectangleWorld.bottom())); + _navigationAction.getZoomRectangleAction().setBottom(static_cast(currentZoomRectangleWorld.top())); + } + connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + + _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); + }); + + connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { + zoomAround(_sourceWidget->rect().center(), 1.1f); + }); + + connect(&_navigationAction.getZoomOutAction(), &TriggerAction::triggered, this, [this]() -> void { + zoomAround(_sourceWidget->rect().center(), .9f); + }); _initialized = true; } @@ -199,6 +226,23 @@ float Navigator2D::getZoomFactor() const return _zoomFactor; } +float Navigator2D::getZoomPercentage() const +{ + const auto& dataBounds = _renderer.getDataBounds(); + + float zoomPercentage = 1.f; + + const auto zoomRectangleMarginWorld = (_renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint(_zoomRectangleMargin, 0)) - _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint())).x(); + const auto zoomRectangleWorldWithMarginsAdded = getZoomRectangleWorld().marginsRemoved(QMarginsF(zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld)); + + if (dataBounds.width() < dataBounds.height()) + zoomPercentage = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorldWithMarginsAdded.height()); + else + zoomPercentage = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorldWithMarginsAdded.width()); + + return zoomPercentage * 100.f; +} + bool Navigator2D::isEnabled() const { return _enabled; @@ -329,8 +373,6 @@ void Navigator2D::resetView(bool force /*= true*/) setZoomCenterWorld(_renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height())); - - // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); @@ -499,33 +541,4 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } -void Navigator2D::updateNavigation() -{ - - //QPointF smoothedZoomCenterWorld{}; - - //if (!_zoomCenterHistory.empty()) { - // QPointF sum(0, 0); - - // for (const auto& zoomCenterWorld : _zoomCenterHistory) { - // sum += zoomCenterWorld; - // } - - // smoothedZoomCenterWorld = sum / static_cast(_zoomCenterHistory.size()); - - // _zoomCenterWorld += (smoothedZoomCenterWorld - _zoomCenterWorld); - - // if (!_zoomCenterHistory.empty()) { - // _zoomCenterHistory.pop_back(); - // } - //} - - //qDebug() << __FUNCTION__ << _zoomCenterWorld; - - - - - //setZoomRectangleWorld(getZoomRectangleWorld()); -} - } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e045e1991..abf994846 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -81,6 +81,12 @@ class CORE_EXPORT Navigator2D : public QObject */ float getZoomFactor() const; + /** + * Get the zoom percentage + * @return Zoom percentage + */ + float getZoomPercentage() const; + /** * Get whether the navigator is enabled * @return Boolean determining whether the navigator is enabled @@ -206,11 +212,6 @@ class CORE_EXPORT Navigator2D : public QObject /** End changing the zoom rectangle in world coordinates */ void endChangeZoomRectangleWorld(); -private: - - /** Smoothes the navigation over time */ - void updateNavigation(); - signals: /** Signals that panning has started */ @@ -276,7 +277,6 @@ class CORE_EXPORT Navigator2D : public QObject float _zoomRectangleMargin; /** Zoom rectangle margin */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ - QTimer _updateNavigationTimer; /** Timer for updating the navigation */ gui::NavigationAction _navigationAction; /** Navigation group action */ static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ From 1877b4959e455c588c315a86440da3519e5f14fb Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 11:59:40 +0200 Subject: [PATCH 27/89] getZoomPercentage is working --- ManiVault/src/actions/NavigationAction.cpp | 9 +++-- ManiVault/src/renderers/Navigator2D.cpp | 43 ++++++++++++---------- ManiVault/src/renderers/Navigator2D.h | 6 +++ 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index d91842340..87538485f 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -18,10 +18,10 @@ namespace mv::gui NavigationAction::NavigationAction(QObject* parent, const QString& title) : HorizontalGroupAction(parent, title), _zoomOutAction(this, "Zoom out"), - _zoomPercentageAction(this, "Zoom Percentage", 10.0f, 1000.0f, 100.0f, 1), + _zoomPercentageAction(this, "Zoom Percentage", 0.01f, 1000.0f, 100.0f, 1), _zoomInAction(this, "Zoom In"), _zoomExtentsAction(this, "Zoom All"), - _zoomSelectionAction(this, "Zoom Around Selection"), + _zoomSelectionAction(this, "Zoom Selection"), _zoomRegionAction(this, "Zoom Region"), _zoomRectangleAction(this, "Zoom Rectangle") { @@ -55,8 +55,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); - addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); - addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); + addAction(&_zoomExtentsAction); + //addAction(&_zoomSelectionAction); + //addAction(&_zoomRegionAction); } void NavigationAction::fromVariantMap(const QVariantMap& variantMap) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 65276e5e5..a92537e2b 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -26,7 +26,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleMargin(50.f), + _zoomRectangleMargin(0.f), _userHasNavigated(), _navigationAction(this, "Navigation") { @@ -66,6 +66,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) } connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + qDebug() << __FUNCTION__ << getZoomPercentage(); _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); }); @@ -73,6 +74,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) zoomAround(_sourceWidget->rect().center(), 1.1f); }); + connect(&_navigationAction.getZoomPercentageAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + setZoomPercentage(value); + }); + connect(&_navigationAction.getZoomOutAction(), &TriggerAction::triggered, this, [this]() -> void { zoomAround(_sourceWidget->rect().center(), .9f); }); @@ -228,19 +233,26 @@ float Navigator2D::getZoomFactor() const float Navigator2D::getZoomPercentage() const { - const auto& dataBounds = _renderer.getDataBounds(); + const auto dataBounds = _renderer.getDataBounds(); + const auto zoomRectangleWorld = getZoomRectangleWorld(); - float zoomPercentage = 1.f; + if (!dataBounds.isValid() || !zoomRectangleWorld.isValid()) + return 1.0f; - const auto zoomRectangleMarginWorld = (_renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint(_zoomRectangleMargin, 0)) - _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint())).x(); - const auto zoomRectangleWorldWithMarginsAdded = getZoomRectangleWorld().marginsRemoved(QMarginsF(zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld)); + const auto viewerSize = _sourceWidget->size(); + const auto totalMargins = 2 * _zoomRectangleMargin; + const auto factorX = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorld.width()); + const auto factorY = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorld.height()); + const auto scaleFactor = factorX > factorY ? factorX : factorY; + + return scaleFactor * 100.f; +} - if (dataBounds.width() < dataBounds.height()) - zoomPercentage = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorldWithMarginsAdded.height()); - else - zoomPercentage = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorldWithMarginsAdded.width()); +void Navigator2D::setZoomPercentage(float zoomPercentage) +{ + //_zoomFactor = 100.f / zoomPercentage; - return zoomPercentage * 100.f; + //setZoomRectangleWorld(getZoomRectangleWorld()); } bool Navigator2D::isEnabled() const @@ -342,15 +354,6 @@ void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) _zoomCenterWorld = zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); - - //if (std::isnan(zoomCenterWorld.x()) || std::isnan(zoomCenterWorld.y())) - // return; - - //_zoomCenterHistory.push_front(zoomCenterWorld); - - //if (_zoomCenterHistory.size() > Navigator2D::maxZoomHistorySize) { - // _zoomCenterHistory.pop_back(); - //} } void Navigator2D::resetView(bool force /*= true*/) @@ -371,7 +374,7 @@ void Navigator2D::resetView(bool force /*= true*/) _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - setZoomCenterWorld(_renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height())); + setZoomCenterWorld(QPoint()); // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index abf994846..2bf4690a1 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -87,6 +87,12 @@ class CORE_EXPORT Navigator2D : public QObject */ float getZoomPercentage() const; + /** + * Set the zoom percentage to \p zoomPercentage + * @param zoomPercentage Zoom percentage + */ + void setZoomPercentage(float zoomPercentage); + /** * Get whether the navigator is enabled * @return Boolean determining whether the navigator is enabled From e4e489a4ec876eb3cdbc544e891e23c349fa6535 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 13:15:20 +0200 Subject: [PATCH 28/89] Fix setZoomPercentage --- ManiVault/src/renderers/DensityRenderer.cpp | 1 + ManiVault/src/renderers/Navigator2D.cpp | 41 +++++++++++++++++---- ManiVault/src/renderers/Navigator2D.h | 2 +- ManiVault/src/renderers/PointRenderer.cpp | 7 ++++ ManiVault/src/renderers/PointRenderer.h | 6 +++ ManiVault/src/renderers/Renderer2D.cpp | 28 ++++++++++++-- ManiVault/src/renderers/Renderer2D.h | 31 +++++++++++++++- 7 files changed, 102 insertions(+), 14 deletions(-) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index ffe8d9f39..71c3e0fc2 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -17,6 +17,7 @@ DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); + setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); } DensityRenderer::~DensityRenderer() diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index a92537e2b..1b93d2ab1 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -44,7 +44,9 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); - connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, [this]() -> void { + resetView(true); + }); const auto zoomRectangleChanged = [this]() -> void { setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); @@ -66,8 +68,12 @@ void Navigator2D::initialize(QWidget* sourceWidget) } connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); - qDebug() << __FUNCTION__ << getZoomPercentage(); _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); + + //const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); + //const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); + + //qDebug() << "-----" << _zoomFactor << 0.01f * getZoomPercentage() << zoomFactorY / (0.01f * getZoomPercentage()); }); connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { @@ -82,6 +88,11 @@ void Navigator2D::initialize(QWidget* sourceWidget) zoomAround(_sourceWidget->rect().center(), .9f); }); + connect(&_renderer, &Renderer2D::worldBoundsChanged, this, [this](const QRectF& worldBounds) -> void { + if (!hasUserNavigated()) + resetView(); + }); + _initialized = true; } @@ -239,8 +250,6 @@ float Navigator2D::getZoomPercentage() const if (!dataBounds.isValid() || !zoomRectangleWorld.isValid()) return 1.0f; - const auto viewerSize = _sourceWidget->size(); - const auto totalMargins = 2 * _zoomRectangleMargin; const auto factorX = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorld.width()); const auto factorY = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorld.height()); const auto scaleFactor = factorX > factorY ? factorX : factorY; @@ -250,9 +259,16 @@ float Navigator2D::getZoomPercentage() const void Navigator2D::setZoomPercentage(float zoomPercentage) { - //_zoomFactor = 100.f / zoomPercentage; + if (zoomPercentage < 0.05f) + return; + + const auto zoomPercentageNormalized = .01f * zoomPercentage; + const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); + + _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; - //setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomRectangleWorld(getZoomRectangleWorld()); } bool Navigator2D::isEnabled() const @@ -356,11 +372,14 @@ void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) setZoomRectangleWorld(getZoomRectangleWorld()); } -void Navigator2D::resetView(bool force /*= true*/) +void Navigator2D::resetView(bool force /*= false*/) { if (!_initialized) return; + if (!force && hasUserNavigated()) + return; + #ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << force; #endif @@ -374,17 +393,21 @@ void Navigator2D::resetView(bool force /*= true*/) _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - setZoomCenterWorld(QPoint()); + setZoomCenterWorld(_renderer.getDataBounds().center()); // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); // _userHasNavigated = false; //} + + } endChangeZoomRectangleWorld(); } endZooming(); + + _userHasNavigated = false; } bool Navigator2D::isPanning() const @@ -515,6 +538,8 @@ void Navigator2D::beginNavigation() qDebug() << __FUNCTION__; #endif + _userHasNavigated = true; + setIsNavigating(true); emit navigationStarted(); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 2bf4690a1..5e744c02a 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -148,7 +148,7 @@ class CORE_EXPORT Navigator2D : public QObject * Reset the view * @param force Force reset event when the user has navigated */ - void resetView(bool force = true); + void resetView(bool force = false); /** * Get whether the renderer is panning diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 1f15a5ea7..7da14ad71 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -256,6 +256,13 @@ namespace mv _positionBuffer.destroy(); } + void PointRenderer::setDataBounds(const QRectF& dataBounds) + { + Renderer2D::setDataBounds(dataBounds); + + setWorldBounds(dataBounds); + } + void PointRenderer::setData(const std::vector& positions) { _gpuPoints.setPositions(positions); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index b0ca4c781..3ff62840b 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -135,6 +135,12 @@ namespace mv public: using Renderer2D::Renderer2D; + /** + * Set data bounds to \p dataBounds + * @param dataBounds Data bounds + */ + void setDataBounds(const QRectF& dataBounds) override; + void setData(const std::vector& points); void setHighlights(const std::vector& highlights, const std::int32_t& numSelectedPoints); void setFocusHighlights(const std::vector& focusHighlights, const std::int32_t& numberOfFocusHighlights); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index a404b641f..8adda12e9 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -68,15 +68,35 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) qDebug() << __FUNCTION__ << dataBounds; #endif + const auto previousDataBounds = _dataBounds; + if (dataBounds == _dataBounds) return; _dataBounds = dataBounds; - //_dataBounds.setWidth(std::max(_dataBounds.width(), 0.00000001)); - //_dataBounds.setHeight(std::max(_dataBounds.height(), 0.0001)); + emit dataBoundsChanged(previousDataBounds, _dataBounds); +} + +QRectF Renderer2D::getWorldBounds() const +{ + return _worldBounds; +} + +void Renderer2D::setWorldBounds(const QRectF& worldBounds) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << _worldBounds; +#endif + + const auto previousWorldBounds = _worldBounds; + + if (worldBounds == _worldBounds) + return; + + _worldBounds = worldBounds; - getNavigator().resetView(); + emit worldBoundsChanged(previousWorldBounds, _worldBounds); } void Renderer2D::updateModelViewProjectionMatrix() @@ -151,7 +171,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QMatrix4x4 matrix; // Create an orthogonal transformation matrix - matrix.ortho(-halfSize.width(), halfSize.width(),0, 2 * halfSize.height(), -1000.0f, +1000.0f); + matrix.ortho(-halfSize.width(), halfSize.width(), halfSize.height(), -halfSize.height(), -1000.0f, +1000.0f); return matrix; } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 49dfe1a44..40e4c548e 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -148,16 +148,45 @@ Q_OBJECT */ virtual void setDataBounds(const QRectF& dataBounds); + /** + * Get world bounds + * @return World bounds + */ + QRectF getWorldBounds() const; + + /** + * Set world bounds to \p worldBounds + * @param worldBounds World bounds + */ + virtual void setWorldBounds(const QRectF& worldBounds); + private: /** Update the model-view-projection matrix */ void updateModelViewProjectionMatrix(); +signals: + + /** + * Signals that the data bounds have changed from \p previousDataBounds to \p currentDataBounds + * @param previousDataBounds Previous data bounds + * @param currentDataBounds Current data bounds + */ + void dataBoundsChanged(const QRectF& previousDataBounds, const QRectF& currentDataBounds); + + /** + * Signals that the world bounds have changed from \p previousWorldBounds to \p currentWorldBounds + * @param previousWorldBounds Previous world bounds + * @param currentWorldBounds Current world bounds + */ + void worldBoundsChanged(const QRectF& previousWorldBounds, const QRectF& currentWorldBounds); + private: QSize _renderSize; /** Size of the renderer canvas */ Navigator2D _navigator; /** 2D navigator */ QRectF _dataBounds; /** Bounds of the data */ - QMatrix4x4 _modelMatrix; + QRectF _worldBounds; /** Bounds of the world */ + QMatrix4x4 _modelMatrix; /** Model matrix */ QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ friend class Navigator2D; From ea0643f15b5699614904b833af6b3cd30e32fb28 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 13:23:21 +0200 Subject: [PATCH 29/89] Re-instate widget action override size hint --- ManiVault/src/actions/NavigationAction.cpp | 2 ++ ManiVault/src/actions/WidgetAction.h | 2 -- ManiVault/src/actions/WidgetActionWidget.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 87538485f..12c75aca4 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -33,6 +33,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); + _zoomPercentageAction.setOverrideSizeHint(QSize(500, 0)); + _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); _zoomExtentsAction.setIconByName("compress"); diff --git a/ManiVault/src/actions/WidgetAction.h b/ManiVault/src/actions/WidgetAction.h index 6f98d9df4..c3c19fc55 100644 --- a/ManiVault/src/actions/WidgetAction.h +++ b/ManiVault/src/actions/WidgetAction.h @@ -772,14 +772,12 @@ class CORE_EXPORT WidgetAction : public QWidgetAction, public util::Serializable * Get override size hint * @return Override size hint */ - [[deprecated("This method is a placeholder and not operational yet")]] QSize getOverrideSizeHint() const; /** * Set override size hint * @param overrideSizeHint Override size hint */ - [[deprecated("This method is a placeholder and not operational yet")]] void setOverrideSizeHint(const QSize& overrideSizeHint); public: // Configuration flags diff --git a/ManiVault/src/actions/WidgetActionWidget.cpp b/ManiVault/src/actions/WidgetActionWidget.cpp index 27945d924..46efb24fe 100644 --- a/ManiVault/src/actions/WidgetActionWidget.cpp +++ b/ManiVault/src/actions/WidgetActionWidget.cpp @@ -30,8 +30,8 @@ QSize WidgetActionWidget::sizeHint() const return popupSizeHint; } - //if (!action->getOverrideSizeHint().isNull()) - // return action->getOverrideSizeHint(); + if (action->getOverrideSizeHint().width() > 0 || action->getOverrideSizeHint().height() > 0) + return action->getOverrideSizeHint(); return QWidget::sizeHint(); } From d9ca1a5eeb5256ac25e22a7005c243c7eb4206f4 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 13:48:44 +0200 Subject: [PATCH 30/89] Work on view alignment --- ManiVault/src/actions/NavigationAction.cpp | 2 +- ManiVault/src/renderers/DensityRenderer.cpp | 4 ---- ManiVault/src/renderers/Navigator2D.cpp | 23 ++++++++++++++------- ManiVault/src/renderers/Renderer2D.cpp | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 12c75aca4..0a092bce3 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -33,7 +33,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); - _zoomPercentageAction.setOverrideSizeHint(QSize(500, 0)); + _zoomPercentageAction.setOverrideSizeHint(QSize(300, 0)); _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 71c3e0fc2..40f482383 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -8,8 +8,6 @@ //#define DENSITY_RENDERER_VERBOSE #endif -#define DENSITY_RENDERER_VERBOSE - namespace mv::gui { @@ -40,8 +38,6 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); updateQuad(); - - getNavigator().setZoomRectangleWorld(dataBounds); } void DensityRenderer::setRenderMode(RenderMode renderMode) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 1b93d2ab1..b6d26169e 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -81,6 +81,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) }); connect(&_navigationAction.getZoomPercentageAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + _userHasNavigated = true; setZoomPercentage(value); }); @@ -259,16 +260,24 @@ float Navigator2D::getZoomPercentage() const void Navigator2D::setZoomPercentage(float zoomPercentage) { - if (zoomPercentage < 0.05f) - return; + beginZooming(); + { + beginChangeZoomRectangleWorld(); + { + if (zoomPercentage < 0.05f) + return; - const auto zoomPercentageNormalized = .01f * zoomPercentage; - const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); - const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); + const auto zoomPercentageNormalized = .01f * zoomPercentage; + const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; + _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; - setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomRectangleWorld(getZoomRectangleWorld()); + } + endChangeZoomRectangleWorld(); + } + endZooming(); } bool Navigator2D::isEnabled() const diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 8adda12e9..5994c734c 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -171,7 +171,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QMatrix4x4 matrix; // Create an orthogonal transformation matrix - matrix.ortho(-halfSize.width(), halfSize.width(), halfSize.height(), -halfSize.height(), -1000.0f, +1000.0f); + matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); return matrix; } From 4cc77cf29b397a7014f326742327747a4a4b1f63 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 16:27:15 +0200 Subject: [PATCH 31/89] Fix zoom percentage --- ManiVault/src/renderers/Navigator2D.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index b6d26169e..0837b78a1 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -69,15 +69,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); - - //const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); - //const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - - //qDebug() << "-----" << _zoomFactor << 0.01f * getZoomPercentage() << zoomFactorY / (0.01f * getZoomPercentage()); }); connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { - zoomAround(_sourceWidget->rect().center(), 1.1f); + setZoomPercentage(getZoomPercentage() + 10.f); }); connect(&_navigationAction.getZoomPercentageAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { @@ -86,7 +81,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) }); connect(&_navigationAction.getZoomOutAction(), &TriggerAction::triggered, this, [this]() -> void { - zoomAround(_sourceWidget->rect().center(), .9f); + setZoomPercentage(getZoomPercentage() - 10.f); }); connect(&_renderer, &Renderer2D::worldBoundsChanged, this, [this](const QRectF& worldBounds) -> void { From 2c706f84bcdc2e5bd72bba4b12a10c86589a893c Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 2 Apr 2025 08:36:22 +0200 Subject: [PATCH 32/89] Fix shortcuts --- ManiVault/src/actions/NavigationAction.cpp | 13 ++++++++---- ManiVault/src/actions/NavigationAction.h | 6 ++++++ ManiVault/src/renderers/Navigator2D.cpp | 23 ++++++++++++++++------ ManiVault/src/renderers/Navigator2D.h | 2 -- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 0a092bce3..d08bcc13e 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -40,10 +40,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction.setIconByName("compress"); _zoomSelectionAction.setIconByName("search-location"); - _zoomOutAction.setShortcut(QKeySequence("-")); - _zoomInAction.setShortcut(QKeySequence("+")); - _zoomExtentsAction.setShortcut(QKeySequence("z")); - _zoomSelectionAction.setShortcut(QKeySequence("d")); + _zoomSelectionAction.setEnabled(false); @@ -62,6 +59,14 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : //addAction(&_zoomRegionAction); } +void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) +{ + _zoomOutAction.setShortcut(shortcutsEnabled ? QKeySequence("-") : QKeySequence()); + _zoomInAction.setShortcut(shortcutsEnabled ? QKeySequence("+") : QKeySequence()); + _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("z") : QKeySequence()); + //_zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("d") : QKeySequence()); +} + void NavigationAction::fromVariantMap(const QVariantMap& variantMap) { HorizontalGroupAction::fromVariantMap(variantMap); diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index 7450269a2..8691ddc43 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -38,6 +38,12 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction */ Q_INVOKABLE NavigationAction(QObject* parent, const QString& title); + /** + * Set whether shortcuts are enabled + * @param shortcutsEnabled Boolean determining whether shortcuts are enabled + */ + void setShortcutsEnabled(bool shortcutsEnabled); + public: // Serialization /** diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 0837b78a1..ff3d14eb7 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -61,10 +61,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) { _navigationAction.getZoomExtentsAction().setEnabled(hasUserNavigated()); - _navigationAction.getZoomRectangleAction().setLeft(static_cast(currentZoomRectangleWorld.left())); - _navigationAction.getZoomRectangleAction().setRight(static_cast(currentZoomRectangleWorld.right())); - _navigationAction.getZoomRectangleAction().setTop(static_cast(currentZoomRectangleWorld.bottom())); - _navigationAction.getZoomRectangleAction().setBottom(static_cast(currentZoomRectangleWorld.top())); + _navigationAction.getZoomRectangleAction().setRectangle(currentZoomRectangleWorld.left(), currentZoomRectangleWorld.right(), currentZoomRectangleWorld.bottom(), currentZoomRectangleWorld.top()); } connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); @@ -89,6 +86,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) resetView(); }); + _sourceWidget->addAction(&_navigationAction.getZoomInAction()); + _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); + _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); + _initialized = true; } @@ -222,8 +223,15 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - //_zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); - //_zoomCenterWorld = zoomRectangleWorld.center(); + if (zoomRectangleWorld == previousZoomRectangleWorld) + return; + + _zoomFactor = std::max( + static_cast(zoomRectangleWorld.width()) / static_cast(_renderer.getRenderSize().width()), + static_cast(zoomRectangleWorld.height()) / static_cast(_renderer.getRenderSize().height()) + ); + + _zoomCenterWorld = zoomRectangleWorld.center(); emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } @@ -287,6 +295,8 @@ void Navigator2D::setEnabled(bool enabled) _enabled = enabled; + _navigationAction.setShortcutsEnabled(_enabled); + emit enabledChanged(_enabled); } @@ -337,6 +347,7 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) { beginChangeZoomRectangleWorld(); { + setZoomRectangleWorld(zoomRectangle); } endChangeZoomRectangleWorld(); } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 5e744c02a..e980acdcb 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -284,8 +284,6 @@ class CORE_EXPORT Navigator2D : public QObject QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ gui::NavigationAction _navigationAction; /** Navigation group action */ - - static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ }; } From 9a6610285d9118c670bbd0775a5ce5b2b197f9fe Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 2 Apr 2025 09:15:01 +0200 Subject: [PATCH 33/89] All shortcuts are working and the shortcuts dialog has also been updated --- ManiVault/src/actions/NavigationAction.cpp | 5 +- .../src/actions/PixelSelectionAction.cpp | 63 +++++++------------ 2 files changed, 24 insertions(+), 44 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index d08bcc13e..bb8d29f0e 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -39,9 +39,6 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomInAction.setIconByName("search-plus"); _zoomExtentsAction.setIconByName("compress"); _zoomSelectionAction.setIconByName("search-location"); - - - _zoomSelectionAction.setEnabled(false); _zoomPercentageAction.setSuffix("%"); @@ -63,7 +60,7 @@ void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) { _zoomOutAction.setShortcut(shortcutsEnabled ? QKeySequence("-") : QKeySequence()); _zoomInAction.setShortcut(shortcutsEnabled ? QKeySequence("+") : QKeySequence()); - _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("z") : QKeySequence()); + _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("O") : QKeySequence()); //_zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("d") : QKeySequence()); } diff --git a/ManiVault/src/actions/PixelSelectionAction.cpp b/ManiVault/src/actions/PixelSelectionAction.cpp index 2c6c5441f..024cc8e73 100644 --- a/ManiVault/src/actions/PixelSelectionAction.cpp +++ b/ManiVault/src/actions/PixelSelectionAction.cpp @@ -61,32 +61,26 @@ PixelSelectionAction::PixelSelectionAction(QObject* parent, const QString& title _rectangleAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Rectangle)); _rectangleAction.setToolTip("Select pixels inside a rectangle (R)"); _rectangleAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _rectangleAction.setShortcut(QKeySequence("R")); _brushAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Brush)); _brushAction.setToolTip("Select pixels using a brush tool (B)"); _brushAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _brushAction.setShortcut(QKeySequence("B")); _lassoAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Lasso)); _lassoAction.setToolTip("Select pixels using a lasso (L)"); _lassoAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _lassoAction.setShortcut(QKeySequence("L")); _polygonAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Polygon)); _polygonAction.setToolTip("Select pixels by drawing a polygon (P)"); _polygonAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _polygonAction.setShortcut(QKeySequence("P")); _sampleAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Sample)); _sampleAction.setToolTip("Sample pixel by dragging over the image (S)"); _sampleAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _sampleAction.setShortcut(QKeySequence("S")); _roiAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); _roiAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::ROI)); _roiAction.setToolTip("Sample within region of interest (I)"); - _roiAction.setShortcut(QKeySequence("I")); _modifierAction.setToolTip("Type of selection modifier"); _modifierAction.setCurrentIndex(static_cast(PixelSelectionModifierType::Replace)); @@ -113,22 +107,20 @@ PixelSelectionAction::PixelSelectionAction(QObject* parent, const QString& title _clearSelectionAction.setToolTip("Clears the selection (E)"); _clearSelectionAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _clearSelectionAction.setShortcut(QKeySequence("E")); - _selectAllAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _selectAllAction.setShortcut(QKeySequence("A")); _selectAllAction.setToolTip("Select all data points (A)"); - + _invertSelectionAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _invertSelectionAction.setShortcut(QKeySequence("I")); _invertSelectionAction.setToolTip("Invert the selection (I)"); + _invertSelectionAction.setToolTip("Invert the selection (I)"); + _notifyDuringSelectionAction.setShortcut(QKeySequence("U")); + _brushRadiusAction.setToolTip("Brush selection tool radius"); _brushRadiusAction.setSuffix("px"); _notifyDuringSelectionAction.setDefaultWidgetFlags(ToggleAction::CheckBox); _notifyDuringSelectionAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _notifyDuringSelectionAction.setShortcut(QKeySequence("U")); _notifyDuringSelectionAction.setToolTip("Notify during selection or only at the end of the selection process (U)"); const auto updatePixelSelectionTypesModel = [this]() { @@ -207,6 +199,16 @@ void PixelSelectionAction::initialize(QWidget* targetWidget, util::PixelSelectio setShortcutsEnabled(true); + _targetWidget->addAction(&_rectangleAction); + _targetWidget->addAction(&_brushAction); + _targetWidget->addAction(&_lassoAction); + _targetWidget->addAction(&_polygonAction); + _targetWidget->addAction(&_sampleAction); + _targetWidget->addAction(&_roiAction); + _targetWidget->addAction(&_selectAllAction); + _targetWidget->addAction(&_clearSelectionAction); + _targetWidget->addAction(&_invertSelectionAction); + _targetWidget->installEventFilter(this); _initialized = true; } @@ -226,34 +228,15 @@ void PixelSelectionAction::setShortcutsEnabled(const bool& shortcutsEnabled) if (!isInitialized()) return; - if (shortcutsEnabled) { - _targetWidget->addAction(&_rectangleAction); - _targetWidget->addAction(&_brushAction); - _targetWidget->addAction(&_lassoAction); - _targetWidget->addAction(&_polygonAction); - _targetWidget->addAction(&_sampleAction); - _targetWidget->addAction(&_modifierAddAction); - _targetWidget->addAction(&_modifierSubtractAction); - _targetWidget->addAction(&_clearSelectionAction); - _targetWidget->addAction(&_selectAllAction); - _targetWidget->addAction(&_invertSelectionAction); - _targetWidget->addAction(&_brushRadiusAction); - _targetWidget->addAction(&_notifyDuringSelectionAction); - } - else { - _targetWidget->removeAction(&_rectangleAction); - _targetWidget->removeAction(&_brushAction); - _targetWidget->removeAction(&_lassoAction); - _targetWidget->removeAction(&_polygonAction); - _targetWidget->removeAction(&_sampleAction); - _targetWidget->removeAction(&_modifierAddAction); - _targetWidget->removeAction(&_modifierSubtractAction); - _targetWidget->removeAction(&_clearSelectionAction); - _targetWidget->removeAction(&_selectAllAction); - _targetWidget->removeAction(&_invertSelectionAction); - _targetWidget->removeAction(&_brushRadiusAction); - _targetWidget->removeAction(&_notifyDuringSelectionAction); - } + _rectangleAction.setShortcut(shortcutsEnabled ? QKeySequence("R") : QKeySequence()); + _brushAction.setShortcut(shortcutsEnabled ? QKeySequence("B") : QKeySequence()); + _lassoAction.setShortcut(shortcutsEnabled ? QKeySequence("L") : QKeySequence()); + _polygonAction.setShortcut(shortcutsEnabled ? QKeySequence("P") : QKeySequence()); + _sampleAction.setShortcut(shortcutsEnabled ? QKeySequence("U") : QKeySequence()); + _roiAction.setShortcut(shortcutsEnabled ? QKeySequence("W") : QKeySequence()); + _selectAllAction.setShortcut(shortcutsEnabled ? QKeySequence("A") : QKeySequence()); + _clearSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("E") : QKeySequence()); + _invertSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("I") : QKeySequence()); } void PixelSelectionAction::initType() From 71ac3523e3092ee0af6f9d09d140a8e3a93dac77 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 2 Apr 2025 14:13:21 +0200 Subject: [PATCH 34/89] Added numerical, decimal and integral point action --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 32 ++- ManiVault/src/actions/DecimalPointAction.cpp | 63 ++++++ ManiVault/src/actions/DecimalPointAction.h | 66 ++++++ ManiVault/src/actions/IntegralPointAction.cpp | 61 ++++++ ManiVault/src/actions/IntegralPointAction.h | 62 ++++++ ManiVault/src/actions/NavigationAction.cpp | 35 ++- ManiVault/src/actions/NavigationAction.h | 5 + ManiVault/src/actions/NumericalAction.h | 51 ++--- .../src/actions/NumericalPointAction.cpp | 9 + ManiVault/src/actions/NumericalPointAction.h | 201 ++++++++++++++++++ ManiVault/src/renderers/DensityRenderer.cpp | 6 +- ManiVault/src/renderers/Navigator2D.cpp | 84 +++++--- ManiVault/src/renderers/Navigator2D.h | 20 ++ ManiVault/src/renderers/Renderer.h | 13 +- ManiVault/src/renderers/Renderer2D.h | 5 +- 15 files changed, 626 insertions(+), 87 deletions(-) create mode 100644 ManiVault/src/actions/DecimalPointAction.cpp create mode 100644 ManiVault/src/actions/DecimalPointAction.h create mode 100644 ManiVault/src/actions/IntegralPointAction.cpp create mode 100644 ManiVault/src/actions/IntegralPointAction.h create mode 100644 ManiVault/src/actions/NumericalPointAction.cpp create mode 100644 ManiVault/src/actions/NumericalPointAction.h diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index 61448982a..e7fa0dbdc 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -91,6 +91,12 @@ set(PUBLIC_NUMERICAL_ACTIONS_HEADERS src/actions/NumericalRangeAction.h src/actions/DecimalRangeAction.h src/actions/IntegralRangeAction.h + src/actions/NumericalPointAction.h + src/actions/DecimalPointAction.h + src/actions/IntegralPointAction.h + src/actions/RectangleAction.h + src/actions/IntegralRectangleAction.h + src/actions/DecimalRectangleAction.h ) set(PUBLIC_NUMERICAL_ACTIONS_SOURCES @@ -100,6 +106,12 @@ set(PUBLIC_NUMERICAL_ACTIONS_SOURCES src/actions/NumericalRangeAction.cpp src/actions/DecimalRangeAction.cpp src/actions/IntegralRangeAction.cpp + src/actions/NumericalPointAction.cpp + src/actions/DecimalPointAction.cpp + src/actions/IntegralPointAction.cpp + src/actions/RectangleAction.cpp + src/actions/IntegralRectangleAction.cpp + src/actions/DecimalRectangleAction.cpp ) set(PUBLIC_NUMERICAL_ACTIONS_FILES @@ -107,23 +119,6 @@ set(PUBLIC_NUMERICAL_ACTIONS_FILES ${PUBLIC_NUMERICAL_ACTIONS_SOURCES} ) -set(PUBLIC_RECTANGLE_ACTIONS_HEADERS - src/actions/RectangleAction.h - src/actions/IntegralRectangleAction.h - src/actions/DecimalRectangleAction.h -) - -set(PUBLIC_RECTANGLE_ACTIONS_SOURCES - src/actions/RectangleAction.cpp - src/actions/IntegralRectangleAction.cpp - src/actions/DecimalRectangleAction.cpp -) - -set(PUBLIC_RECTANGLE_ACTIONS_FILES - ${PUBLIC_RECTANGLE_ACTIONS_HEADERS} - ${PUBLIC_RECTANGLE_ACTIONS_SOURCES} -) - set(PUBLIC_TEXTUAL_ACTIONS_HEADERS src/actions/StringAction.h src/actions/StringsAction.h @@ -1052,7 +1047,6 @@ set(PUBLIC_HEADERS ${PUBLIC_EVENT_HEADERS} ${PUBLIC_COLOR_MAP_ACTION_HEADERS} ${PUBLIC_NUMERICAL_ACTIONS_HEADERS} - ${PUBLIC_RECTANGLE_ACTIONS_HEADERS} ${PUBLIC_TEXTUAL_ACTIONS_HEADERS} ${PUBLIC_GROUPING_ACTIONS_HEADERS} ${PUBLIC_TRIGGER_ACTIONS_HEADERS} @@ -1100,7 +1094,6 @@ set(PUBLIC_SOURCES ${PUBLIC_EVENT_SOURCES} ${PUBLIC_COLOR_MAP_ACTION_SOURCES} ${PUBLIC_NUMERICAL_ACTIONS_SOURCES} - ${PUBLIC_RECTANGLE_ACTIONS_SOURCES} ${PUBLIC_TEXTUAL_ACTIONS_SOURCES} ${PUBLIC_GROUPING_ACTIONS_SOURCES} ${PUBLIC_TRIGGER_ACTIONS_SOURCES} @@ -1161,7 +1154,6 @@ source_group(CoreInterface FILES ${PUBLIC_CORE_INTERFACE_FILES}) source_group(Event FILES ${PUBLIC_EVENT_FILES}) source_group(Actions\\Colormap FILES ${PUBLIC_COLOR_MAP_ACTION_FILES}) source_group(Actions\\Numerical FILES ${PUBLIC_NUMERICAL_ACTIONS_FILES}) -source_group(Actions\\Rectangle FILES ${PUBLIC_RECTANGLE_ACTIONS_FILES}) source_group(Actions\\Textual FILES ${PUBLIC_TEXTUAL_ACTIONS_FILES}) source_group(Actions\\Grouping FILES ${PUBLIC_GROUPING_ACTIONS_FILES}) source_group(Actions\\Trigger FILES ${PUBLIC_TRIGGER_ACTIONS_FILES}) diff --git a/ManiVault/src/actions/DecimalPointAction.cpp b/ManiVault/src/actions/DecimalPointAction.cpp new file mode 100644 index 000000000..f89e90b73 --- /dev/null +++ b/ManiVault/src/actions/DecimalPointAction.cpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "DecimalPointAction.h" + +using namespace mv::util; + +namespace mv::gui { + +DecimalPointAction::DecimalPointAction(QObject* parent, const QString& title) : + NumericalPointAction(parent, title, std::numeric_limits::lowest(), std::numeric_limits::max()) +{ + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) { + getAction(static_cast(axisIndex)).setNumberOfDecimals(2); + + connect(&getAction(static_cast(axisIndex)), &DecimalAction::valueChanged, this, [this](float value) -> void { + emit valueChanged(getX(), getY()); + }); + } +} + +void DecimalPointAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) +{ + auto publicDecimalPointAction = dynamic_cast(publicAction); + + Q_ASSERT(publicDecimalPointAction); + + if (!publicDecimalPointAction) + return; + + connect(this, &DecimalPointAction::valueChanged, publicDecimalPointAction, [publicDecimalPointAction](float x, float y) -> void { + publicDecimalPointAction->set(x, y); + }); + + connect(publicDecimalPointAction, &DecimalPointAction::valueChanged, this, [this](float x, float y) -> void { + set(x, y); + }); + + set(publicDecimalPointAction->get()); + + NumericalPointAction::connectToPublicAction(publicAction, recursive); +} + +void DecimalPointAction::disconnectFromPublicAction(bool recursive) +{ + if (!isConnected()) + return; + + auto publicDecimalPointAction = dynamic_cast(getPublicAction()); + + Q_ASSERT(publicDecimalPointAction); + + if (!publicDecimalPointAction) + return; + + disconnect(this, &DecimalPointAction::valueChanged, publicDecimalPointAction, nullptr); + disconnect(publicDecimalPointAction, &DecimalPointAction::valueChanged, this, nullptr); + + NumericalPointAction::disconnectFromPublicAction(recursive); +} + +} diff --git a/ManiVault/src/actions/DecimalPointAction.h b/ManiVault/src/actions/DecimalPointAction.h new file mode 100644 index 000000000..0a41ea9dd --- /dev/null +++ b/ManiVault/src/actions/DecimalPointAction.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include "NumericalPointAction.h" + +namespace mv::gui { + +/** + * Decimal action class + * + * Stores a two-dimensional decimal point value. + * + * @author Thomas Kroes + */ +class CORE_EXPORT DecimalPointAction : public NumericalPointAction +{ + Q_OBJECT + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + */ + Q_INVOKABLE explicit DecimalPointAction(QObject* parent, const QString& title); + +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; + +signals: + + /** + * Signals that the coordinate values changed to \p x and \p y + * @param x X coordinate value + * @param y Y coordinate value + */ + void valueChanged(float x, float y); + +protected: + static constexpr float INIT_VALUE = 0.0; /** Initialization value */ + static constexpr int INIT_DECIMALS = 1; /** Initialization number of decimals */ + + friend class AbstractActionsManager; +}; + +} + +Q_DECLARE_METATYPE(mv::gui::DecimalPointAction) + +inline const auto decimalPointActionMetaTypeId = qRegisterMetaType("mv::gui::DecimalPointAction"); diff --git a/ManiVault/src/actions/IntegralPointAction.cpp b/ManiVault/src/actions/IntegralPointAction.cpp new file mode 100644 index 000000000..f7ed69040 --- /dev/null +++ b/ManiVault/src/actions/IntegralPointAction.cpp @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "IntegralPointAction.h" + +using namespace mv::util; + +namespace mv::gui { + +IntegralPointAction::IntegralPointAction(QObject* parent, const QString& title) : + NumericalPointAction(parent, title, std::numeric_limits::lowest(), std::numeric_limits::max()) +{ + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) { + connect(&getAction(static_cast(axisIndex)), &IntegralAction::valueChanged, this, [this](std::int32_t value) -> void { + emit valueChanged(getX(), getY()); + }); + } +} + +void IntegralPointAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) +{ + auto publicIntegralPointAction = dynamic_cast(publicAction); + + Q_ASSERT(publicIntegralPointAction); + + if (!publicIntegralPointAction) + return; + + connect(this, &IntegralPointAction::valueChanged, publicIntegralPointAction, [publicIntegralPointAction](std::int32_t x, std::int32_t y) -> void { + publicIntegralPointAction->set(x, y); + }); + + connect(publicIntegralPointAction, &IntegralPointAction::valueChanged, this, [this](std::int32_t x, std::int32_t y) -> void { + set(x, y); + }); + + set(publicIntegralPointAction->get()); + + NumericalPointAction::connectToPublicAction(publicAction, recursive); +} + +void IntegralPointAction::disconnectFromPublicAction(bool recursive) +{ + if (!isConnected()) + return; + + auto publicIntegralPointAction = dynamic_cast(getPublicAction()); + + Q_ASSERT(publicIntegralPointAction); + + if (!publicIntegralPointAction) + return; + + disconnect(this, &IntegralPointAction::valueChanged, publicIntegralPointAction, nullptr); + disconnect(publicIntegralPointAction, &IntegralPointAction::valueChanged, this, nullptr); + + NumericalPointAction::disconnectFromPublicAction(recursive); +} + +} diff --git a/ManiVault/src/actions/IntegralPointAction.h b/ManiVault/src/actions/IntegralPointAction.h new file mode 100644 index 000000000..b96992eae --- /dev/null +++ b/ManiVault/src/actions/IntegralPointAction.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include "NumericalPointAction.h" + +namespace mv::gui { + +/** + * Integral action class + * + * Stores a two-dimensional integral point value. + * + * @author Thomas Kroes + */ +class CORE_EXPORT IntegralPointAction : public NumericalPointAction +{ + Q_OBJECT + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + */ + Q_INVOKABLE explicit IntegralPointAction(QObject* parent, const QString& title); + +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; + +signals: + + /** + * Signals that the coordinate values changed to \p x and \p y + * @param x X coordinate value + * @param y Y coordinate value + */ + void valueChanged(std::int32_t x, std::int32_t y); + + friend class AbstractActionsManager; +}; + +} + +Q_DECLARE_METATYPE(mv::gui::IntegralPointAction) + +inline const auto integralPointActionMetaTypeId = qRegisterMetaType("mv::gui::IntegralPointAction"); diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index bb8d29f0e..f69f921c9 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -4,6 +4,8 @@ #include "NavigationAction.h" +#include "GroupSectionTreeItem.h" + #ifdef _DEBUG //#define NAVIGATION_ACTION_VERBOSE #endif @@ -23,7 +25,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction(this, "Zoom All"), _zoomSelectionAction(this, "Zoom Selection"), _zoomRegionAction(this, "Zoom Region"), - _zoomRectangleAction(this, "Zoom Rectangle") + _zoomRectangleAction(this, "Zoom Rectangle"), + _zoomCenterAction(this, "Zoom Center"), + _zoomFactorAction(this, "Zoom Factor") { setShowLabels(false); @@ -48,12 +52,33 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomRectangleAction.setIcon(combineIcons(StyledIcon("expand"), StyledIcon("ellipsis-h"))); _zoomRectangleAction.setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup); + _zoomCenterAction.setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup); + _zoomCenterAction.setIconByName("ruler"); + _zoomCenterAction.setDefaultWidgetFlags(GroupAction::WidgetFlag::Vertical); + _zoomCenterAction.setPopupSizeHint(QSize(250, 0)); + + _zoomCenterAction.getXAction().setDefaultWidgetFlags(DecimalAction::WidgetFlag::SpinBox); + _zoomCenterAction.getYAction().setDefaultWidgetFlags(DecimalAction::WidgetFlag::SpinBox); + addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); addAction(&_zoomExtentsAction); - //addAction(&_zoomSelectionAction); + addAction(&_zoomCenterAction); + //addAction(&_zoomRegionAction); + + // connect(&_zoomCenterXAction, &DecimalAction::valueChanged, this, [this](float value) -> void { + // qDebug() << "Zoom center x: " << value; + //}); + + // connect(&_zoomCenterYAction, &DecimalAction::valueChanged, this, [this](float value) -> void { + // qDebug() << "Zoom center y: " << value; + //}); + + connect(&_zoomFactorAction, &DecimalAction::valueChanged, this, [this](float value) -> void { + qDebug() << "Zoom factor: " << value; + }); } void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) @@ -68,14 +93,16 @@ void NavigationAction::fromVariantMap(const QVariantMap& variantMap) { HorizontalGroupAction::fromVariantMap(variantMap); - _zoomRectangleAction.fromParentVariantMap(variantMap); + _zoomCenterAction.fromParentVariantMap(variantMap); + _zoomFactorAction.fromParentVariantMap(variantMap); } QVariantMap NavigationAction::toVariantMap() const { auto variantMap = HorizontalGroupAction::toVariantMap(); - _zoomRectangleAction.insertIntoVariantMap(variantMap); + _zoomCenterAction.insertIntoVariantMap(variantMap); + _zoomFactorAction.insertIntoVariantMap(variantMap); return variantMap; } diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index 8691ddc43..d7164d57c 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -68,6 +69,8 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } DecimalRectangleAction& getZoomRectangleAction() { return _zoomRectangleAction; } + DecimalPointAction& getZoomCenterAction() { return _zoomCenterAction; } + DecimalAction& getZoomFactorAction() { return _zoomFactorAction; } private: TriggerAction _zoomOutAction; /** Zoom out action */ @@ -77,6 +80,8 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ TriggerAction _zoomRegionAction; /** Zoom to region action */ DecimalRectangleAction _zoomRectangleAction; /** Rectangle action for setting the current zoom bounds */ + DecimalPointAction _zoomCenterAction; /** Zoom center action */ + DecimalAction _zoomFactorAction; /** Zoom factor action */ }; } diff --git a/ManiVault/src/actions/NumericalAction.h b/ManiVault/src/actions/NumericalAction.h index 126ac0c65..ad9202dfb 100644 --- a/ManiVault/src/actions/NumericalAction.h +++ b/ManiVault/src/actions/NumericalAction.h @@ -59,23 +59,15 @@ class NumericalAction : public WidgetAction _value(), _minimum(std::numeric_limits::lowest()), _maximum(std::numeric_limits::max()), - _prefix(), - _suffix(), _numberOfDecimals(), - _updateDuringDrag(true), - _valueChanged(), - _minimumChanged(), - _maximumChanged(), - _prefixChanged(), - _suffixChanged(), - _numberOfDecimalsChanged() + _updateDuringDrag(true) { setText(title); setDefaultWidgetFlags(WidgetFlag::Default); } /** Gets the current value */ - virtual NumericalType getValue() const final { + virtual NumericalType getValue() const { return _value; } @@ -101,7 +93,7 @@ class NumericalAction : public WidgetAction } /** Gets the minimum value */ - virtual NumericalType getMinimum() const final { + virtual NumericalType getMinimum() const { return _minimum; } @@ -109,7 +101,7 @@ class NumericalAction : public WidgetAction * Sets the minimum value * @param minimum Minimum value */ - virtual void setMinimum(NumericalType minimum) final { + virtual void setMinimum(NumericalType minimum) { if (minimum == _minimum) return; @@ -119,7 +111,7 @@ class NumericalAction : public WidgetAction } /** Gets the maximum value */ - virtual NumericalType getMaximum() const final { + virtual NumericalType getMaximum() const { return _maximum; } @@ -127,7 +119,7 @@ class NumericalAction : public WidgetAction * Sets the maximum value * @param maximum Maximum value */ - virtual void setMaximum(NumericalType maximum) final { + virtual void setMaximum(NumericalType maximum) { if (maximum == _maximum) return; @@ -140,7 +132,7 @@ class NumericalAction : public WidgetAction * Gets the value range * @return Range */ - virtual util::NumericalRange getRange() const final { + virtual util::NumericalRange getRange() const { return { getMinimum(), getMaximum() }; } @@ -148,7 +140,7 @@ class NumericalAction : public WidgetAction * Sets the value range * @param range Range */ - virtual void setRange(util::NumericalRange range) final { + virtual void setRange(util::NumericalRange range) { setMinimum(range.getMinimum()); setMaximum(range.getMaximum()); } @@ -158,13 +150,13 @@ class NumericalAction : public WidgetAction * @param minimum Minimum value * @param maximum Maximum value */ - virtual void setRange(NumericalType minimum, NumericalType maximum) final { + virtual void setRange(NumericalType minimum, NumericalType maximum) { setMinimum(minimum); setMaximum(maximum); } /** Gets the prefix */ - virtual QString getPrefix() const final { + virtual QString getPrefix() const { return _prefix; } @@ -172,7 +164,7 @@ class NumericalAction : public WidgetAction * Sets the prefix * @param prefix Prefix */ - virtual void setPrefix(const QString& prefix) final { + virtual void setPrefix(const QString& prefix) { if (prefix == _prefix) return; @@ -182,7 +174,7 @@ class NumericalAction : public WidgetAction } /** Gets the suffix */ - virtual QString getSuffix() const final { + virtual QString getSuffix() const { return _suffix; } @@ -190,7 +182,7 @@ class NumericalAction : public WidgetAction * Sets the suffix * @param suffix Suffix */ - virtual void setSuffix(const QString& suffix) final { + virtual void setSuffix(const QString& suffix) { if (suffix == _suffix) return; @@ -200,7 +192,7 @@ class NumericalAction : public WidgetAction } /** Gets the number of decimals */ - virtual std::uint32_t getNumberOfDecimals() const final { + virtual std::uint32_t getNumberOfDecimals() const { return _numberOfDecimals; } @@ -208,7 +200,7 @@ class NumericalAction : public WidgetAction * Sets the number of decimals * @param numberOfDecimals number of decimals */ - virtual void setNumberOfDecimals(std::uint32_t numberOfDecimals) final { + virtual void setNumberOfDecimals(std::uint32_t numberOfDecimals) { if (numberOfDecimals == _numberOfDecimals) return; @@ -218,7 +210,7 @@ class NumericalAction : public WidgetAction } /** Gets whether the value should update during interaction */ - virtual bool getUpdateDuringDrag() const final { + virtual bool getUpdateDuringDrag() const { return _updateDuringDrag; } @@ -226,7 +218,7 @@ class NumericalAction : public WidgetAction * Sets whether the value should update during interaction * @param updateDuringDrag Whether the value should update during interaction */ - virtual void setUpdateDuringDrag(bool updateDuringDrag) final { + virtual void setUpdateDuringDrag(bool updateDuringDrag) { if (updateDuringDrag == _updateDuringDrag) return; @@ -234,23 +226,24 @@ class NumericalAction : public WidgetAction } /** Returns whether the current value is at its minimum */ - virtual bool isAtMinimum() const final { + virtual bool isAtMinimum() const { return _value == _minimum; } /** Returns whether the current value is at its maximum */ - virtual bool isAtMaximum() const final { + virtual bool isAtMaximum() const { return _value == _maximum; } /** Returns the length of the interval defined by the minimum and maximum value */ - virtual double getIntervalLength() const final { + virtual double getIntervalLength() const { return static_cast(_maximum) - static_cast(_minimum); } /** Returns the normalized value */ - virtual double getNormalized() const final { + virtual double getNormalized() const { const auto offset = static_cast(_value) - static_cast(_minimum); + return static_cast(offset / getIntervalLength()); } diff --git a/ManiVault/src/actions/NumericalPointAction.cpp b/ManiVault/src/actions/NumericalPointAction.cpp new file mode 100644 index 000000000..d3bca223e --- /dev/null +++ b/ManiVault/src/actions/NumericalPointAction.cpp @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "NumericalPointAction.h" + +namespace mv::gui { + +} \ No newline at end of file diff --git a/ManiVault/src/actions/NumericalPointAction.h b/ManiVault/src/actions/NumericalPointAction.h new file mode 100644 index 000000000..4626af3b6 --- /dev/null +++ b/ManiVault/src/actions/NumericalPointAction.h @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include "GroupAction.h" + +#include +#include + +namespace mv::gui { + +/** + * Numerical point action base class + * + * Stores a two-dimensional point value. + * + * @author Thomas Kroes + */ +template +class NumericalPointAction : public GroupAction +{ +public: + + /** Axis enum */ + enum class Axis { + X = 0, /** X axis */ + Y, /** Y axis */ + + Count + }; + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + * @param minimum Minimum value + * @param maximum Maximum value + */ + NumericalPointAction(QObject* parent, const QString& title, NumericalType minimum, NumericalType maximum) : + GroupAction(parent, title), + _elementActions{ + NumericalActionType(this, "X", minimum, maximum), + NumericalActionType(this, "Y", minimum, maximum) + } + { + setText(title); + setDefaultWidgetFlags(WidgetFlag::Default); + + addAction(&getXAction()); + addAction(&getYAction()); + } + + /** + * Retrieves the X coordinate value + * @return The X coordinate + */ + NumericalType getX() const { return getXAction().getValue(); } + + /** + * Set the X coordinate value + * @param x X coordinate value + */ + void setX(const NumericalType& x) { + getXAction().setValue(x); + } + + /** + * Set the Y coordinate value + * @param y Y coordinate value + */ + void setY(const NumericalType& y) { + getYAction().setValue(y); + } + + /** + * Retrieves the Y coordinate value + * @return The Y coordinate + */ + NumericalType getY() const { return getYAction().getValue(); } + + /** + * Set the X and Y coordinate values + * @param x X coordinate value + * @param y Y coordinate value + */ + void set(const NumericalType& x, const NumericalType& y) { + setX(x); + setY(y); + } + + /** + * Retrieves the X and Y coordinate values as a QPoint + * @return QPoint value + */ + QPoint getPoint() const { + return QPoint(static_cast(getX()), static_cast(getY())); + } + + /** + * Set the X and Y coordinate values from a QPoint + * @param point QPoint value + */ + void set(const QPoint& point) { + set(static_cast(point.x()), static_cast(point.y())); + } + + /** + * Retrieves the X and Y coordinate values as a QPointF + * @return QPointF value + */ + QPointF getPointF() const { + return QPointF(getX(), getY()); + } + + /** + * Set the X and Y coordinate values from a QPointF + * @param point QPointF value + */ + void set(const QPointF& point) { + set(static_cast(point.x()), static_cast(point.y())); + } + + /** + * Retrieves the X and Y coordinate values as a std::pair + * @return Pair of X and Y coordinate values + */ + std::pair get() const { + return std::make_pair(getX(), getY()); + } + + /** + * Set the X and Y coordinate values from a std::pair + * @param pair Pair of X and Y coordinate values + */ + void set(const std::pair& pair) { + set(pair.first, pair.second); + } + + /** + * Retrieves the X and Y coordinate values as a QVector2D + * @return QVector2D value + */ + QVector2D getVector() const { + return QVector2D(getX(), getY()); + } + + /** + * Set the X and Y coordinate values from a Qt vector + * @param vector QVector2D value + */ + void set(const QVector2D& vector) { + set(static_cast(vector.x()), static_cast(vector.y())); + } + +public: // Serialization + + /** + * Load numerical point action from variant map + * @param variantMap Variant map representation of the numerical point action + */ + void fromVariantMap(const QVariantMap& variantMap) override + { + WidgetAction::fromVariantMap(variantMap); + + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) + _elementActions[axisIndex].fromParentVariantMap(variantMap); + } + + /** + * Save numerical point action to variant map + * @return Variant map representation of the numerical point action + */ + QVariantMap toVariantMap() const override + { + auto variantMap = WidgetAction::toVariantMap(); + + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) + _elementActions[axisIndex].insertIntoVariantMap(variantMap); + + return variantMap; + } + +public: // Action getters + + const NumericalActionType& getAction(const Axis& axis) const { return _elementActions[static_cast(axis)]; } + const NumericalActionType& getXAction() const { return getAction(Axis::X); } + const NumericalActionType& getYAction() const { return getAction(Axis::Y); } + + NumericalActionType& getAction(const Axis& axis) { return _elementActions[static_cast(axis)]; } + NumericalActionType& getXAction() { return getAction(Axis::X); } + NumericalActionType& getYAction() { return getAction(Axis::Y); } + +private: + NumericalActionType _elementActions[static_cast(Axis::Count)]; /** Elements actions */ +}; + +} diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 40f482383..5498f9cb3 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -14,8 +14,6 @@ namespace mv::gui DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { - getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); - setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); } DensityRenderer::~DensityRenderer() @@ -37,6 +35,10 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); + // .0f, -_densityComputation.getDensityTextureSize().height() + + setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); + updateQuad(); } diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index ff3d14eb7..c747c2281 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -68,6 +68,28 @@ void Navigator2D::initialize(QWidget* sourceWidget) _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); }); + connect(this, &Navigator2D::zoomCenterWorldChanged, this, [this](const QPointF& previousZoomCenterWorld, const QPointF& currentZoomCenterWorld) -> void { + _navigationAction.getZoomCenterAction().set(currentZoomCenterWorld); + + qDebug() << currentZoomCenterWorld; + }); + + connect(this, &Navigator2D::zoomFactorChanged, this, [this](float previousZoomFactor, float currentZoomFactor) -> void { + _navigationAction.getZoomFactorAction().setValue(currentZoomFactor); + }); + + connect(&_navigationAction.getZoomCenterAction(), &DecimalPointAction::valueChanged, this, [this](float x, float y) -> void { + beginChangeZoomRectangleWorld(); + { + setZoomCenterWorld(QPointF(x, y)); + } + endChangeZoomRectangleWorld(); + }); + + // connect(&_navigationAction.getZoomCenterYAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + // setZoomCenterWorld(QPointF(_zoomCenterWorld.x(), value)); + //}); + connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { setZoomPercentage(getZoomPercentage() + 10.f); }); @@ -81,11 +103,17 @@ void Navigator2D::initialize(QWidget* sourceWidget) setZoomPercentage(getZoomPercentage() - 10.f); }); + connect(&_navigationAction.getZoomFactorAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + setZoomFactor(value); + }); + connect(&_renderer, &Renderer2D::worldBoundsChanged, this, [this](const QRectF& worldBounds) -> void { if (!hasUserNavigated()) resetView(); }); + setZoomFactor(_navigationAction.getZoomFactorAction().getValue()); + _sourceWidget->addAction(&_navigationAction.getZoomInAction()); _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); @@ -246,16 +274,29 @@ float Navigator2D::getZoomFactor() const return _zoomFactor; } +void Navigator2D::setZoomFactor(float zoomFactor) +{ + qDebug() << __FUNCTION__ << zoomFactor; + if (zoomFactor == _zoomFactor) + return; + + const auto previousZoomFactor = _zoomFactor; + + _zoomFactor = zoomFactor; + + emit zoomFactorChanged(previousZoomFactor, _zoomFactor); +} + float Navigator2D::getZoomPercentage() const { - const auto dataBounds = _renderer.getDataBounds(); + const auto worldBounds = _renderer.getWorldBounds(); const auto zoomRectangleWorld = getZoomRectangleWorld(); - if (!dataBounds.isValid() || !zoomRectangleWorld.isValid()) + if (!worldBounds.isValid() || !zoomRectangleWorld.isValid()) return 1.0f; - const auto factorX = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorld.width()); - const auto factorY = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorld.height()); + const auto factorX = static_cast(worldBounds.width()) / static_cast(zoomRectangleWorld.width()); + const auto factorY = static_cast(worldBounds.height()) / static_cast(zoomRectangleWorld.height()); const auto scaleFactor = factorX > factorY ? factorX : factorY; return scaleFactor * 100.f; @@ -271,12 +312,10 @@ void Navigator2D::setZoomPercentage(float zoomPercentage) return; const auto zoomPercentageNormalized = .01f * zoomPercentage; - const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); - const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); + const auto zoomFactorX = static_cast(_renderer.getWorldBounds().width()) / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = static_cast(_renderer.getWorldBounds().height()) / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; - - setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomFactor(std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized); } endChangeZoomRectangleWorld(); } @@ -325,8 +364,7 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) { const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - _zoomFactor /= factor; - + setZoomFactor(_zoomFactor /= factor); setZoomCenterWorld(p1 + (_zoomCenterWorld - p1) / factor); } endChangeZoomRectangleWorld(); @@ -382,13 +420,18 @@ void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) if (zoomCenterWorld == _zoomCenterWorld) return; + const auto previousZoomCenterWorld = _zoomCenterWorld; + _zoomCenterWorld = zoomCenterWorld; - setZoomRectangleWorld(getZoomRectangleWorld()); + emit zoomCenterWorldChanged(previousZoomCenterWorld, _zoomCenterWorld); } void Navigator2D::resetView(bool force /*= false*/) { + if (mv::projects().isOpeningProject() || mv::projects().isImportingProject()) + return; + if (!_initialized) return; @@ -403,20 +446,11 @@ void Navigator2D::resetView(bool force /*= false*/) { beginChangeZoomRectangleWorld(); { - const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); - const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - - _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - - setZoomCenterWorld(_renderer.getDataBounds().center()); - - // if (!_userHasNavigated || force) { - // setZoomRectangleWorld(_renderer.getDataBounds()); - - // _userHasNavigated = false; - //} + const auto zoomFactorX = _renderer.getWorldBounds().width() / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = _renderer.getWorldBounds().height() / static_cast(_renderer.getRenderSize().height()); - + setZoomFactor(std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f); + setZoomCenterWorld(_renderer.getWorldBounds().center()); } endChangeZoomRectangleWorld(); } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e980acdcb..8648fbda8 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -81,6 +81,12 @@ class CORE_EXPORT Navigator2D : public QObject */ float getZoomFactor() const; + /** + * Set the zoom factor to \p zoomFactor + * @param zoomFactor Zoom factor + */ + void setZoomFactor(float zoomFactor); + /** * Get the zoom percentage * @return Zoom percentage @@ -269,6 +275,20 @@ class CORE_EXPORT Navigator2D : public QObject */ void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); + /** + * Signals that the zoom center in world coordinates has changed from \p previousZoomCenterWorld to \p currentZoomCenterWorld + * @param previousZoomCenterWorld Previous world zoom center + * @param currentZoomCenterWorld Current world zoom center + */ + void zoomCenterWorldChanged(const QPointF& previousZoomCenterWorld, const QPointF& currentZoomCenterWorld); + + /** + * Signals that the zoom factor has changed from \p previousZoomFactor to \p currentZoomFactor + * @param previousZoomFactor Previous zoom factor + * @param currentZoomFactor Current zoom factor + */ + void zoomFactorChanged(float previousZoomFactor, float currentZoomFactor); + private: QPointer _sourceWidget; /** Source widget for panning and zooming */ Renderer2D& _renderer; /** Reference to parent renderer */ diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index f556b8b6f..9f0ae2b49 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -12,6 +12,8 @@ #include "ManiVaultGlobals.h" +#include "actions/WidgetAction.h" + #include #include @@ -24,11 +26,14 @@ class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core { protected: - /** - * Construct a new renderer - * @param parent Pointer to the parent object + /** + * Construct with pointer to \p parent object + * @param parent Pointer to parent object */ - explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + explicit Renderer(QObject* parent = nullptr) : + QObject(parent) + { + } virtual void init() = 0; virtual void resize(QSize renderSize) = 0; diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 40e4c548e..c3f0c49c6 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -30,9 +30,8 @@ Q_OBJECT public: /** - * Construct a new two-dimensional renderer - * - * @param parent Pointer to the parent object + * Construct with pointer to \p parent object + * @param parent Pointer to parent object */ explicit Renderer2D(QObject* parent = nullptr); From 439f9cd16f4ce393fee550c08e4bd47a571e9261 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 09:34:54 +0200 Subject: [PATCH 35/89] Synchronize zoom percentage --- ManiVault/src/renderers/Navigator2D.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index c747c2281..4e3912731 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -285,6 +285,8 @@ void Navigator2D::setZoomFactor(float zoomFactor) _zoomFactor = zoomFactor; emit zoomFactorChanged(previousZoomFactor, _zoomFactor); + + setZoomPercentage(getZoomPercentage()); } float Navigator2D::getZoomPercentage() const From 6cadbfdcbb35490e858aa05d1d709a4f6eaa9dd7 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 10:24:56 +0200 Subject: [PATCH 36/89] Zoom to selection is working --- ManiVault/src/actions/NavigationAction.cpp | 1 + ManiVault/src/util/NumericalRange.h | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index f69f921c9..5026f6e09 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -64,6 +64,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); addAction(&_zoomExtentsAction); + addAction(&_zoomSelectionAction); addAction(&_zoomCenterAction); //addAction(&_zoomRegionAction); diff --git a/ManiVault/src/util/NumericalRange.h b/ManiVault/src/util/NumericalRange.h index 408c37507..95e006857 100644 --- a/ManiVault/src/util/NumericalRange.h +++ b/ManiVault/src/util/NumericalRange.h @@ -85,6 +85,7 @@ class NumericalRange : public QPair /** * Addition operator + * @param other Other range * @return Added range */ NumericalRange& operator += (const NumericalRange& other) @@ -95,11 +96,24 @@ class NumericalRange : public QPair return *this; } + /** + * Addition operator + * @param value Value to add + * @return Added range + */ + NumericalRange& operator += (float value) + { + this->first = std::min(this->first, value); + this->second = std::max(this->second, value); + + return *this; + } + /** * Equality operator * @param rhs Right-hand-side operator */ - const bool operator == (const NumericalRange& rhs) const { + bool operator == (const NumericalRange& rhs) const { return rhs.getMinimum() == getMinimum() && rhs.getMaximum() == getMaximum(); } @@ -107,7 +121,7 @@ class NumericalRange : public QPair * Inequality operator * @param rhs Right-hand-side operator */ - const bool operator != (const NumericalRange& rhs) const { + bool operator != (const NumericalRange& rhs) const { return rhs.getMinimum() != getMinimum() || rhs.getMaximum() != getMaximum(); } }; From ad763a2568c45af8defd238a792da76dfe7871a0 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 10:52:44 +0200 Subject: [PATCH 37/89] Fix cursors --- ManiVault/src/renderers/Navigator2D.cpp | 44 ++++++++++++++++++++----- ManiVault/src/renderers/Navigator2D.h | 14 +++++++- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 4e3912731..924351c08 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -150,12 +150,16 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (event->type() == QEvent::Wheel) { if (auto* wheelEvent = dynamic_cast(event)) { - constexpr auto zoomSensitivity = .1f; - - if (wheelEvent->angleDelta().x() < 0) - zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); - else - zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + changeCursor(Qt::ClosedHandCursor); + { + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + } + restoreCursor(); } } @@ -165,7 +169,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) resetView(); if (mouseEvent->buttons() == Qt::LeftButton) { - _sourceWidget->setCursor(Qt::ClosedHandCursor); + changeCursor(Qt::ClosedHandCursor); _mousePositions << mouseEvent->pos(); @@ -175,7 +179,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) } if (event->type() == QEvent::MouseButtonRelease) { - _sourceWidget->setCursor(Qt::ArrowCursor); + restoreCursor(); _mousePositions.clear(); @@ -276,7 +280,6 @@ float Navigator2D::getZoomFactor() const void Navigator2D::setZoomFactor(float zoomFactor) { - qDebug() << __FUNCTION__ << zoomFactor; if (zoomFactor == _zoomFactor) return; @@ -591,6 +594,8 @@ void Navigator2D::beginNavigation() _userHasNavigated = true; + changeCursor(Qt::OpenHandCursor); + setIsNavigating(true); emit navigationStarted(); @@ -605,6 +610,8 @@ void Navigator2D::endNavigation() qDebug() << __FUNCTION__; #endif + restoreCursor(); + setIsNavigating(false); emit navigationEnded(); @@ -620,4 +627,23 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } +void Navigator2D::changeCursor(const QCursor& cursor) const +{ + Q_ASSERT(_sourceWidget); + + if (!_sourceWidget) + return; + + _sourceWidget->setCursor(cursor); +} + +void Navigator2D::restoreCursor() const +{ + Q_ASSERT(_sourceWidget); + + if (!_sourceWidget) + return; + + _sourceWidget->setCursor(_cachedCursor); +} } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 8648fbda8..115748bd6 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -224,6 +224,17 @@ class CORE_EXPORT Navigator2D : public QObject /** End changing the zoom rectangle in world coordinates */ void endChangeZoomRectangleWorld(); +protected: // Cursor + + /** + * Change the cursor to \p cursor + * @param cursor Cursor + */ + void changeCursor(const QCursor& cursor) const; + + /** Restore cached cursor */ + void restoreCursor() const; + signals: /** Signals that panning has started */ @@ -303,7 +314,8 @@ class CORE_EXPORT Navigator2D : public QObject float _zoomRectangleMargin; /** Zoom rectangle margin */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ - gui::NavigationAction _navigationAction; /** Navigation group action */ + gui::NavigationAction _navigationAction; /** Navigation group action */ + QCursor _cachedCursor; /** Cached cursor */ }; } From 70d9a2fa0ab35778c8632d32f0954602fca90438 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 12:17:32 +0200 Subject: [PATCH 38/89] Add icon modifier support --- ManiVault/src/actions/NavigationAction.cpp | 13 +++++----- ManiVault/src/renderers/Navigator2D.cpp | 22 ++++++++-------- ManiVault/src/renderers/Navigator2D.h | 2 +- ManiVault/src/util/StyledIcon.cpp | 30 +++++++++++++++++++++- ManiVault/src/util/StyledIcon.h | 22 +++++++++++----- ManiVault/src/util/StyledIconCommon.h | 1 + ManiVault/src/util/StyledIconEngine.cpp | 18 +++++++++++++ 7 files changed, 82 insertions(+), 26 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 5026f6e09..146112ef2 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -34,15 +34,16 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomOutAction.setToolTip("Zoom out by 10% (-)"); _zoomPercentageAction.setToolTip("Zoom in/out (+)"); _zoomInAction.setToolTip("Zoom in by 10%"); - _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); - _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); + _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (o)"); + _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (b)"); _zoomPercentageAction.setOverrideSizeHint(QSize(300, 0)); _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); - _zoomExtentsAction.setIconByName("compress"); - _zoomSelectionAction.setIconByName("search-location"); + _zoomExtentsAction.setIconByName("expand"); + + _zoomSelectionAction.setIcon(StyledIcon("expand").withModifier("mouse-pointer")); _zoomSelectionAction.setEnabled(false); _zoomPercentageAction.setSuffix("%"); @@ -63,8 +64,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); - addAction(&_zoomExtentsAction); - addAction(&_zoomSelectionAction); + addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); + addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); addAction(&_zoomCenterAction); //addAction(&_zoomRegionAction); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 924351c08..38f389570 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -150,16 +150,12 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (event->type() == QEvent::Wheel) { if (auto* wheelEvent = dynamic_cast(event)) { - changeCursor(Qt::ClosedHandCursor); - { - constexpr auto zoomSensitivity = .1f; - - if (wheelEvent->angleDelta().x() < 0) - zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); - else - zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); - } - restoreCursor(); + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); } } @@ -610,7 +606,7 @@ void Navigator2D::endNavigation() qDebug() << __FUNCTION__; #endif - restoreCursor(); + changeCursor(Qt::ArrowCursor); setIsNavigating(false); @@ -627,13 +623,15 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } -void Navigator2D::changeCursor(const QCursor& cursor) const +void Navigator2D::changeCursor(const QCursor& cursor) { Q_ASSERT(_sourceWidget); if (!_sourceWidget) return; + _cachedCursor = _sourceWidget->cursor(); + _sourceWidget->setCursor(cursor); } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 115748bd6..c7e357f35 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -230,7 +230,7 @@ class CORE_EXPORT Navigator2D : public QObject * Change the cursor to \p cursor * @param cursor Cursor */ - void changeCursor(const QCursor& cursor) const; + void changeCursor(const QCursor& cursor); /** Restore cached cursor */ void restoreCursor() const; diff --git a/ManiVault/src/util/StyledIcon.cpp b/ManiVault/src/util/StyledIcon.cpp index 07477c28f..348cb15cc 100644 --- a/ManiVault/src/util/StyledIcon.cpp +++ b/ManiVault/src/util/StyledIcon.cpp @@ -52,7 +52,7 @@ QMap StyledIcon::pixmaps = {} QVector StyledIcon::iconFontPreferenceGroups = { { "FontAwesomeRegular", "FontAwesomeSolid", "FontAwesomeBrandsRegular" } }; QMap> StyledIcon::iconFontVersions = {}; -StyledIcon::StyledIcon(const QString& iconName /*= ""*/, const QString& iconFontName /*= defaultIconFontName*/, const Version& iconFontVersion /*= defaultIconFontVersion*/, QWidget* parent /*= nullptr*/) +StyledIcon::StyledIcon(const QString& iconName /*= ""*/, const QString& iconFontName /*= defaultIconFontName*/, const Version& iconFontVersion /*= defaultIconFontVersion*/) { if (!iconName.isEmpty() && !iconFontName.isEmpty()) set(iconName, iconFontName, iconFontVersion); @@ -260,6 +260,34 @@ StyledIcon StyledIcon::withMode(const StyledIconMode& mode) return *this; } +StyledIcon StyledIcon::withModifier(const QString& iconName, const QString& iconFontName, const Version& iconFontVersion) +{ + try + { + if (iconName.isEmpty() || iconFontName.isEmpty()) { + return *this; + } + + _modifierIconName = iconName; + _modifierIconFontName = iconFontName; + _modifierIconFontVersion = iconFontVersion; + _iconSettings._modifierSha = generateSha(_modifierIconName, _modifierIconFontName, _modifierIconFontVersion); + + const auto iconFontResourcePath = getIconFontResourcePath(_modifierIconFontName, _modifierIconFontVersion); + + if (!QFile::exists(iconFontResourcePath)) + throw std::runtime_error(QString("Font resource not found: %1").arg(iconFontResourcePath).toStdString()); + + pixmaps[_iconSettings._modifierSha] = createIconPixmap(_modifierIconName, _modifierIconFontName, _modifierIconFontVersion, _iconSettings._mode == StyledIconMode::FixedColor ? Qt::black : _iconSettings._fixedColor); + } + catch (std::exception& e) + { + qWarning() << "Unable to set icon modifier: " << e.what(); + } + + return *this; +} + QFont StyledIcon::getIconFont(std::int32_t fontPointSize /*= -1*/, const QString& iconFontName /*= defaultIconFontName*/, const Version& iconFontVersion /*= defaultIconFontVersion*/) { const auto iconFontResourceName = getIconFontResourceName(iconFontName, iconFontVersion); diff --git a/ManiVault/src/util/StyledIcon.h b/ManiVault/src/util/StyledIcon.h index f31be3f16..5b0144335 100644 --- a/ManiVault/src/util/StyledIcon.h +++ b/ManiVault/src/util/StyledIcon.h @@ -39,9 +39,8 @@ class CORE_EXPORT StyledIcon * @param iconName Name of the icon * @param iconFontName Name of the icon font * @param iconFontVersion Version of the icon font - * @param parent Pointer to parent object (maybe nullptr) */ - explicit StyledIcon(const QString& iconName = "", const QString& iconFontName = defaultIconFontName, const Version& iconFontVersion = defaultIconFontVersion, QWidget* parent = nullptr); + explicit StyledIcon(const QString& iconName = "", const QString& iconFontName = defaultIconFontName, const Version& iconFontVersion = defaultIconFontVersion); /** * Copy construct from \p other styled icon @@ -150,6 +149,14 @@ class CORE_EXPORT StyledIcon */ StyledIcon withMode(const StyledIconMode& mode); + /** + * Set icon modifier + * @param iconName Name of the modifier icon + * @param iconFontName Name of the modifier icon font + * @param iconFontVersion Version of the modifier icon font + */ + StyledIcon withModifier(const QString& iconName, const QString& iconFontName = defaultIconFontName, const Version& iconFontVersion = defaultIconFontVersion); + /** * Get icon font for \p iconFontName at \p iconFontVersion * @param fontPointSize Point size of the font @@ -260,10 +267,13 @@ class CORE_EXPORT StyledIcon static void updateIconFontVersions(const QString& iconFontName); private: - QString _iconName; /** Name of the icon */ - QString _iconFontName; /** Name of the icon font */ - Version _iconFontVersion; /** Version of the icon font */ - StyledIconSettings _iconSettings; /** Icon settings */ + QString _iconName; /** Name of the icon */ + QString _iconFontName; /** Name of the icon font */ + Version _iconFontVersion; /** Version of the icon font */ + StyledIconSettings _iconSettings; /** Icon settings */ + QString _modifierIconName; /** Name of the modifier icon */ + QString _modifierIconFontName; /** Name of the modifier icon font */ + Version _modifierIconFontVersion; /** Version of the modifier icon font */ protected: static QMap fontMetadata; /** Font-specific metadata */ diff --git a/ManiVault/src/util/StyledIconCommon.h b/ManiVault/src/util/StyledIconCommon.h index 20aa6db61..a9fe8881f 100644 --- a/ManiVault/src/util/StyledIconCommon.h +++ b/ManiVault/src/util/StyledIconCommon.h @@ -42,6 +42,7 @@ struct StyledIconSettings QPalette::ColorRole getColorRoleForCurrentTheme() const; QString _sha; /** Icon key */ + QString _modifierSha; /** Modifier icon key */ StyledIconMode _mode; /** Styled icon coloring mode */ QPalette::ColorGroup _colorGroupLightTheme; /** Color group for light theme */ QPalette::ColorGroup _colorGroupDarkTheme; /** Color group for dark theme */ diff --git a/ManiVault/src/util/StyledIconEngine.cpp b/ManiVault/src/util/StyledIconEngine.cpp index f083edf6d..a0eb19748 100644 --- a/ManiVault/src/util/StyledIconEngine.cpp +++ b/ManiVault/src/util/StyledIconEngine.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace mv::util { @@ -52,6 +53,18 @@ QPixmap StyledIconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::Sta break; } } + + if (!_iconSettings._modifierSha.isEmpty()) { + const auto recolorColor = qApp->palette().color(static_cast(mode), _iconSettings.getColorRoleForCurrentTheme()); + const auto modifierIconPixmap = recolorPixmap(StyledIcon::pixmaps[_iconSettings._modifierSha], size / 2, recolorColor); + + QPainter modifierIconPixmapPainter(&result); + + modifierIconPixmapPainter.setRenderHint(QPainter::Antialiasing); + modifierIconPixmapPainter.setRenderHint(QPainter::SmoothPixmapTransform, true); + modifierIconPixmapPainter.setRenderHint(QPainter::LosslessImageRendering, true); + modifierIconPixmapPainter.drawPixmap(QPoint(size.width() / 2, size.height() / 2), modifierIconPixmap); + } auto& badgeParameters = _iconSettings._badgeParameters; @@ -124,6 +137,11 @@ QPixmap StyledIconEngine::recolorPixmap(const QPixmap& pixmap, const QSize& size coloredPixmap.fill(Qt::transparent); QPainter painter(&coloredPixmap); + + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::LosslessImageRendering, true); + painter.drawPixmap(0, 0, size.width(), size.height(), pixmap); painter.setCompositionMode(QPainter::CompositionMode_SourceIn); From 1ce5e4c0437f5e10ab0521ae97f626c8899f0dd0 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 13:33:10 +0200 Subject: [PATCH 39/89] Added zoom margins back in --- ManiVault/src/actions/NavigationAction.cpp | 20 +++----------------- ManiVault/src/renderers/DensityRenderer.cpp | 13 ++++++++++--- ManiVault/src/renderers/DensityRenderer.h | 3 +++ ManiVault/src/renderers/Navigator2D.cpp | 9 +++++---- ManiVault/src/renderers/Navigator2D.h | 5 +++-- ManiVault/src/renderers/PointRenderer.cpp | 10 +++++++++- ManiVault/src/renderers/PointRenderer.h | 3 +++ ManiVault/src/renderers/Renderer2D.cpp | 10 ++++++++-- ManiVault/src/renderers/Renderer2D.h | 5 +++++ 9 files changed, 49 insertions(+), 29 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 146112ef2..dea3ca25b 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -41,9 +41,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); - _zoomExtentsAction.setIconByName("expand"); + _zoomExtentsAction.setIconByName("compress"); - _zoomSelectionAction.setIcon(StyledIcon("expand").withModifier("mouse-pointer")); + _zoomSelectionAction.setIcon(StyledIcon("compress").withModifier("mouse-pointer")); _zoomSelectionAction.setEnabled(false); _zoomPercentageAction.setSuffix("%"); @@ -64,23 +64,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); + addAction(&_zoomCenterAction); addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); - addAction(&_zoomCenterAction); - - //addAction(&_zoomRegionAction); - - // connect(&_zoomCenterXAction, &DecimalAction::valueChanged, this, [this](float value) -> void { - // qDebug() << "Zoom center x: " << value; - //}); - - // connect(&_zoomCenterYAction, &DecimalAction::valueChanged, this, [this](float value) -> void { - // qDebug() << "Zoom center y: " << value; - //}); - - connect(&_zoomFactorAction, &DecimalAction::valueChanged, this, [this](float value) -> void { - qDebug() << "Zoom factor: " << value; - }); } void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 5498f9cb3..9e702914d 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -35,11 +35,18 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); - // .0f, -_densityComputation.getDensityTextureSize().height() + updateQuad(); +} - setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); +QRectF DensityRenderer::computeWorldBounds() const +{ + const auto textureSize = static_cast(_densityComputation.getDensityTextureSize().height()); + const auto marginX = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); + const auto marginY = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); + const auto margin = std::max(marginX, marginY); + const auto margins = QMarginsF(margin, margin, margin, margin); - updateQuad(); + return QRectF(QPointF(),_densityComputation.getDensityTextureSize()).marginsAdded(margins); } void DensityRenderer::setRenderMode(RenderMode renderMode) diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 09f4c9e51..b42b91a52 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -39,6 +39,9 @@ class CORE_EXPORT DensityRenderer : public Renderer2D */ void setDataBounds(const QRectF& dataBounds) override; + /** Update the world bounds */ + QRectF computeWorldBounds() const override; + void setRenderMode(RenderMode renderMode); void setData(const std::vector* data); void setWeights(const std::vector* weights); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 38f389570..ae200f867 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -26,7 +26,8 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleMargin(0.f), + _zoomMarginScreen(100.f), + _zoomMarginWorld(.0f), _userHasNavigated(), _navigationAction(this, "Navigation") { @@ -264,9 +265,9 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } -float Navigator2D::getZoomRectangleMargin() const +float Navigator2D::getZoomMarginScreen() const { - return _zoomRectangleMargin; + return _zoomMarginScreen; } float Navigator2D::getZoomFactor() const @@ -450,7 +451,7 @@ void Navigator2D::resetView(bool force /*= false*/) const auto zoomFactorX = _renderer.getWorldBounds().width() / static_cast(_renderer.getRenderSize().width()); const auto zoomFactorY = _renderer.getWorldBounds().height() / static_cast(_renderer.getRenderSize().height()); - setZoomFactor(std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f); + setZoomFactor(std::max(zoomFactorX, zoomFactorY)); setZoomCenterWorld(_renderer.getWorldBounds().center()); } endChangeZoomRectangleWorld(); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index c7e357f35..75cfec858 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -73,7 +73,7 @@ class CORE_EXPORT Navigator2D : public QObject * Get the zoom rectangle margin * @return Zoom rectangle margin */ - float getZoomRectangleMargin() const; + float getZoomMarginScreen() const; /** * Get the zoom factor @@ -311,7 +311,8 @@ class CORE_EXPORT Navigator2D : public QObject bool _isZooming; /** Zooming flag */ float _zoomFactor; /** Zoom factor */ QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ + float _zoomMarginScreen; /** Zoom margin in screen coordinates */ + float _zoomMarginWorld; /** Zoom margin in world coordinates */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ gui::NavigationAction _navigationAction; /** Navigation group action */ diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 7da14ad71..af48a5990 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -259,8 +259,16 @@ namespace mv void PointRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); + } + + QRectF PointRenderer::computeWorldBounds() const + { + const auto marginX = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().height()) / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); + const auto marginY = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().width()) / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); + const auto margin = std::max(marginX, marginY); + const auto margins = QMarginsF(margin, margin, margin, margin); - setWorldBounds(dataBounds); + return getDataBounds().marginsAdded(margins); } void PointRenderer::setData(const std::vector& positions) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 3ff62840b..8082da470 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -141,6 +141,9 @@ namespace mv */ void setDataBounds(const QRectF& dataBounds) override; + /** Update the world bounds */ + QRectF computeWorldBounds() const override; + void setData(const std::vector& points); void setHighlights(const std::vector& highlights, const std::int32_t& numSelectedPoints); void setFocusHighlights(const std::vector& focusHighlights, const std::int32_t& numberOfFocusHighlights); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 5994c734c..3fe3c2682 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -22,6 +22,10 @@ Renderer2D::Renderer2D(QObject* parent) : void Renderer2D::resize(QSize renderSize) { _renderSize = renderSize; + + setWorldBounds(computeWorldBounds()); + + getNavigator().resetView(); } QSize Renderer2D::getRenderSize() const @@ -68,14 +72,16 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) qDebug() << __FUNCTION__ << dataBounds; #endif - const auto previousDataBounds = _dataBounds; + const auto previousDataBounds = _dataBounds; if (dataBounds == _dataBounds) return; _dataBounds = dataBounds; - + emit dataBoundsChanged(previousDataBounds, _dataBounds); + + setWorldBounds(computeWorldBounds()); } QRectF Renderer2D::getWorldBounds() const diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index c3f0c49c6..6fb77d268 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -164,6 +164,11 @@ Q_OBJECT /** Update the model-view-projection matrix */ void updateModelViewProjectionMatrix(); +protected: + + /** Compute the world bounds */ + virtual QRectF computeWorldBounds() const = 0; + signals: /** From 66ec4e6a8eeba5a1f429b7e3cfb67055d1b1eb85 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 13:41:08 +0200 Subject: [PATCH 40/89] Change how the default size is overridden --- ManiVault/src/actions/WidgetActionWidget.cpp | 2 +- ManiVault/src/renderers/Navigator2D.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ManiVault/src/actions/WidgetActionWidget.cpp b/ManiVault/src/actions/WidgetActionWidget.cpp index 46efb24fe..a5732c123 100644 --- a/ManiVault/src/actions/WidgetActionWidget.cpp +++ b/ManiVault/src/actions/WidgetActionWidget.cpp @@ -30,7 +30,7 @@ QSize WidgetActionWidget::sizeHint() const return popupSizeHint; } - if (action->getOverrideSizeHint().width() > 0 || action->getOverrideSizeHint().height() > 0) + if (action->getOverrideSizeHint().width() > 0 && action->getOverrideSizeHint().height() > 0) return action->getOverrideSizeHint(); return QWidget::sizeHint(); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index ae200f867..df1da9744 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -71,8 +71,6 @@ void Navigator2D::initialize(QWidget* sourceWidget) connect(this, &Navigator2D::zoomCenterWorldChanged, this, [this](const QPointF& previousZoomCenterWorld, const QPointF& currentZoomCenterWorld) -> void { _navigationAction.getZoomCenterAction().set(currentZoomCenterWorld); - - qDebug() << currentZoomCenterWorld; }); connect(this, &Navigator2D::zoomFactorChanged, this, [this](float previousZoomFactor, float currentZoomFactor) -> void { @@ -87,10 +85,6 @@ void Navigator2D::initialize(QWidget* sourceWidget) endChangeZoomRectangleWorld(); }); - // connect(&_navigationAction.getZoomCenterYAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { - // setZoomCenterWorld(QPointF(_zoomCenterWorld.x(), value)); - //}); - connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { setZoomPercentage(getZoomPercentage() + 10.f); }); From dba3127ab4fd3b2ba97bdcd3887e636b8e3766c7 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 15:54:46 +0200 Subject: [PATCH 41/89] Work navigation freezing --- ManiVault/src/actions/NavigationAction.cpp | 23 +++ ManiVault/src/actions/NavigationAction.h | 3 + ManiVault/src/renderers/Navigator2D.cpp | 203 ++++++++++++++++----- ManiVault/src/renderers/Navigator2D.h | 72 ++++++-- ManiVault/src/util/StyledIconEngine.cpp | 7 +- 5 files changed, 246 insertions(+), 62 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index dea3ca25b..525b7862f 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -25,6 +25,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction(this, "Zoom All"), _zoomSelectionAction(this, "Zoom Selection"), _zoomRegionAction(this, "Zoom Region"), + _freezeNavigation(this, "Freeze Navigation"), _zoomRectangleAction(this, "Zoom Rectangle"), _zoomCenterAction(this, "Zoom Center"), _zoomFactorAction(this, "Zoom Factor") @@ -36,6 +37,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomInAction.setToolTip("Zoom in by 10%"); _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (o)"); _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (b)"); + _zoomRegionAction.setToolTip("Zoom to a picked region"); + _freezeNavigation.setToolTip("Freeze the navigation"); _zoomPercentageAction.setOverrideSizeHint(QSize(300, 0)); @@ -46,6 +49,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomSelectionAction.setIcon(StyledIcon("compress").withModifier("mouse-pointer")); _zoomSelectionAction.setEnabled(false); + _zoomRegionAction.setIcon(StyledIcon("compress").withModifier("search")); + _zoomPercentageAction.setSuffix("%"); _zoomPercentageAction.setUpdateDuringDrag(false); @@ -67,6 +72,24 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomCenterAction); addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); + addAction(&_zoomRegionAction, gui::TriggerAction::Icon); + addAction(&_freezeNavigation, gui::ToggleAction::WidgetFlag::CheckBox); + + const auto updateReadOnly = [this]() -> void { + const auto notFrozen = _freezeNavigation.isChecked(); + + _zoomOutAction.setEnabled(!notFrozen); + _zoomPercentageAction.setEnabled(!notFrozen); + _zoomInAction.setEnabled(!notFrozen); + _zoomExtentsAction.setEnabled(!notFrozen); + _zoomSelectionAction.setEnabled(!notFrozen); + _zoomRegionAction.setEnabled(!notFrozen); + _zoomCenterAction.setEnabled(!notFrozen); + }; + + updateReadOnly(); + + connect(&_freezeNavigation, &ToggleAction::toggled, this, updateReadOnly); } void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index d7164d57c..6e399d7fd 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction& getZoomExtentsAction() { return _zoomExtentsAction; } TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } + ToggleAction& getFreezeNavigation() { return _freezeNavigation; } DecimalRectangleAction& getZoomRectangleAction() { return _zoomRectangleAction; } DecimalPointAction& getZoomCenterAction() { return _zoomCenterAction; } DecimalAction& getZoomFactorAction() { return _zoomFactorAction; } @@ -79,6 +81,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction _zoomExtentsAction; /** Zoom extents action */ TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ TriggerAction _zoomRegionAction; /** Zoom to region action */ + ToggleAction _freezeNavigation; /** Freeze navigation action */ DecimalRectangleAction _zoomRectangleAction; /** Rectangle action for setting the current zoom bounds */ DecimalPointAction _zoomCenterAction; /** Zoom center action */ DecimalAction _zoomFactorAction; /** Zoom factor action */ diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index df1da9744..5ee831a7b 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -6,6 +6,8 @@ #include "Renderer2D.h" +#include + #ifdef _DEBUG //#define NAVIGATOR_2D_VERBOSE #endif @@ -17,6 +19,25 @@ using namespace mv::gui; namespace mv { +Navigator2D::ZoomOverlayWidget::ZoomOverlayWidget(Navigator2D& navigator, QWidget* targetWidget): + gui::OverlayWidget(targetWidget), + _navigator(navigator) +{ +} + +void Navigator2D::ZoomOverlayWidget::paintEvent(QPaintEvent* event) +{ + if (_navigator.getZoomRegionRectangle().isValid()) { + QPainter painter(this); + + painter.setBrush(QColor(0, 0, 0, 50)); + painter.setPen(QPen(QColor(0, 0, 0, 150), 2)); + painter.drawRect(_navigator.getZoomRegionRectangle()); + + event->accept(); + } +} + Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : QObject(parent), _renderer(renderer), @@ -28,6 +49,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _zoomFactor(1.0f), _zoomMarginScreen(100.f), _zoomMarginWorld(.0f), + _zoomRegionInProgress(false), _userHasNavigated(), _navigationAction(this, "Navigation") { @@ -107,12 +129,18 @@ void Navigator2D::initialize(QWidget* sourceWidget) resetView(); }); + connect(&_navigationAction.getZoomRegionAction(), &TriggerAction::triggered, this, [this]() -> void { + beginZoomToRegion(); + }); + setZoomFactor(_navigationAction.getZoomFactorAction().getValue()); _sourceWidget->addAction(&_navigationAction.getZoomInAction()); _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); + _zoomOverlayWidget = new ZoomOverlayWidget(*this, _sourceWidget); + _initialized = true; } @@ -121,6 +149,9 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (!_initialized || !_enabled) return false; + if (getNavigationAction().getFreezeNavigation().isChecked()) + return QObject::eventFilter(watched, event); + if (event->type() == QEvent::KeyPress) { if (const auto* keyEvent = dynamic_cast(event)) { if (keyEvent->key() == Qt::Key_Alt) { @@ -142,51 +173,93 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) } if (isNavigating()) { + if (_zoomRegionInProgress) { + const auto updateZoomRegion = [this]() -> void { + int x1 = std::min(_zoomRegionPoints[0].x(), _zoomRegionPoints[1].x()); + int y1 = std::min(_zoomRegionPoints[0].y(), _zoomRegionPoints[1].y()); + int x2 = std::max(_zoomRegionPoints[0].x(), _zoomRegionPoints[1].x()); + int y2 = std::max(_zoomRegionPoints[0].y(), _zoomRegionPoints[1].y()); + + _zoomRegionRectangle = QRect(x1, y1, x2 - x1, y2 - y1); + + if (_zoomOverlayWidget) + _zoomOverlayWidget->update(); + }; + + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->buttons() == Qt::LeftButton) { + _zoomRegionPoints << mouseEvent->pos() << mouseEvent->pos(); + + updateZoomRegion(); + } + } + } + + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->buttons() == Qt::LeftButton) { + if (_zoomRegionPoints.size() == 2) + _zoomRegionPoints[1] = mouseEvent->pos(); - if (event->type() == QEvent::Wheel) { - if (auto* wheelEvent = dynamic_cast(event)) { - constexpr auto zoomSensitivity = .1f; + updateZoomRegion(); + } + } + } - if (wheelEvent->angleDelta().x() < 0) - zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); - else - zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + if (event->type() == QEvent::MouseButtonRelease) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->button() == Qt::LeftButton) { + endZoomToRegion(); + } + } + } + } else { + if (event->type() == QEvent::Wheel) { + if (auto* wheelEvent = dynamic_cast(event)) { + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + } } - } - if (event->type() == QEvent::MouseButtonPress) { - if (const auto* mouseEvent = dynamic_cast(event)) { - if (mouseEvent->button() == Qt::MiddleButton) - resetView(); + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->button() == Qt::MiddleButton) + resetView(); - if (mouseEvent->buttons() == Qt::LeftButton) { - changeCursor(Qt::ClosedHandCursor); + if (mouseEvent->buttons() == Qt::LeftButton) { + changeCursor(Qt::ClosedHandCursor); - _mousePositions << mouseEvent->pos(); + _mousePositions << mouseEvent->pos(); - _sourceWidget->update(); + _sourceWidget->update(); + } } } - } - if (event->type() == QEvent::MouseButtonRelease) { - restoreCursor(); + if (event->type() == QEvent::MouseButtonRelease) { + restoreCursor(); - _mousePositions.clear(); + _mousePositions.clear(); - _sourceWidget->update(); - } + _sourceWidget->update(); + } - if (event->type() == QEvent::MouseMove) { - if (const auto* mouseEvent = dynamic_cast(event)) { - _mousePositions << mouseEvent->pos(); + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) { + _mousePositions << mouseEvent->pos(); - 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; + 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; - panBy(-panVector); + panBy(-panVector); + } } } } @@ -240,6 +313,9 @@ QRectF Navigator2D::getZoomRectangleWorld() const void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + #ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << zoomRectangleWorld; #endif @@ -271,6 +347,9 @@ float Navigator2D::getZoomFactor() const void Navigator2D::setZoomFactor(float zoomFactor) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + if (zoomFactor == _zoomFactor) return; @@ -300,11 +379,14 @@ float Navigator2D::getZoomPercentage() const void Navigator2D::setZoomPercentage(float zoomPercentage) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + beginZooming(); { beginChangeZoomRectangleWorld(); { - if (zoomPercentage < 0.05f) + if (zoomPercentage < 0.01f) return; const auto zoomPercentageNormalized = .01f * zoomPercentage; @@ -347,12 +429,11 @@ const gui::NavigationAction& Navigator2D::getNavigationAction() const void Navigator2D::zoomAround(const QPoint& center, float factor) { - if (!_initialized) + if (getNavigationAction().getFreezeNavigation().isChecked()) return; -#ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << center << factor; -#endif + if (!_initialized) + return; beginZooming(); { @@ -370,12 +451,11 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) { - if (!_initialized) + if (getNavigationAction().getFreezeNavigation().isChecked()) return; -#ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << zoomRectangle; -#endif + if (!_initialized) + return; beginZooming(); { @@ -390,12 +470,11 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) void Navigator2D::panBy(const QPointF& delta) { - if (!_initialized) + if (getNavigationAction().getFreezeNavigation().isChecked()) return; -#ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << delta; -#endif + if (!_initialized) + return; beginPanning(); { @@ -413,6 +492,9 @@ void Navigator2D::panBy(const QPointF& delta) void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + if (zoomCenterWorld == _zoomCenterWorld) return; @@ -434,6 +516,9 @@ void Navigator2D::resetView(bool force /*= false*/) if (!force && hasUserNavigated()) return; + if (_navigationAction.getFreezeNavigation().isChecked()) + return; + #ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << force; #endif @@ -475,6 +560,11 @@ bool Navigator2D::hasUserNavigated() const return _userHasNavigated; } +QRect Navigator2D::getZoomRegionRectangle() const +{ + return _zoomRegionRectangle; +} + void Navigator2D::setIsPanning(bool isPanning) { if (!_initialized) @@ -618,6 +708,33 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } +void Navigator2D::beginZoomToRegion() +{ + setIsNavigating(true); + + _zoomRegionInProgress = true; + _zoomRegionRectangle = QRect(); + + if (_zoomOverlayWidget) + _zoomOverlayWidget->update(); +} + +void Navigator2D::endZoomToRegion() +{ + _zoomRegionPoints.clear(); + + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), _zoomRegionRectangle.topLeft()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), _zoomRegionRectangle.bottomRight()).toPointF(); + + setZoomRectangleWorld(QRectF(p1, p2)); + + _zoomRegionInProgress = false; + _zoomRegionRectangle = QRect(); + + if (_zoomOverlayWidget) + _zoomOverlayWidget->update(); +} + void Navigator2D::changeCursor(const QCursor& cursor) { Q_ASSERT(_sourceWidget); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 75cfec858..1c5add717 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -26,6 +26,30 @@ class CORE_EXPORT Navigator2D : public QObject { Q_OBJECT +public: + + /** For drawing the zoom region */ + class ZoomOverlayWidget : public gui::OverlayWidget + { + public: + + /** + * Construct a new zoom overlay widget + * @param navigator Reference to the navigator + * @param targetWidget Pointer to the target widget + */ + ZoomOverlayWidget(Navigator2D& navigator, QWidget* targetWidget); + + /** + * Override the paint event to draw the zoom regio rectangle + * @param event + */ + void paintEvent(QPaintEvent* event) override; + + private: + Navigator2D& _navigator; /** Reference to the navigator */ + }; + public: /** @@ -180,6 +204,12 @@ class CORE_EXPORT Navigator2D : public QObject */ bool hasUserNavigated() const; + /** + * Get the zoom region rectangle in screen coordinates + * @return Zoom region rectangle in screen coordinates + */ + QRect getZoomRegionRectangle() const; + protected: // Navigation /** @@ -224,6 +254,12 @@ class CORE_EXPORT Navigator2D : public QObject /** End changing the zoom rectangle in world coordinates */ void endChangeZoomRectangleWorld(); + /** Begin zooming to a region */ + void beginZoomToRegion(); + + /** End zooming to a region */ + void endZoomToRegion(); + protected: // Cursor /** @@ -301,22 +337,26 @@ class CORE_EXPORT Navigator2D : public QObject void zoomFactorChanged(float previousZoomFactor, float currentZoomFactor); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _enabled; /** Enabled flag */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ - float _zoomMarginScreen; /** Zoom margin in screen coordinates */ - float _zoomMarginWorld; /** Zoom margin in world coordinates */ - QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ - bool _userHasNavigated; /** Boolean determining whether the user has navigated */ - gui::NavigationAction _navigationAction; /** Navigation group action */ - QCursor _cachedCursor; /** Cached cursor */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ + float _zoomMarginScreen; /** Zoom margin in screen coordinates */ + float _zoomMarginWorld; /** Zoom margin in world coordinates */ + QVector _zoomRegionPoints; /** Zoom region points */ + QRect _zoomRegionRectangle; /** Zoom region rectangle */ + bool _zoomRegionInProgress; /** Zoom region in progress flag */ + QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + gui::NavigationAction _navigationAction; /** Navigation group action */ + QCursor _cachedCursor; /** Cached cursor */ + QPointer _zoomOverlayWidget; /** Zoom overlay widget */ }; } diff --git a/ManiVault/src/util/StyledIconEngine.cpp b/ManiVault/src/util/StyledIconEngine.cpp index a0eb19748..4291fe666 100644 --- a/ManiVault/src/util/StyledIconEngine.cpp +++ b/ManiVault/src/util/StyledIconEngine.cpp @@ -55,15 +55,16 @@ QPixmap StyledIconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::Sta } if (!_iconSettings._modifierSha.isEmpty()) { - const auto recolorColor = qApp->palette().color(static_cast(mode), _iconSettings.getColorRoleForCurrentTheme()); - const auto modifierIconPixmap = recolorPixmap(StyledIcon::pixmaps[_iconSettings._modifierSha], size / 2, recolorColor); + const auto recolorColor = qApp->palette().color(static_cast(mode), _iconSettings.getColorRoleForCurrentTheme()); + const auto scaledModifierIconPixmap = StyledIcon::pixmaps[_iconSettings._modifierSha].scaled(size / 2, Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation); + const auto recoloredModifierIconPixmap = recolorPixmap(scaledModifierIconPixmap, size / 2, recolorColor); QPainter modifierIconPixmapPainter(&result); modifierIconPixmapPainter.setRenderHint(QPainter::Antialiasing); modifierIconPixmapPainter.setRenderHint(QPainter::SmoothPixmapTransform, true); modifierIconPixmapPainter.setRenderHint(QPainter::LosslessImageRendering, true); - modifierIconPixmapPainter.drawPixmap(QPoint(size.width() / 2, size.height() / 2), modifierIconPixmap); + modifierIconPixmapPainter.drawPixmap(QPointF(std::round(size.width() / 2.f), std::round(size.height() / 2.f)), recoloredModifierIconPixmap); } auto& badgeParameters = _iconSettings._badgeParameters; From 78fe6cd6699de26b5c988a11e05c134f64aa11ba Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 7 Apr 2025 13:55:19 +0200 Subject: [PATCH 42/89] Align density renderer with point renderer and fix shortcuts --- ManiVault/src/actions/NavigationAction.cpp | 3 +- ManiVault/src/renderers/DensityRenderer.cpp | 42 ++++++++++++--------- ManiVault/src/renderers/DensityRenderer.h | 6 +++ ManiVault/src/renderers/Navigator2D.cpp | 2 + ManiVault/src/renderers/Renderer2D.cpp | 20 +++++++++- ManiVault/src/renderers/Renderer2D.h | 27 ++++++++++--- 6 files changed, 75 insertions(+), 25 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 525b7862f..2d076efe4 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -97,7 +97,8 @@ void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) _zoomOutAction.setShortcut(shortcutsEnabled ? QKeySequence("-") : QKeySequence()); _zoomInAction.setShortcut(shortcutsEnabled ? QKeySequence("+") : QKeySequence()); _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("O") : QKeySequence()); - //_zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("d") : QKeySequence()); + _zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("H") : QKeySequence()); + _zoomRegionAction.setShortcut(shortcutsEnabled ? QKeySequence("F") : QKeySequence()); } void NavigationAction::fromVariantMap(const QVariantMap& variantMap) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 9e702914d..d6e3066e4 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -33,20 +33,24 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); - _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); - updateQuad(); } -QRectF DensityRenderer::computeWorldBounds() const +QRectF DensityRenderer::computeWorldBounds() const { - const auto textureSize = static_cast(_densityComputation.getDensityTextureSize().height()); - const auto marginX = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); - const auto marginY = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); - const auto margin = std::max(marginX, marginY); - const auto margins = QMarginsF(margin, margin, margin, margin); + const auto squareSize = std::max(getDataBounds().width(), getDataBounds().height()); + const auto squareDataBounds = QRectF(getDataBounds().center() - QPointF(squareSize / 2.f, squareSize / 2.f), QSizeF(squareSize, squareSize)); + const auto marginX = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().width()) / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); + const auto marginY = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().height()) / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); + const auto margin = std::max(marginX, marginY); + const auto margins = QMarginsF(margin, margin, margin, margin); + + return squareDataBounds.marginsAdded(margins); +} - return QRectF(QPointF(),_densityComputation.getDensityTextureSize()).marginsAdded(margins); +void DensityRenderer::setDensityComputationDataBounds(const QRectF& bounds) +{ + _densityComputation.setBounds(bounds.left(), bounds.right(), bounds.bottom(), bounds.top()); } void DensityRenderer::setRenderMode(RenderMode renderMode) @@ -148,15 +152,17 @@ void DensityRenderer::destroy() void DensityRenderer::updateQuad() { - const auto textureSize = _densityComputation.getDensityTextureSize().toSizeF(); - const auto width = static_cast(textureSize.width()); - const auto height = static_cast(textureSize.height()); - + const auto worldBounds = getDataBounds();//computeWorldBounds(); + const auto left = static_cast(worldBounds.left()); + const auto right = static_cast(worldBounds.right()); + const auto top = static_cast(worldBounds.top()); + const auto bottom = static_cast(worldBounds.bottom()); + float vertices[] = { - 0.f, 0.f, 0.0f, 0.0f, - width, 0.f, 1.0f, 0.0f, - width, height, 1.0f, 1.0f, - 0.f, height, 0.0f, 1.0f + left, bottom, 0.0f, 0.0f, + right, bottom, 1.0f, 0.0f, + right, top, 1.0f, 1.0f, + left, top, 0.0f, 1.0f }; unsigned int indices[] = { @@ -190,6 +196,8 @@ void DensityRenderer::updateQuad() void DensityRenderer::drawQuad() { + updateQuad(); + glBindVertexArray(_VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index b42b91a52..dc8c629b6 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -42,6 +42,12 @@ class CORE_EXPORT DensityRenderer : public Renderer2D /** Update the world bounds */ QRectF computeWorldBounds() const override; + /** + * Set density computation data boundss + * @param bounds Density computation data bounds + */ + void setDensityComputationDataBounds(const QRectF& bounds); + void setRenderMode(RenderMode renderMode); void setData(const std::vector* data); void setWeights(const std::vector* weights); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 5ee831a7b..fc65635e3 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -138,6 +138,8 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->addAction(&_navigationAction.getZoomInAction()); _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); + _sourceWidget->addAction(&_navigationAction.getZoomSelectionAction()); + _sourceWidget->addAction(&_navigationAction.getZoomRegionAction()); _zoomOverlayWidget = new ZoomOverlayWidget(*this, _sourceWidget); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 3fe3c2682..2747585b3 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -35,14 +35,32 @@ QSize Renderer2D::getRenderSize() const Navigator2D& Renderer2D::getNavigator() { + if (_customNavigator) { + return *_customNavigator; + } + return _navigator; } const Navigator2D& Renderer2D::getNavigator() const { + if (_customNavigator) { + return *_customNavigator; + } + return _navigator; } +QPointer Renderer2D::getCustomNavigator() const +{ + return _customNavigator; +} + +void Renderer2D::setCustomNavigator(const QPointer& customNavigator) +{ + _customNavigator = customNavigator; +} + void Renderer2D::beginRender() { #ifdef RENDERER_2D_VERBOSE @@ -117,7 +135,7 @@ QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewM QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const { - const auto clipSpacePos = getProjectionMatrix() * (_navigator.getViewMatrix() * QVector4D(position, 1.0)); + const auto clipSpacePos = getProjectionMatrix() * (getNavigator().getViewMatrix() * QVector4D(position, 1.0)); return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 6fb77d268..3b62053a1 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -59,6 +59,20 @@ Q_OBJECT */ const Navigator2D& getNavigator() const; +public: + + /** + * Get custom navigator + * @return Pointer to custom navigator + */ + QPointer getCustomNavigator() const; + +/** + * Set custom navigator to \p customNavigator + * @param customNavigator Pointer to custom navigator + */ + void setCustomNavigator(const QPointer& customNavigator); + public: // Coordinate conversions /** @@ -186,12 +200,13 @@ Q_OBJECT void worldBoundsChanged(const QRectF& previousWorldBounds, const QRectF& currentWorldBounds); private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ - QRectF _dataBounds; /** Bounds of the data */ - QRectF _worldBounds; /** Bounds of the world */ - QMatrix4x4 _modelMatrix; /** Model matrix */ - QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + QPointer _customNavigator; /** Use this one in stead of Renderer2D#_navigator when set */ + QRectF _dataBounds; /** Bounds of the data */ + QRectF _worldBounds; /** Bounds of the world */ + QMatrix4x4 _modelMatrix; /** Model matrix */ + QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ friend class Navigator2D; }; From 994569b61409741c795b4c47b6d301a54f4a4ac5 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 14 Apr 2025 13:56:43 +0200 Subject: [PATCH 43/89] Some explicit default inits --- ManiVault/src/renderers/PointRenderer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 8082da470..66f272c88 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -197,7 +197,7 @@ namespace mv private: /* Point properties */ - PointSettings _pointSettings; + PointSettings _pointSettings = {}; PointEffect _pointEffect = PointEffect::Size; /** Selection visualization */ @@ -212,9 +212,9 @@ namespace mv bool _randomizedDepthEnabled = true; /* Rendering variables */ - ShaderProgram _shader; - PointArrayObject _gpuPoints; - Texture2D _colormap; /** 2D colormap, sets point color based on point position */ + ShaderProgram _shader = {}; + PointArrayObject _gpuPoints = {}; + Texture2D _colormap = {}; /** 2D colormap, sets point color based on point position */ std::int32_t _numSelectedPoints = 0; /** Number of selected (highlighted points) */ std::int32_t _numberOfFocusHighlights = 0; /** Number of focus highlights */ }; From f48a5f473f1a44635bdd1dca304eb42d84b3c764 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 14 Apr 2025 13:57:01 +0200 Subject: [PATCH 44/89] Add several helper functions --- ManiVault/src/renderers/DensityRenderer.cpp | 6 ++++-- ManiVault/src/renderers/DensityRenderer.h | 2 +- ManiVault/src/renderers/PointRenderer.cpp | 12 ++++++++++++ ManiVault/src/renderers/PointRenderer.h | 5 +++++ ManiVault/src/renderers/Renderer2D.cpp | 5 +++++ ManiVault/src/renderers/Renderer2D.h | 6 ++++++ 6 files changed, 33 insertions(+), 3 deletions(-) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index d6e3066e4..c0189817d 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -10,10 +10,12 @@ namespace mv::gui { - -DensityRenderer::DensityRenderer(RenderMode renderMode) : +DensityRenderer::DensityRenderer(RenderMode renderMode, QWidget* sourceWidget, QObject* parent) : + Renderer2D(parent), _renderMode(renderMode) { + if (sourceWidget) + setSourceWidget(sourceWidget); } DensityRenderer::~DensityRenderer() diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index dc8c629b6..47afef1b5 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -24,7 +24,7 @@ class CORE_EXPORT DensityRenderer : public Renderer2D DENSITY, LANDSCAPE }; - DensityRenderer(RenderMode renderMode); + DensityRenderer(RenderMode renderMode, QWidget* sourceWidget = nullptr, QObject* parent = nullptr); ~DensityRenderer() override; /** diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index af48a5990..764d8e097 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -256,6 +256,13 @@ namespace mv _positionBuffer.destroy(); } + PointRenderer::PointRenderer(QWidget* sourceWidget, QObject* parent) : + Renderer2D(parent) + { + if(sourceWidget) + setSourceWidget(sourceWidget); + } + void PointRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); @@ -427,6 +434,11 @@ namespace mv _randomizedDepthEnabled = randomizedDepth; } + void PointRenderer::initView() + { + getNavigator().resetView(true); + } + void PointRenderer::init() { initializeOpenGLFunctions(); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 66f272c88..223ce5cde 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -135,6 +135,9 @@ namespace mv public: using Renderer2D::Renderer2D; + PointRenderer() = default; + PointRenderer(QWidget* sourceWidget, QObject* parent = nullptr); + /** * Set data bounds to \p dataBounds * @param dataBounds Data bounds @@ -165,6 +168,8 @@ namespace mv void setAlpha(const float alpha); void setPointScaling(PointScaling scalingMode); + void initView(); + public: // Selection visualization PointSelectionDisplayMode getSelectionDisplayMode() const; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 2747585b3..5b765219d 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -51,6 +51,11 @@ const Navigator2D& Renderer2D::getNavigator() const return _navigator; } +void Renderer2D::setSourceWidget(QWidget* sourceWidget) +{ + getNavigator().initialize(sourceWidget); +} + QPointer Renderer2D::getCustomNavigator() const { return _customNavigator; diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 3b62053a1..9d2991848 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -59,6 +59,12 @@ Q_OBJECT */ const Navigator2D& getNavigator() const; + /** + * Initializes the source widget used for setting the renderer view + * @param sourceWidget Pointer to the renderer widget + */ + void setSourceWidget(QWidget* sourceWidget); + public: /** From 1c86befc5fbd433939b4dce3ef60f9420c7bda0f Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 10:13:03 +0100 Subject: [PATCH 45/89] Remove unused image renderer and add two-dimensional renderer --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 4 ++-- ManiVault/src/renderers/{ImageRenderer.cpp => Renderer2D.cpp} | 0 ManiVault/src/renderers/{ImageRenderer.h => Renderer2D.h} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename ManiVault/src/renderers/{ImageRenderer.cpp => Renderer2D.cpp} (100%) rename ManiVault/src/renderers/{ImageRenderer.h => Renderer2D.h} (100%) diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index 12d6a3ce8..09e932b9a 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -472,13 +472,13 @@ set(PUBLIC_RENDERERS_HEADERS src/renderers/Renderer.h src/renderers/PointRenderer.h src/renderers/DensityRenderer.h - src/renderers/ImageRenderer.h + src/renderers/Renderer2D.h ) set(PUBLIC_RENDERERS_SOURCES src/renderers/PointRenderer.cpp src/renderers/DensityRenderer.cpp - src/renderers/ImageRenderer.cpp + src/renderers/Renderer2D.cpp ) set(PUBLIC_RENDERERS_FILES diff --git a/ManiVault/src/renderers/ImageRenderer.cpp b/ManiVault/src/renderers/Renderer2D.cpp similarity index 100% rename from ManiVault/src/renderers/ImageRenderer.cpp rename to ManiVault/src/renderers/Renderer2D.cpp diff --git a/ManiVault/src/renderers/ImageRenderer.h b/ManiVault/src/renderers/Renderer2D.h similarity index 100% rename from ManiVault/src/renderers/ImageRenderer.h rename to ManiVault/src/renderers/Renderer2D.h From 3337999bfd8e47eedae83915fb4c3388dce5cf1d Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 10:35:11 +0100 Subject: [PATCH 46/89] Work on Renderer2D class --- ManiVault/src/renderers/PointRenderer.h | 4 +-- ManiVault/src/renderers/Renderer.h | 4 ++- ManiVault/src/renderers/Renderer2D.cpp | 25 +++++----------- ManiVault/src/renderers/Renderer2D.h | 39 ++++++++++++++++++------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 36a5e124c..f69763f7c 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -4,7 +4,7 @@ #pragma once -#include "Renderer.h" +#include "Renderer2D.h" #include "graphics/Bounds.h" #include "graphics/BufferObject.h" @@ -127,7 +127,7 @@ namespace mv float _alpha = DEFAULT_ALPHA_VALUE; }; - class CORE_EXPORT PointRenderer : public Renderer + class CORE_EXPORT PointRenderer : public Renderer2D { public: void setData(const std::vector& points); diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index 3f0bef283..6d5b1624f 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -19,8 +19,10 @@ namespace mv { - class CORE_EXPORT Renderer : protected QOpenGLFunctions_3_3_Core + class CORE_EXPORT Renderer : protected QObject, protected QOpenGLFunctions_3_3_Core { + explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + virtual void init() = 0; virtual void resize(QSize renderSize) = 0; virtual void render() = 0; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index a6dc29b9c..aff3cc7a1 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -2,27 +2,18 @@ // A corresponding LICENSE file is located in the root directory of this source tree // Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) -#include "ImageRenderer.h" +#include "Renderer2D.h" namespace mv { - void ImageRenderer::init() - { - - } - - void ImageRenderer::resize(QSize renderSize) - { - - } - - void ImageRenderer::render() - { +Renderer2D::Renderer2D(QWidget* sourceWidget, QObject* parent) : + Renderer(parent), + _sourceWidget(sourceWidget) +{ + if (_sourceWidget) { + _sourceWidget->installEventFilter(this); } +} - void ImageRenderer::destroy() - { - - } } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 319dcb808..dfbc90d1f 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -8,14 +8,31 @@ namespace mv { - class CORE_EXPORT ImageRenderer : public Renderer - { - public: - void init() override; - void resize(QSize renderSize) override; - void render() override; - void destroy() override; - private: - ShaderProgram _shader; - }; -} // namespace mv + +/** + * Renderer 2D class + * + * Supports two-dimensional rendering: + * - Organizes panning and zooming + * - Sets up the matrix transformations + * - Renders 2D data + * + * @author Thomas Kroes + */ +class CORE_EXPORT Renderer2D : public Renderer +{ +public: + + /** + * Construct a new two-dimensional renderer + * + * @param sourceWidget If set, use this widget to do panning and zooming + * @param parent Pointer to the parent object + */ + explicit Renderer2D(QWidget* sourceWidget = nullptr, QObject* parent = nullptr); + +private: + QPointer _sourceWidget; /** Source widget for panning and zooming */ +}; + +} From 21442021e7ed4923b7b7677abbc9d8abc9ad5c72 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 12:11:05 +0100 Subject: [PATCH 47/89] Work on Renderer2D class --- .../src/actions/PixelSelectionAction.cpp | 8 +- ManiVault/src/renderers/PointRenderer.h | 2 + ManiVault/src/renderers/Renderer.h | 4 +- ManiVault/src/renderers/Renderer2D.cpp | 280 +++++++++++++++++- ManiVault/src/renderers/Renderer2D.h | 144 +++++++++ 5 files changed, 432 insertions(+), 6 deletions(-) diff --git a/ManiVault/src/actions/PixelSelectionAction.cpp b/ManiVault/src/actions/PixelSelectionAction.cpp index be774cff0..2c6c5441f 100644 --- a/ManiVault/src/actions/PixelSelectionAction.cpp +++ b/ManiVault/src/actions/PixelSelectionAction.cpp @@ -417,15 +417,15 @@ QMenu* PixelSelectionAction::getContextMenu() bool PixelSelectionAction::eventFilter(QObject* object, QEvent* event) { if (!isEnabled()) - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); const auto keyEvent = dynamic_cast(event); if (!keyEvent) - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); if (keyEvent->isAutoRepeat()) - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); switch (keyEvent->type()) { @@ -471,7 +471,7 @@ bool PixelSelectionAction::eventFilter(QObject* object, QEvent* event) break; } - return QObject::eventFilter(object, event); + return QWidgetAction::eventFilter(object, event); } void PixelSelectionAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index f69763f7c..86cc5452a 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -130,6 +130,8 @@ namespace mv class CORE_EXPORT PointRenderer : public Renderer2D { public: + using Renderer2D::Renderer2D; + void setData(const std::vector& points); void setHighlights(const std::vector& highlights, const std::int32_t& numSelectedPoints); void setFocusHighlights(const std::vector& focusHighlights, const std::int32_t& numberOfFocusHighlights); diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index 6d5b1624f..244bddb79 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -19,10 +19,12 @@ namespace mv { - class CORE_EXPORT Renderer : protected QObject, protected QOpenGLFunctions_3_3_Core + class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core { + public: explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + protected: virtual void init() = 0; virtual void resize(QSize renderSize) = 0; virtual void render() = 0; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index aff3cc7a1..f6209fe37 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -4,16 +4,294 @@ #include "Renderer2D.h" +#ifdef _DEBUG + //#define RENDERER_2D_VERBOSE +#endif + +#define RENDERER_2D_VERBOSE + namespace mv { Renderer2D::Renderer2D(QWidget* sourceWidget, QObject* parent) : Renderer(parent), - _sourceWidget(sourceWidget) + _sourceWidget(sourceWidget), + _isNavigating(false), + _isPanning(false), + _isZooming(false) { if (_sourceWidget) { _sourceWidget->installEventFilter(this); + _sourceWidget->setFocusPolicy(Qt::StrongFocus); + } +} + +bool Renderer2D::eventFilter(QObject* watched, QEvent* event) +{ + if (event->type() == QEvent::KeyPress) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + beginNavigation(); + + return Renderer::eventFilter(watched, event); + } + } + } + + if (event->type() == QEvent::KeyRelease) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + endNavigation(); + + return Renderer::eventFilter(watched, event); + } + } + } + + if (isNavigating()) { + + if (event->type() == QEvent::Wheel) { + if (auto* wheelEvent = dynamic_cast(event)) + zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); + } + + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + if (mouseEvent->button() == Qt::MiddleButton) + resetView(); + + if (mouseEvent->buttons() == Qt::LeftButton) + { + _sourceWidget->setCursor(Qt::ClosedHandCursor); + + _mousePositions << mouseEvent->pos(); + + _sourceWidget->update(); + } + } + } + + if (event->type() == QEvent::MouseButtonRelease) { + _sourceWidget->setCursor(Qt::ArrowCursor); + + _mousePositions.clear(); + + _sourceWidget->update(); + } + + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + _mousePositions << mouseEvent->pos(); + + 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; + + panBy(panVector); + } + } + } + } + + return Renderer::eventFilter(watched, event); +} + +void Renderer2D::zoomAround(const QPointF& center, float factor) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__ << center << factor; +#endif + + beginZooming(); + { + + } + endZooming(); + + // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); +} + +void Renderer2D::zoomToRectangle(const QRectF& zoomRectangle) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__ << zoomRectangle; +#endif + + beginZooming(); + { + + } + endZooming(); + + //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 Renderer2D::panBy(const QPointF& to) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__ << to; +#endif + + beginPanning(); + { + } + endPanning(); + + //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(); +} + +void Renderer2D::resetView() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif +} + +bool Renderer2D::isPanning() const +{ + return _isPanning; +} + +bool Renderer2D::isZooming() const +{ + return _isZooming; +} + +bool Renderer2D::isNavigating() const +{ + return _isNavigating; +} + +void Renderer2D::setIsPanning(bool isPanning) +{ + if (isPanning == _isPanning) + return; + + _isPanning = isPanning; + + emit isPanningChanged(_isPanning); +} + +void Renderer2D::setIsZooming(bool isZooming) +{ + if (isZooming == _isZooming) + return; + + _isZooming = isZooming; + + emit isZoomingChanged(_isZooming); +} + +void Renderer2D::setIsNavigating(bool isNavigating) +{ + if (isNavigating == _isNavigating) + return; + + _isNavigating = isNavigating; + + emit isNavigatingChanged(_isNavigating); +} + +void Renderer2D::beginPanning() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsPanning(true); + + emit panningStarted(); +} + +void Renderer2D::endPanning() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsPanning(false); + + emit panningEnded(); +} + +void Renderer2D::beginZooming() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsZooming(true); + + emit zoomingStarted(); +} + +void Renderer2D::endZooming() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsZooming(false); + + emit zoomingEnded(); +} + +void Renderer2D::beginNavigation() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsNavigating(true); + + emit navigationStarted(); +} + +void Renderer2D::endNavigation() +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCSIG__; +#endif + + setIsNavigating(false); + + emit navigationEnded(); } } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index dfbc90d1f..841dbc405 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -21,6 +21,9 @@ namespace mv */ class CORE_EXPORT Renderer2D : public Renderer { + +Q_OBJECT + public: /** @@ -31,8 +34,149 @@ class CORE_EXPORT Renderer2D : public Renderer */ explicit Renderer2D(QWidget* sourceWidget = nullptr, QObject* parent = nullptr); + /** + * Watch \p watched for events + * + * @param watched Watched object + * @param event Event + * @return True if the event was handled, false otherwise + */ + bool eventFilter(QObject* watched, QEvent* event) override; + +public: // Navigation + + /** + * Zoom by \p factor around \p center + * + * @param at Point to zoom around + * @param factor Zoom factor + */ + void zoomAround(const QPointF& center, float factor); + + /** + * Zoom to \p zoomRectangle + * + * @param zoomRectangle Zoom to this rectangle + */ + void zoomToRectangle(const QRectF& zoomRectangle); + + /** + * Pan by \p to + * + * @param to Pan by this amount + */ + void panBy(const QPointF& to); + + /** Zoom to extents of the data bounds (with a margin around it) */ + void resetView(); + + /** + * Get whether the renderer is panning + * + * @return Boolean determining whether the renderer is panning + */ + bool isPanning() const; + + /** + * Get whether the renderer is zooming + * + * @return Boolean determining whether the renderer is zooming + */ + bool isZooming() const; + + /** + * Get whether the renderer is navigating + * + * @return Boolean determining whether the renderer is navigating + */ + bool isNavigating() const; + +protected: // Navigation + + /** + * Set whether the renderer is panning to \p isPanning + * + * @param isPanning Boolean determining whether the renderer is panning + */ + void setIsPanning(bool isPanning); + + /** + * Set whether the renderer is zooming to \p isZooming + * + * @param isZooming Boolean determining whether the renderer is zooming + */ + void setIsZooming(bool isZooming); + + /** + * Set whether the renderer is navigating to \p isNavigating + * + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void setIsNavigating(bool isNavigating); + + + /** Panning has begun */ + void beginPanning(); + + /** Panning has ended */ + void endPanning(); + + /** Zooming has begun */ + void beginZooming(); + + /** Zooming has ended */ + void endZooming(); + + /** Navigation has begun */ + void beginNavigation(); + + /** Navigation has ended */ + void endNavigation(); + +signals: + + /** Signals that panning has started */ + void panningStarted(); + + /** Signals that panning has ended */ + void panningEnded(); + + /** + * Signals that is panning changed to \p isPanning + * @param isPanning + */ + void isPanningChanged(bool isPanning); + + /** Signals that zooming has started */ + void zoomingStarted(); + + /** Signals that zooming has ended */ + void zoomingEnded(); + + /** + * Signals that is zooming changed to \p isZooming + * @param isZooming + */ + void isZoomingChanged(bool isZooming); + + /** Signals that navigation has started */ + void navigationStarted(); + + /** Signals that navigation has ended */ + void navigationEnded(); + + /** + * Signals that is navigating changed to \p isNavigating + * @param isNavigating + */ + void isNavigatingChanged(bool isNavigating); + private: QPointer _sourceWidget; /** Source widget for panning and zooming */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ }; } From 8bdb918be824c9ee8156c4670c85adb31fa7a34d Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 13:10:52 +0100 Subject: [PATCH 48/89] Added a two-dimensional navigator class for two-dimensional renderers --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 2 + ManiVault/src/renderers/DensityRenderer.cpp | 3 +- ManiVault/src/renderers/DensityRenderer.h | 10 +- ManiVault/src/renderers/Navigator2D.cpp | 351 ++++++++++++++++++++ ManiVault/src/renderers/Navigator2D.h | 190 +++++++++++ ManiVault/src/renderers/PointRenderer.cpp | 89 +++-- ManiVault/src/renderers/PointRenderer.h | 5 - ManiVault/src/renderers/Renderer.h | 44 ++- ManiVault/src/renderers/Renderer2D.cpp | 271 +-------------- ManiVault/src/renderers/Renderer2D.h | 157 ++------- 10 files changed, 657 insertions(+), 465 deletions(-) create mode 100644 ManiVault/src/renderers/Navigator2D.cpp create mode 100644 ManiVault/src/renderers/Navigator2D.h diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index 09e932b9a..74c6b9859 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -473,12 +473,14 @@ set(PUBLIC_RENDERERS_HEADERS src/renderers/PointRenderer.h src/renderers/DensityRenderer.h src/renderers/Renderer2D.h + src/renderers/Navigator2D.h ) set(PUBLIC_RENDERERS_SOURCES src/renderers/PointRenderer.cpp src/renderers/DensityRenderer.cpp src/renderers/Renderer2D.cpp + src/renderers/Navigator2D.cpp ) set(PUBLIC_RENDERERS_FILES diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index c7237ed8f..04f0f238e 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -9,8 +9,7 @@ namespace mv namespace gui { - DensityRenderer::DensityRenderer(RenderMode renderMode) - : + DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 8f5826940..3ab56fd63 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -4,7 +4,7 @@ #pragma once -#include "Renderer.h" +#include "Renderer2D.h" #include "graphics/Bounds.h" #include "graphics/Shader.h" @@ -21,7 +21,7 @@ namespace mv namespace gui { - class CORE_EXPORT DensityRenderer : public Renderer + class CORE_EXPORT DensityRenderer : public Renderer2D { public: @@ -50,8 +50,10 @@ namespace mv void init() override; void resize(QSize renderSize) override; - void render() override; - void destroy() override; + + void render() override; + + void destroy() override; void setColorMapRange(const float& min, const float& max); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp new file mode 100644 index 000000000..8e5739374 --- /dev/null +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "Navigator2D.h" +#include "Renderer2D.h" + +#ifdef _DEBUG + //#define NAVIGATOR_2D_VERBOSE +#endif + +#define NAVIGATOR_2D_VERBOSE + +namespace mv +{ + +Navigator2D::Navigator2D(QObject* parent) : + QObject(parent), + _initialized(false), + _isNavigating(false), + _isPanning(false), + _isZooming(false) +{ +} + +void Navigator2D::initialize(QWidget* sourceWidget, Renderer2D* renderer) +{ + Q_ASSERT(sourceWidget && renderer); + + if (sourceWidget && renderer) { + _sourceWidget = sourceWidget; + _renderer = renderer; + + _sourceWidget->installEventFilter(this); + _sourceWidget->setFocusPolicy(Qt::StrongFocus); + + _initialized = true; + } +} + +bool Navigator2D::eventFilter(QObject* watched, QEvent* event) +{ + if (!_initialized) + return false; + + if (event->type() == QEvent::KeyPress) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + beginNavigation(); + + return QObject::eventFilter(watched, event); + } + } + } + + if (event->type() == QEvent::KeyRelease) { + if (const auto* keyEvent = dynamic_cast(event)) { + if (keyEvent->key() == Qt::Key_Alt) { + endNavigation(); + + return QObject::eventFilter(watched, event); + } + } + } + + if (isNavigating()) { + + if (event->type() == QEvent::Wheel) { + if (auto* wheelEvent = dynamic_cast(event)) + zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); + } + + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + if (mouseEvent->button() == Qt::MiddleButton) + resetView(); + + if (mouseEvent->buttons() == Qt::LeftButton) + { + _sourceWidget->setCursor(Qt::ClosedHandCursor); + + _mousePositions << mouseEvent->pos(); + + _sourceWidget->update(); + } + } + } + + if (event->type() == QEvent::MouseButtonRelease) { + _sourceWidget->setCursor(Qt::ArrowCursor); + + _mousePositions.clear(); + + _sourceWidget->update(); + } + + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) + { + _mousePositions << mouseEvent->pos(); + + 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; + + panBy(panVector); + } + } + } + } + + return QObject::eventFilter(watched, event); +} + +void Navigator2D::zoomAround(const QPointF& center, float factor) +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << center << factor; +#endif + + beginZooming(); + { + + } + endZooming(); + + // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); +} + +void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << zoomRectangle; +#endif + + beginZooming(); + { + + } + endZooming(); + + //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 Navigator2D::panBy(const QPointF& to) +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << to; +#endif + + beginPanning(); + { + + } + endPanning(); + + //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(); +} + +void Navigator2D::resetView() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif +} + +bool Navigator2D::isPanning() const +{ + return _isPanning; +} + +bool Navigator2D::isZooming() const +{ + return _isZooming; +} + +bool Navigator2D::isNavigating() const +{ + return _isNavigating; +} + +void Navigator2D::setIsPanning(bool isPanning) +{ + if (!_initialized) + return; + + if (isPanning == _isPanning) + return; + + _isPanning = isPanning; + + emit isPanningChanged(_isPanning); +} + +void Navigator2D::setIsZooming(bool isZooming) +{ + if (!_initialized) + return; + + if (isZooming == _isZooming) + return; + + _isZooming = isZooming; + + emit isZoomingChanged(_isZooming); +} + +void Navigator2D::setIsNavigating(bool isNavigating) +{ + if (!_initialized) + return; + + if (isNavigating == _isNavigating) + return; + + _isNavigating = isNavigating; + + emit isNavigatingChanged(_isNavigating); +} + +void Navigator2D::beginPanning() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsPanning(true); + + emit panningStarted(); +} + +void Navigator2D::endPanning() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsPanning(false); + + emit panningEnded(); +} + +void Navigator2D::beginZooming() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsZooming(true); + + emit zoomingStarted(); +} + +void Navigator2D::endZooming() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsZooming(false); + + emit zoomingEnded(); +} + +void Navigator2D::beginNavigation() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsNavigating(true); + + emit navigationStarted(); +} + +void Navigator2D::endNavigation() +{ + if (!_initialized) + return; + +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__; +#endif + + setIsNavigating(false); + + emit navigationEnded(); +} + +} diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h new file mode 100644 index 000000000..e23cbd38d --- /dev/null +++ b/ManiVault/src/renderers/Navigator2D.h @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include + +namespace mv +{ + +class Renderer2D; + +/** + * Navigator 2D class + * + * Orchestrates panning and zooming in a widget that displays 2D data using Renderer2D + * + * @author Thomas Kroes + */ +class CORE_EXPORT Navigator2D : public QObject +{ + +Q_OBJECT + +public: + + /** + * Construct a new two-dimensional navigator + * + * @param parent Pointer to the parent object + */ + explicit Navigator2D(QObject* parent = nullptr); + + /** + * Initializes the two-dimensional navigator with a \p widget and a \p renderer + * + * @param sourceWidget Pointer to the renderer widget + * @param renderer Pointer to the renderer + */ + void initialize(QWidget* sourceWidget, Renderer2D* renderer); + + /** + * Watch \p watched for events + * + * @param watched Watched object + * @param event Event + * @return True if the event was handled, false otherwise + */ + bool eventFilter(QObject* watched, QEvent* event) override; + +public: // Navigation + + /** + * Zoom by \p factor around \p center + * + * @param center Point to zoom around + * @param factor Zoom factor + */ + void zoomAround(const QPointF& center, float factor); + + /** + * Zoom to \p zoomRectangle + * + * @param zoomRectangle Zoom to this rectangle + */ + void zoomToRectangle(const QRectF& zoomRectangle); + + /** + * Pan by \p to + * + * @param to Pan by this amount + */ + void panBy(const QPointF& to); + + /** Zoom to extents of the data bounds (with a margin around it) */ + void resetView(); + + /** + * Get whether the renderer is panning + * + * @return Boolean determining whether the renderer is panning + */ + bool isPanning() const; + + /** + * Get whether the renderer is zooming + * + * @return Boolean determining whether the renderer is zooming + */ + bool isZooming() const; + + /** + * Get whether the renderer is navigating + * + * @return Boolean determining whether the renderer is navigating + */ + bool isNavigating() const; + +protected: // Navigation + + /** + * Set whether the renderer is panning to \p isPanning + * + * @param isPanning Boolean determining whether the renderer is panning + */ + void setIsPanning(bool isPanning); + + /** + * Set whether the renderer is zooming to \p isZooming + * + * @param isZooming Boolean determining whether the renderer is zooming + */ + void setIsZooming(bool isZooming); + + /** + * Set whether the renderer is navigating to \p isNavigating + * + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void setIsNavigating(bool isNavigating); + + + /** Panning has begun */ + void beginPanning(); + + /** Panning has ended */ + void endPanning(); + + /** Zooming has begun */ + void beginZooming(); + + /** Zooming has ended */ + void endZooming(); + + /** Navigation has begun */ + void beginNavigation(); + + /** Navigation has ended */ + void endNavigation(); + +signals: + + /** Signals that panning has started */ + void panningStarted(); + + /** Signals that panning has ended */ + void panningEnded(); + + /** + * Signals that is panning changed to \p isPanning + * @param isPanning + */ + void isPanningChanged(bool isPanning); + + /** Signals that zooming has started */ + void zoomingStarted(); + + /** Signals that zooming has ended */ + void zoomingEnded(); + + /** + * Signals that is zooming changed to \p isZooming + * @param isZooming + */ + void isZoomingChanged(bool isZooming); + + /** Signals that navigation has started */ + void navigationStarted(); + + /** Signals that navigation has ended */ + void navigationEnded(); + + /** + * Signals that is navigating changed to \p isNavigating + * @param isNavigating + */ + void isNavigatingChanged(bool isNavigating); + +private: + QPointer _sourceWidget; /** Source widget for panning and zooming */ + QPointer _renderer; /** Source widget for panning and zooming */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ +}; + +} diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index a2cdeb1c7..2381abbcf 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -369,11 +369,6 @@ namespace mv return _gpuPoints; } - QSize PointRenderer::getWindowsSize() const - { - return _windowSize; - } - std::int32_t PointRenderer::getNumSelectedPoints() const { return _numSelectedPoints; @@ -485,66 +480,56 @@ namespace mv } } - void PointRenderer::resize(QSize renderSize) - { - int w = renderSize.width(); - int h = renderSize.height(); - - _windowSize.setWidth(w); - _windowSize.setHeight(h); - } - void PointRenderer::render() { - int w = _windowSize.width(); - int h = _windowSize.height(); - int size = w < h ? w : h; + beginRender(); + { + // World to clip transformation + _orthoM = createProjectionMatrix(_boundsView); - glViewport(w / 2 - size / 2, h / 2 - size / 2, size, size); + _shader.bind(); - // World to clip transformation - _orthoM = createProjectionMatrix(_boundsView); + const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; + const auto size = std::max(getRenderSize().width(), getRenderSize().height()); - _shader.bind(); + _shader.uniform1f("pointSize", _pointSettings._pointSize); + _shader.uniform1f("pointSizeScale", absoluteRendering ? (1.0 / size) : 1.0f / size); - // Point size uniforms - bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; - _shader.uniform1f("pointSize", _pointSettings._pointSize); - _shader.uniform1f("pointSizeScale", absoluteRendering ? (1.0 / size) : 1.0f / size); + _shader.uniformMatrix3f("orthoM", _orthoM); + _shader.uniform1f("pointOpacity", _pointSettings._alpha); + _shader.uniform1i("scalarEffect", _pointEffect); - _shader.uniformMatrix3f("orthoM", _orthoM); - _shader.uniform1f("pointOpacity", _pointSettings._alpha); - _shader.uniform1i("scalarEffect", _pointEffect); - - _shader.uniform4f("dataBounds", _boundsData.getLeft(), _boundsData.getRight(), _boundsData.getBottom(), _boundsData.getTop()); + _shader.uniform4f("dataBounds", _boundsData.getLeft(), _boundsData.getRight(), _boundsData.getBottom(), _boundsData.getTop()); - _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); - _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); - _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); - _shader.uniform1i("selectionOutlineOverrideColor", _selectionOutlineOverrideColor); - _shader.uniform1f("selectionOutlineOpacity", _selectionOutlineOpacity); - _shader.uniform1i("selectionHaloEnabled", _selectionHaloEnabled); + _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); + _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); + _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); + _shader.uniform1i("selectionOutlineOverrideColor", _selectionOutlineOverrideColor); + _shader.uniform1f("selectionOutlineOpacity", _selectionOutlineOpacity); + _shader.uniform1i("selectionHaloEnabled", _selectionHaloEnabled); - _shader.uniform1i("randomizedDepthEnabled", _randomizedDepthEnabled); + _shader.uniform1i("randomizedDepthEnabled", _randomizedDepthEnabled); - _shader.uniform1i("hasHighlights", _gpuPoints.hasHighlights()); - _shader.uniform1i("hasFocusHighlights", _gpuPoints.hasFocusHighlights()); - _shader.uniform1i("hasScalars", _gpuPoints.hasColorScalars()); - _shader.uniform1i("hasColors", _gpuPoints.hasColors()); - _shader.uniform1i("hasSizes", _gpuPoints.hasSizeScalars()); - _shader.uniform1i("hasOpacities", _gpuPoints.hasOpacityScalars()); - _shader.uniform1i("numSelectedPoints", _numSelectedPoints); + _shader.uniform1i("hasHighlights", _gpuPoints.hasHighlights()); + _shader.uniform1i("hasFocusHighlights", _gpuPoints.hasFocusHighlights()); + _shader.uniform1i("hasScalars", _gpuPoints.hasColorScalars()); + _shader.uniform1i("hasColors", _gpuPoints.hasColors()); + _shader.uniform1i("hasSizes", _gpuPoints.hasSizeScalars()); + _shader.uniform1i("hasOpacities", _gpuPoints.hasOpacityScalars()); + _shader.uniform1i("numSelectedPoints", _numSelectedPoints); - if (_gpuPoints.hasColorScalars()) - _shader.uniform3f("colorMapRange", _gpuPoints.getColorMapRange()); + if (_gpuPoints.hasColorScalars()) + _shader.uniform3f("colorMapRange", _gpuPoints.getColorMapRange()); - if (_colormap.isCreated() && (_pointEffect == PointEffect::Color || _pointEffect == PointEffect::Color2D)) - { - _colormap.bind(0); - _shader.uniform1i("colormap", 0); - } + if (_colormap.isCreated() && (_pointEffect == PointEffect::Color || _pointEffect == PointEffect::Color2D)) + { + _colormap.bind(0); + _shader.uniform1i("colormap", 0); + } - _gpuPoints.draw(); + _gpuPoints.draw(); + } + endRender(); } void PointRenderer::destroy() diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 86cc5452a..20f42507c 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -166,7 +166,6 @@ namespace mv Matrix3f getProjectionMatrix() const; const PointArrayObject& getGpuPoints() const; - QSize getWindowsSize() const; std::int32_t getNumSelectedPoints() const; const PointSettings& getPointSettings() const; @@ -198,7 +197,6 @@ namespace mv void setRandomizedDepthEnabled(bool randomizedDepth); void init() override; - void resize(QSize renderSize) override; void render() override; void destroy() override; @@ -221,9 +219,6 @@ namespace mv /* Depth control */ bool _randomizedDepthEnabled = true; - /* Window properties */ - QSize _windowSize; - /* Rendering variables */ ShaderProgram _shader; diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index 244bddb79..f556b8b6f 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -19,15 +19,37 @@ namespace mv { - class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core - { - public: - explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} - - protected: - virtual void init() = 0; - virtual void resize(QSize renderSize) = 0; - virtual void render() = 0; - virtual void destroy() = 0; - }; + +class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core +{ +protected: + + /** + * Construct a new renderer + * @param parent Pointer to the parent object + */ + explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + + virtual void init() = 0; + virtual void resize(QSize renderSize) = 0; + + /** + * Get the render size + * @return Render size + */ + virtual QSize getRenderSize() const = 0; + + /** Begin rendering */ + virtual void beginRender() = 0; + + /** Render */ + virtual void render() = 0; + + /** End rendering */ + virtual void endRender() = 0; + + /** Destroy the renderer */ + virtual void destroy() = 0; +}; + } diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index f6209fe37..a7dd4ebb1 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -13,285 +13,42 @@ namespace mv { -Renderer2D::Renderer2D(QWidget* sourceWidget, QObject* parent) : - Renderer(parent), - _sourceWidget(sourceWidget), - _isNavigating(false), - _isPanning(false), - _isZooming(false) +Renderer2D::Renderer2D(QObject* parent) : + Renderer(parent) { - if (_sourceWidget) { - _sourceWidget->installEventFilter(this); - _sourceWidget->setFocusPolicy(Qt::StrongFocus); - } } -bool Renderer2D::eventFilter(QObject* watched, QEvent* event) +void Renderer2D::resize(QSize renderSize) { - if (event->type() == QEvent::KeyPress) { - if (const auto* keyEvent = dynamic_cast(event)) { - if (keyEvent->key() == Qt::Key_Alt) { - beginNavigation(); - - return Renderer::eventFilter(watched, event); - } - } - } - - if (event->type() == QEvent::KeyRelease) { - if (const auto* keyEvent = dynamic_cast(event)) { - if (keyEvent->key() == Qt::Key_Alt) { - endNavigation(); - - return Renderer::eventFilter(watched, event); - } - } - } - - if (isNavigating()) { - - if (event->type() == QEvent::Wheel) { - if (auto* wheelEvent = dynamic_cast(event)) - zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); - } - - if (event->type() == QEvent::MouseButtonPress) { - if (const auto* mouseEvent = dynamic_cast(event)) - { - if (mouseEvent->button() == Qt::MiddleButton) - resetView(); - - if (mouseEvent->buttons() == Qt::LeftButton) - { - _sourceWidget->setCursor(Qt::ClosedHandCursor); - - _mousePositions << mouseEvent->pos(); - - _sourceWidget->update(); - } - } - } - - if (event->type() == QEvent::MouseButtonRelease) { - _sourceWidget->setCursor(Qt::ArrowCursor); - - _mousePositions.clear(); - - _sourceWidget->update(); - } - - if (event->type() == QEvent::MouseMove) { - if (const auto* mouseEvent = dynamic_cast(event)) - { - _mousePositions << mouseEvent->pos(); - - 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; - - panBy(panVector); - } - } - } - } - - return Renderer::eventFilter(watched, event); -} - -void Renderer2D::zoomAround(const QPointF& center, float factor) -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__ << center << factor; -#endif - - beginZooming(); - { - - } - endZooming(); - - // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); -} - -void Renderer2D::zoomToRectangle(const QRectF& zoomRectangle) -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__ << zoomRectangle; -#endif - - beginZooming(); - { - - } - endZooming(); - - //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 Renderer2D::panBy(const QPointF& to) -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__ << to; -#endif - - beginPanning(); - { - - } - endPanning(); - - //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(); -} - -void Renderer2D::resetView() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif -} - -bool Renderer2D::isPanning() const -{ - return _isPanning; -} - -bool Renderer2D::isZooming() const -{ - return _isZooming; -} - -bool Renderer2D::isNavigating() const -{ - return _isNavigating; -} - -void Renderer2D::setIsPanning(bool isPanning) -{ - if (isPanning == _isPanning) - return; - - _isPanning = isPanning; - - emit isPanningChanged(_isPanning); + _renderSize = renderSize; } -void Renderer2D::setIsZooming(bool isZooming) +QSize Renderer2D::getRenderSize() const { - if (isZooming == _isZooming) - return; - - _isZooming = isZooming; - - emit isZoomingChanged(_isZooming); + return _renderSize; } -void Renderer2D::setIsNavigating(bool isNavigating) +Navigator2D& Renderer2D::getNavigator() { - if (isNavigating == _isNavigating) - return; - - _isNavigating = isNavigating; - - emit isNavigatingChanged(_isNavigating); + return _navigator; } -void Renderer2D::beginPanning() +void Renderer2D::beginRender() { #ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; + qDebug() << __FUNCTION__; #endif - setIsPanning(true); + const auto size = _renderSize.width() < _renderSize.height() ? _renderSize.width() : _renderSize.height(); - emit panningStarted(); + glViewport(_renderSize.width() / 2 - size / 2, _renderSize.height() / 2 - size / 2, size, size); } -void Renderer2D::endPanning() +void Renderer2D::endRender() { #ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; + qDebug() << __FUNCTION__; #endif - - setIsPanning(false); - - emit panningEnded(); -} - -void Renderer2D::beginZooming() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsZooming(true); - - emit zoomingStarted(); -} - -void Renderer2D::endZooming() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsZooming(false); - - emit zoomingEnded(); -} - -void Renderer2D::beginNavigation() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsNavigating(true); - - emit navigationStarted(); -} - -void Renderer2D::endNavigation() -{ -#ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCSIG__; -#endif - - setIsNavigating(false); - - emit navigationEnded(); } } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 841dbc405..de1999869 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -5,6 +5,7 @@ #pragma once #include "Renderer.h" +#include "Navigator2D.h" namespace mv { @@ -13,7 +14,7 @@ namespace mv * Renderer 2D class * * Supports two-dimensional rendering: - * - Organizes panning and zooming + * - Orchestrates panning and zooming using Navigator2D * - Sets up the matrix transformations * - Renders 2D data * @@ -29,154 +30,42 @@ Q_OBJECT /** * Construct a new two-dimensional renderer * - * @param sourceWidget If set, use this widget to do panning and zooming * @param parent Pointer to the parent object */ - explicit Renderer2D(QWidget* sourceWidget = nullptr, QObject* parent = nullptr); + explicit Renderer2D(QObject* parent = nullptr); - /** - * Watch \p watched for events - * - * @param watched Watched object - * @param event Event - * @return True if the event was handled, false otherwise - */ - bool eventFilter(QObject* watched, QEvent* event) override; - -public: // Navigation - - /** - * Zoom by \p factor around \p center - * - * @param at Point to zoom around - * @param factor Zoom factor - */ - void zoomAround(const QPointF& center, float factor); - - /** - * Zoom to \p zoomRectangle - * - * @param zoomRectangle Zoom to this rectangle - */ - void zoomToRectangle(const QRectF& zoomRectangle); - - /** - * Pan by \p to - * - * @param to Pan by this amount - */ - void panBy(const QPointF& to); - - /** Zoom to extents of the data bounds (with a margin around it) */ - void resetView(); - - /** - * Get whether the renderer is panning - * - * @return Boolean determining whether the renderer is panning - */ - bool isPanning() const; - - /** - * Get whether the renderer is zooming - * - * @return Boolean determining whether the renderer is zooming - */ - bool isZooming() const; - - /** - * Get whether the renderer is navigating - * - * @return Boolean determining whether the renderer is navigating - */ - bool isNavigating() const; - -protected: // Navigation - - /** - * Set whether the renderer is panning to \p isPanning - * - * @param isPanning Boolean determining whether the renderer is panning - */ - void setIsPanning(bool isPanning); + /** + * Resize the renderer to \p renderSize + * @param renderSize New size of the renderer + */ + void resize(QSize renderSize) override; /** - * Set whether the renderer is zooming to \p isZooming - * - * @param isZooming Boolean determining whether the renderer is zooming + * Get the render size + * @return Render size */ - void setIsZooming(bool isZooming); + QSize getRenderSize() const override; /** - * Set whether the renderer is navigating to \p isNavigating + * Get the 2D navigator * - * @param isNavigating Boolean determining whether the renderer is navigating - */ - void setIsNavigating(bool isNavigating); - - - /** Panning has begun */ - void beginPanning(); - - /** Panning has ended */ - void endPanning(); - - /** Zooming has begun */ - void beginZooming(); - - /** Zooming has ended */ - void endZooming(); - - /** Navigation has begun */ - void beginNavigation(); - - /** Navigation has ended */ - void endNavigation(); - -signals: - - /** Signals that panning has started */ - void panningStarted(); - - /** Signals that panning has ended */ - void panningEnded(); - - /** - * Signals that is panning changed to \p isPanning - * @param isPanning - */ - void isPanningChanged(bool isPanning); - - /** Signals that zooming has started */ - void zoomingStarted(); - - /** Signals that zooming has ended */ - void zoomingEnded(); - - /** - * Signals that is zooming changed to \p isZooming - * @param isZooming + * @return Reference to the 2D navigator */ - void isZoomingChanged(bool isZooming); + Navigator2D& getNavigator(); - /** Signals that navigation has started */ - void navigationStarted(); +protected: - /** Signals that navigation has ended */ - void navigationEnded(); + /** Begin rendering */ + void beginRender() override; - /** - * Signals that is navigating changed to \p isNavigating - * @param isNavigating - */ - void isNavigatingChanged(bool isNavigating); + /** End rendering */ + void endRender() override; private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + + friend class Navigator2D; }; } From 5dcc0c2f5fa0a8fa253e74cf35a6de62139403d8 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 20 Mar 2025 13:21:03 +0100 Subject: [PATCH 49/89] Work on two-dimensional navigator --- ManiVault/src/renderers/Navigator2D.cpp | 36 ++++++++++++------------- ManiVault/src/renderers/Navigator2D.h | 10 +++---- ManiVault/src/renderers/Renderer2D.cpp | 3 ++- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 8e5739374..d31e50d0f 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -14,8 +14,9 @@ namespace mv { -Navigator2D::Navigator2D(QObject* parent) : +Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : QObject(parent), + _renderer(renderer), _initialized(false), _isNavigating(false), _isPanning(false), @@ -23,13 +24,12 @@ Navigator2D::Navigator2D(QObject* parent) : { } -void Navigator2D::initialize(QWidget* sourceWidget, Renderer2D* renderer) +void Navigator2D::initialize(QWidget* sourceWidget) { - Q_ASSERT(sourceWidget && renderer); + Q_ASSERT(sourceWidget); - if (sourceWidget && renderer) { + if (sourceWidget) { _sourceWidget = sourceWidget; - _renderer = renderer; _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); @@ -102,9 +102,9 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) 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; + const auto& previousMousePosition = _mousePositions[_mousePositions.size() - 2]; + const auto& currentMousePosition = _mousePositions[_mousePositions.size() - 1]; + const auto panVector = currentMousePosition - previousMousePosition; panBy(panVector); } @@ -120,7 +120,7 @@ void Navigator2D::zoomAround(const QPointF& center, float factor) if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << center << factor; #endif @@ -138,7 +138,7 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << zoomRectangle; #endif @@ -163,7 +163,7 @@ void Navigator2D::panBy(const QPointF& to) if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << to; #endif @@ -205,7 +205,7 @@ void Navigator2D::resetView() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif } @@ -269,7 +269,7 @@ void Navigator2D::beginPanning() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -283,7 +283,7 @@ void Navigator2D::endPanning() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -297,7 +297,7 @@ void Navigator2D::beginZooming() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -311,7 +311,7 @@ void Navigator2D::endZooming() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -325,7 +325,7 @@ void Navigator2D::beginNavigation() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif @@ -339,7 +339,7 @@ void Navigator2D::endNavigation() if (!_initialized) return; -#ifdef RENDERER_2D_VERBOSE +#ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__; #endif diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e23cbd38d..0033be899 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -28,17 +28,17 @@ Q_OBJECT /** * Construct a new two-dimensional navigator * + * @param renderer Reference to parent renderer * @param parent Pointer to the parent object */ - explicit Navigator2D(QObject* parent = nullptr); + explicit Navigator2D(Renderer2D& renderer, QObject* parent = nullptr); /** - * Initializes the two-dimensional navigator with a \p widget and a \p renderer + * Initializes the two-dimensional navigator with a \p sourceWidget * * @param sourceWidget Pointer to the renderer widget - * @param renderer Pointer to the renderer */ - void initialize(QWidget* sourceWidget, Renderer2D* renderer); + void initialize(QWidget* sourceWidget); /** * Watch \p watched for events @@ -179,7 +179,7 @@ Q_OBJECT private: QPointer _sourceWidget; /** Source widget for panning and zooming */ - QPointer _renderer; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ bool _initialized; /** Initialized flag */ QVector _mousePositions; /** Recorded mouse positions */ bool _isNavigating; /** Navigating flag */ diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index a7dd4ebb1..537319ca4 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -14,7 +14,8 @@ namespace mv { Renderer2D::Renderer2D(QObject* parent) : - Renderer(parent) + Renderer(parent), + _navigator(*this) { } From a5073f9f9c9f43484dbef4ad8b16a875c1b97df0 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 21 Mar 2025 09:38:16 +0100 Subject: [PATCH 50/89] Work on navigator --- ManiVault/res/shaders/PointPlot.vert | 2 +- ManiVault/src/graphics/Shader.cpp | 4 + ManiVault/src/graphics/Shader.h | 3 + ManiVault/src/renderers/Navigator2D.cpp | 64 ++++++++--- ManiVault/src/renderers/Navigator2D.h | 31 +++--- ManiVault/src/renderers/PointRenderer.cpp | 11 +- ManiVault/src/renderers/Renderer2D.cpp | 125 +++++++++++++++++++++- ManiVault/src/renderers/Renderer2D.h | 79 +++++++++++++- 8 files changed, 282 insertions(+), 37 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index bc000a036..613951f56 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -132,7 +132,7 @@ void main() vPosOrig = position; // Transform position to clip space - vec2 pos = (orthoM * vec3(position, 1)).xy; + vec2 pos = (orthoM * vec4(position, 0, 1)).xy; // Resize point quad according to properties vec2 scaledVertex = vertex * pointSize * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); diff --git a/ManiVault/src/graphics/Shader.cpp b/ManiVault/src/graphics/Shader.cpp index dd99c67b6..275037cb8 100644 --- a/ManiVault/src/graphics/Shader.cpp +++ b/ManiVault/src/graphics/Shader.cpp @@ -156,6 +156,10 @@ void ShaderProgram::uniformMatrix3f(const char* name, Matrix3f& m) { glUniformMatrix3fv(location(name), 1, false, m.toArray()); } +void ShaderProgram::uniformMatrix3f(const char* name, float* data) { + glUniformMatrix3fv(location(name), 1, false, data); +} + //void Shader::uniformMatrix4f(const char* name, Matrix4f& m) { // glUniformMatrix4fv(location(name), 1, false, m.toArray()); //} diff --git a/ManiVault/src/graphics/Shader.h b/ManiVault/src/graphics/Shader.h index 2ee0c49ce..0cf9e07d7 100644 --- a/ManiVault/src/graphics/Shader.h +++ b/ManiVault/src/graphics/Shader.h @@ -36,6 +36,9 @@ class CORE_EXPORT ShaderProgram : protected QOpenGLFunctions_3_3_Core void uniform3fv(const char* name, int count, Vector3f* v); void uniform4f(const char* name, float v0, float v1, float v2, float v3); void uniformMatrix3f(const char* name, Matrix3f& m); + + void uniformMatrix3f(const char* name, float* data); + //void uniformMatrix4f(const char* name, Matrix4f& m); void uniformMatrix4f(const char* name, const float* const m); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index d31e50d0f..a5cb95e60 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -20,7 +20,8 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _initialized(false), _isNavigating(false), _isPanning(false), - _isZooming(false) + _isZooming(false), + _zoomFactor(1.0f) { } @@ -66,18 +67,22 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (isNavigating()) { if (event->type() == QEvent::Wheel) { - if (auto* wheelEvent = dynamic_cast(event)) - zoomAround(wheelEvent->position().toPoint(), static_cast(wheelEvent->angleDelta().x()) / 1200.f); + if (auto* wheelEvent = dynamic_cast(event)) { + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + } } if (event->type() == QEvent::MouseButtonPress) { - if (const auto* mouseEvent = dynamic_cast(event)) - { + if (const auto* mouseEvent = dynamic_cast(event)) { if (mouseEvent->button() == Qt::MiddleButton) resetView(); - if (mouseEvent->buttons() == Qt::LeftButton) - { + if (mouseEvent->buttons() == Qt::LeftButton) { _sourceWidget->setCursor(Qt::ClosedHandCursor); _mousePositions << mouseEvent->pos(); @@ -96,12 +101,10 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) } if (event->type() == QEvent::MouseMove) { - if (const auto* mouseEvent = dynamic_cast(event)) - { + if (const auto* mouseEvent = dynamic_cast(event)) { _mousePositions << mouseEvent->pos(); - if (mouseEvent->buttons() == Qt::LeftButton && _mousePositions.size() >= 2) - { + 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; @@ -115,7 +118,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) return QObject::eventFilter(watched, event); } -void Navigator2D::zoomAround(const QPointF& center, float factor) +void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) return; @@ -126,7 +129,22 @@ void Navigator2D::zoomAround(const QPointF& center, float factor) beginZooming(); { + _zoomFactor *= factor; + + computeZoomRectangle(); + + /* + const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), center).toPointF(); + const auto v1 = _renderer.getZoomRectangle().topLeft() - p1; + const auto v2 = v1 / factor; + auto zoomRectangle = _renderer.getZoomRectangle(); + + zoomRectangle.setTopLeft(p1 + v2); + zoomRectangle.setSize(zoomRectangle.size() / factor); + + _renderer.setZoomRectangle(zoomRectangle); + */ } endZooming(); @@ -158,18 +176,29 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) //update(); } -void Navigator2D::panBy(const QPointF& to) +void Navigator2D::panBy(const QPointF& delta) { if (!_initialized) return; #ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << to; + qDebug() << __FUNCTION__ << delta; #endif beginPanning(); { + const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), QPoint()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), delta.toPoint()).toPointF(); + + //auto zoomRectangle = _renderer.getZoomRectangle(); + //zoomRectangle.setTopLeft(zoomRectangle.topLeft() + (p2 - p1)); + + //_renderer.setZoomRectangle(zoomRectangle); + + _panCoordinates -= QVector2D(p2 - p1) / _zoomFactor; + + computeZoomRectangle(); } endPanning(); @@ -348,4 +377,11 @@ void Navigator2D::endNavigation() emit navigationEnded(); } +void Navigator2D::computeZoomRectangle() const +{ + QRectF zoomRectangle(_panCoordinates.toPointF(), _zoomFactor * _renderer.getRenderSize()); + + _renderer.setZoomRectangle(zoomRectangle); +} + } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 0033be899..a4331a84d 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -5,6 +5,7 @@ #pragma once #include +#include namespace mv { @@ -35,7 +36,6 @@ Q_OBJECT /** * Initializes the two-dimensional navigator with a \p sourceWidget - * * @param sourceWidget Pointer to the renderer widget */ void initialize(QWidget* sourceWidget); @@ -53,46 +53,40 @@ Q_OBJECT /** * Zoom by \p factor around \p center - * * @param center Point to zoom around * @param factor Zoom factor */ - void zoomAround(const QPointF& center, float factor); + void zoomAround(const QPoint& center, float factor); /** * Zoom to \p zoomRectangle - * * @param zoomRectangle Zoom to this rectangle */ void zoomToRectangle(const QRectF& zoomRectangle); /** - * Pan by \p to - * - * @param to Pan by this amount + * Pan by \p delta + * @param delta Pan by this amount */ - void panBy(const QPointF& to); + void panBy(const QPointF& delta); /** Zoom to extents of the data bounds (with a margin around it) */ void resetView(); /** * Get whether the renderer is panning - * * @return Boolean determining whether the renderer is panning */ bool isPanning() const; /** * Get whether the renderer is zooming - * * @return Boolean determining whether the renderer is zooming */ bool isZooming() const; /** * Get whether the renderer is navigating - * * @return Boolean determining whether the renderer is navigating */ bool isNavigating() const; @@ -101,26 +95,22 @@ Q_OBJECT /** * Set whether the renderer is panning to \p isPanning - * * @param isPanning Boolean determining whether the renderer is panning */ void setIsPanning(bool isPanning); /** * Set whether the renderer is zooming to \p isZooming - * * @param isZooming Boolean determining whether the renderer is zooming */ void setIsZooming(bool isZooming); /** * Set whether the renderer is navigating to \p isNavigating - * * @param isNavigating Boolean determining whether the renderer is navigating */ void setIsNavigating(bool isNavigating); - /** Panning has begun */ void beginPanning(); @@ -139,6 +129,11 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); +private: + + /** Compute the zoom rectangle in world space */ + void computeZoomRectangle() const; + signals: /** Signals that panning has started */ @@ -161,7 +156,7 @@ Q_OBJECT /** * Signals that is zooming changed to \p isZooming - * @param isZooming + * @param isZooming Boolean determining whether the renderer is zooming */ void isZoomingChanged(bool isZooming); @@ -173,7 +168,7 @@ Q_OBJECT /** * Signals that is navigating changed to \p isNavigating - * @param isNavigating + * @param isNavigating Boolean determining whether the renderer is navigating */ void isNavigatingChanged(bool isNavigating); @@ -185,6 +180,8 @@ Q_OBJECT bool _isNavigating; /** Navigating flag */ bool _isPanning; /** Panning flag */ bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QVector2D _panCoordinates; /** Pan coordinates in world space */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 2381abbcf..4faa1b9d8 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -484,8 +484,17 @@ namespace mv { beginRender(); { + qDebug() << getZoomRectangle(); + + const float aspectRatio = getRenderSize().width() / static_cast(getRenderSize().height()); + + QMatrix4x4 projection; + + + projection.ortho(aspectRatio * getZoomRectangle().left(), aspectRatio * getZoomRectangle().right(), getZoomRectangle().bottom(), getZoomRectangle().top(), -1.f, 1.f); + // World to clip transformation - _orthoM = createProjectionMatrix(_boundsView); + _orthoM = createProjectionMatrix(Bounds(getZoomRectangle().left(), getZoomRectangle().right(), getZoomRectangle().top(), getZoomRectangle().bottom())); _shader.bind(); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 537319ca4..60f22d9c0 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -15,7 +15,8 @@ namespace mv Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), - _navigator(*this) + _navigator(*this), + _zoomRectangle(0, 0, 200, 100) { } @@ -42,7 +43,7 @@ void Renderer2D::beginRender() const auto size = _renderSize.width() < _renderSize.height() ? _renderSize.width() : _renderSize.height(); - glViewport(_renderSize.width() / 2 - size / 2, _renderSize.height() / 2 - size / 2, size, size); + glViewport(0, 0, _renderSize.width(), _renderSize.height()); } void Renderer2D::endRender() @@ -52,4 +53,124 @@ void Renderer2D::endRender() #endif } +QRectF Renderer2D::getZoomRectangle() const +{ + return _zoomRectangle; +} + +void Renderer2D::setZoomRectangle(const QRectF& zoomRectangle) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << zoomRectangle; +#endif + + if (zoomRectangle == _zoomRectangle) + return; + + const auto previousZoomRectangle = _zoomRectangle; + + _zoomRectangle = zoomRectangle; + + emit zoomRectangleChanged(previousZoomRectangle, _zoomRectangle); +} + +QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const +{ + return QVector3D(screenPoint.x(), getRenderSize().height() - screenPoint.y(), 0).unproject(modelViewMatrix, getProjectionMatrix(), QRect(0, 0, getRenderSize().width(), getRenderSize().height())); +} + +QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const +{ + const auto clipSpacePos = getProjectionMatrix() * (getViewMatrix() * QVector4D(position, 1.0)); + return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); +} + +QPoint Renderer2D::getWorldPositionToScreenPoint(const QVector3D& position) const +{ + const auto normalizedScreenPoint = QVector2D(1.0f, -1.0f) * getWorldPositionToNormalizedScreenPoint(position); + const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + + return (viewSize * ((QVector2D(1.0f, 1.0f) + normalizedScreenPoint) / 2.0f)).toPoint(); +} + +QVector2D Renderer2D::getScreenPointToNormalizedScreenPoint(const QVector2D& screenPoint) const +{ + const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + return QVector2D(-1.f, -1.f) + 2.f * (QVector2D(screenPoint.x(), getRenderSize().height() - screenPoint.y()) / viewSize); +} + +QMatrix4x4 Renderer2D::getScreenToNormalizedScreenMatrix() const +{ + QMatrix4x4 translate, scale; + + translate.translate(-1.0f, -1.0f, 0.0f); + scale.scale(2.0f / static_cast(getRenderSize().width()), 2.0f / static_cast(getRenderSize().height()), 1.0f); + + return translate * scale; +} + +QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const +{ + QMatrix4x4 translate, scale; + + const auto size = QSizeF(getRenderSize()); + const auto halfSize = 0.5f * size; + + scale.scale(halfSize.width(), halfSize.height(), 1.0f); + translate.translate(size.width(), 1, 0.0f); + + return translate * scale; +} + +QMatrix4x4 Renderer2D::getViewMatrix() const +{ + QMatrix4x4 lookAt, scale; + + // Construct look-at parameters + const auto eye = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 1); + const auto center = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 0); + const auto up = QVector3D(0, 1, 0); + + // Create look-at transformation matrix + lookAt.lookAt(eye, center, up); + + const auto viewerSize = getRenderSize(); + const auto factorX = static_cast(viewerSize.width()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.width()) : 1.0f); + const auto factorY = static_cast(viewerSize.height()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.height()) : 1.0f); + const auto scaleFactor = factorX < factorY ? factorX : factorY; + + const auto d = 1.0f - (2 * _zoomMargin) / std::max(viewerSize.width(), viewerSize.height()); + + // Create scale matrix + scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); + + // Return composite matrix of scale and look-at transformation matrix + return scale * lookAt; +} + +QMatrix4x4 Renderer2D::getProjectionMatrix() const +{ + // Compute half of the widget size + const auto halfSize = getRenderSize() / 2; + + QMatrix4x4 matrix; + + // Create an orthogonal transformation matrix + matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); + + return matrix; +} + +QRect Renderer2D::getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const +{ + // Compute screen bounding rectangle extremes + const auto topLeftScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.bottomLeft())); + const auto bottomRightScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.topRight())); + + return { + topLeftScreen, + bottomRightScreen + }; +} + } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index de1999869..32ccb415b 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -7,6 +7,8 @@ #include "Renderer.h" #include "Navigator2D.h" +#include + namespace mv { @@ -36,6 +38,7 @@ Q_OBJECT /** * Resize the renderer to \p renderSize + * * @param renderSize New size of the renderer */ void resize(QSize renderSize) override; @@ -53,6 +56,55 @@ Q_OBJECT */ Navigator2D& getNavigator(); +public: // Coordinate conversions + + /** + * Convert \p screenPoint to point in world coordinates using \p modelViewMatrix + * @param modelViewMatrix Model-view matrix + * @param screenPoint Point in screen coordinates [0..width, 0..height] + * @return Position in world coordinates + */ + QVector3D getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const; + + /** + * Convert \p position in world coordinates to point in normalized screen coordinates + * @param position Position in world coordinates + * @return Point in normalized screen coordinates [-1..1, -1..1] + */ + QVector2D getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const; + + /** + * Convert \p position in world coordinates to point in screen coordinates + * @param position Position in world coordinates + * @return Point in screen coordinates [0..width, 0..height] + */ + QPoint getWorldPositionToScreenPoint(const QVector3D& position) const; + + /** + * Convert \p screenPoint to point in normalized screen coordinates + * @param screenPoint Point in screen coordinates [0..width, 0..height] + * @return Point in normalized screen coordinates [-1..1, -1..1] + */ + QVector2D getScreenPointToNormalizedScreenPoint(const QVector2D& screenPoint) const; + + /** Returns the matrix that converts screen coordinates [0..width, 0..height] to normalized screen coordinates [-1..1, -1..1] */ + QMatrix4x4 getScreenToNormalizedScreenMatrix() const; + + /** Returns the matrix that converts normalized screen coordinates [-1..1, -1..1] to screen coordinates [0..width, 0..height] */ + QMatrix4x4 getNormalizedScreenToScreenMatrix() const; + + /** Returns the view matrix */ + QMatrix4x4 getViewMatrix() const; + + /** Returns the projection matrix */ + QMatrix4x4 getProjectionMatrix() const; + + /** + * Get screen bounding rectangle from world bounding rectangle + * @param worldBoundingRectangle World bounding rectangle + */ + QRect getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const; + protected: /** Begin rendering */ @@ -61,9 +113,32 @@ Q_OBJECT /** End rendering */ void endRender() override; + /** + * Get the zoom rectangle + * @return Zoom rectangle + */ + QRectF getZoomRectangle() const; + + /** + * Set the zoom rectangle to \p zoomRectangle + * @param zoomRectangle Zoom rectangle + */ + void setZoomRectangle(const QRectF& zoomRectangle); + +signals: + + /** + * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle + * @param previousZoomRectangle Previous zoom rectangle + * @param currentZoomRectangle Current zoom rectangle + */ + void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); + private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + float _zoomMargin = 100.f; /** Margin for zooming */ + QRectF _zoomRectangle; /** Zoom rectangle */ friend class Navigator2D; }; From ecacae008305334fd78bb1b69ce0c116d297db2e Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 21 Mar 2025 17:51:34 +0100 Subject: [PATCH 51/89] Zooming around and panning is working properly --- ManiVault/res/shaders/PointPlot.frag | 3 + ManiVault/res/shaders/PointPlot.vert | 8 +- ManiVault/src/graphics/Shader.cpp | 6 +- ManiVault/src/graphics/Shader.h | 2 + ManiVault/src/renderers/Navigator2D.cpp | 116 +++++++++------------- ManiVault/src/renderers/Navigator2D.h | 40 +++++--- ManiVault/src/renderers/PointRenderer.cpp | 82 ++------------- ManiVault/src/renderers/PointRenderer.h | 31 +----- ManiVault/src/renderers/Renderer2D.cpp | 46 ++++----- ManiVault/src/renderers/Renderer2D.h | 20 +++- 10 files changed, 133 insertions(+), 221 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.frag b/ManiVault/res/shaders/PointPlot.frag index 7be52f9ce..e6263aa35 100644 --- a/ManiVault/res/shaders/PointPlot.frag +++ b/ManiVault/res/shaders/PointPlot.frag @@ -59,6 +59,9 @@ float normalize(float minPixelValue, float maxPixelValue, float pixelValue) void main() { + fragColor = vec4(1, 0, 0, 1); + return; + bool isSelectionHighlighted = vHighlight == 1; bool isFocusHighlighted = vFocusHighlight == 1; bool isHighlighted = isSelectionHighlighted || isFocusHighlighted; diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 613951f56..3561a62f4 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -16,7 +16,7 @@ uniform float pointSize; /** Point size */ uniform float pointSizeScale; /** Scale factor in absolute point size mode */ uniform int scalarEffect; uniform float pointOpacity; /** Point opacity */ -uniform mat3 orthoM; /** Projection matrix from bounds space to clip space */ +uniform mat4 mvp; /** Projection matrix from bounds space to clip space */ uniform bool hasHighlights; /** Whether a highlight buffer is used */ uniform bool hasFocusHighlights; /** Whether a focus highlight buffer is used */ uniform bool hasScalars; /** Whether a scalar buffer is used */ @@ -103,8 +103,6 @@ float floatConstruct( uint m ) { return f - 1.0; // Range [0:1] } - - // Pseudo-random value in half-open range [0:1]. float random( float x ) { return floatConstruct(hash(floatBitsToUint(x))); } float random( vec2 v ) { return floatConstruct(hash(floatBitsToUint(v))); } @@ -129,10 +127,8 @@ void main() if (hasOpacities) vOpacity = opacity; - vPosOrig = position; - // Transform position to clip space - vec2 pos = (orthoM * vec4(position, 0, 1)).xy; + vec2 pos = (mvp * vec4(position, 0, 1)).xy; // Resize point quad according to properties vec2 scaledVertex = vertex * pointSize * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); diff --git a/ManiVault/src/graphics/Shader.cpp b/ManiVault/src/graphics/Shader.cpp index 275037cb8..7be631a37 100644 --- a/ManiVault/src/graphics/Shader.cpp +++ b/ManiVault/src/graphics/Shader.cpp @@ -160,9 +160,9 @@ void ShaderProgram::uniformMatrix3f(const char* name, float* data) { glUniformMatrix3fv(location(name), 1, false, data); } -//void Shader::uniformMatrix4f(const char* name, Matrix4f& m) { -// glUniformMatrix4fv(location(name), 1, false, m.toArray()); -//} +void ShaderProgram::uniformMatrix4f(const char* name, float* data) { + glUniformMatrix4fv(location(name), 1, false, data); +} void ShaderProgram::uniformMatrix4f(const char* name, const float* const m) { diff --git a/ManiVault/src/graphics/Shader.h b/ManiVault/src/graphics/Shader.h index 0cf9e07d7..b8fb2648f 100644 --- a/ManiVault/src/graphics/Shader.h +++ b/ManiVault/src/graphics/Shader.h @@ -39,6 +39,8 @@ class CORE_EXPORT ShaderProgram : protected QOpenGLFunctions_3_3_Core void uniformMatrix3f(const char* name, float* data); + void uniformMatrix4f(const char* name, float* data); + //void uniformMatrix4f(const char* name, Matrix4f& m); void uniformMatrix4f(const char* name, const float* const m); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index a5cb95e60..88aa60cf2 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -21,7 +21,9 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isNavigating(false), _isPanning(false), _isZooming(false), - _zoomFactor(1.0f) + _zoomFactor(1.0f), + _zoomRectangleSize(1000, 1000), + _zoomRectangleMargin(0.f) { } @@ -109,7 +111,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) const auto& currentMousePosition = _mousePositions[_mousePositions.size() - 1]; const auto panVector = currentMousePosition - previousMousePosition; - panBy(panVector); + panBy(-panVector); } } } @@ -118,6 +120,40 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) return QObject::eventFilter(watched, event); } +QMatrix4x4 Navigator2D::getViewMatrix() const +{ + QMatrix4x4 lookAt, scale; + + // Construct look-at parameters + const auto eye = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 1); + const auto center = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 0); + const auto up = QVector3D(0, 1, 0); + + // Create look-at transformation matrix + lookAt.lookAt(eye, center, up); + + const auto viewerSize = _renderer.getRenderSize(); + const auto factorX = static_cast(viewerSize.width()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().width()) : 1.0f); + const auto factorY = static_cast(viewerSize.height()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().height()) : 1.0f); + const auto scaleFactor = factorX < factorY ? factorX : factorY; + + const auto d = 1.0f - (2 * _zoomRectangleMargin) / std::max(viewerSize.width(), viewerSize.height()); + + // Create scale matrix + scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); + + // Return composite matrix of scale and look-at transformation matrix + return scale * lookAt; +} + +QRectF Navigator2D::getZoomRectangle() const +{ + return { + _zoomRectangleTopLeft, + _zoomRectangleSize + }; +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) @@ -129,26 +165,16 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) beginZooming(); { - _zoomFactor *= factor; - - computeZoomRectangle(); - - /* - const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), center).toPointF(); - const auto v1 = _renderer.getZoomRectangle().topLeft() - p1; + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); + const auto v1 = getZoomRectangle().topLeft() - p1; const auto v2 = v1 / factor; - auto zoomRectangle = _renderer.getZoomRectangle(); + _zoomRectangleTopLeft = p1 + v2; + _zoomRectangleSize = getZoomRectangle().size() / factor; - zoomRectangle.setTopLeft(p1 + v2); - zoomRectangle.setSize(zoomRectangle.size() / factor); - - _renderer.setZoomRectangle(zoomRectangle); - */ + _renderer.setZoomRectangle(getZoomRectangle()); } endZooming(); - - // _navigationAction.getZoomRectangleAction().setBounds(_dataRectangleAction.getBounds()); } void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) @@ -165,15 +191,6 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) } endZooming(); - - //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 Navigator2D::panBy(const QPointF& delta) @@ -187,46 +204,14 @@ void Navigator2D::panBy(const QPointF& delta) beginPanning(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), QPoint()).toPointF(); - const auto p2 = _renderer.getScreenPointToWorldPosition(_renderer.getViewMatrix(), delta.toPoint()).toPointF(); - - //auto zoomRectangle = _renderer.getZoomRectangle(); - - //zoomRectangle.setTopLeft(zoomRectangle.topLeft() + (p2 - p1)); - - //_renderer.setZoomRectangle(zoomRectangle); + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _panCoordinates -= QVector2D(p2 - p1) / _zoomFactor; + _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); - computeZoomRectangle(); + _renderer.setZoomRectangle(getZoomRectangle()); } endPanning(); - - //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(); } void Navigator2D::resetView() @@ -377,11 +362,4 @@ void Navigator2D::endNavigation() emit navigationEnded(); } -void Navigator2D::computeZoomRectangle() const -{ - QRectF zoomRectangle(_panCoordinates.toPointF(), _zoomFactor * _renderer.getRenderSize()); - - _renderer.setZoomRectangle(zoomRectangle); -} - } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index a4331a84d..d5f1c9918 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -6,6 +6,7 @@ #include #include +#include namespace mv { @@ -49,6 +50,18 @@ Q_OBJECT */ bool eventFilter(QObject* watched, QEvent* event) override; + /** + * Get the view matrix + * @return View matrix + */ + QMatrix4x4 getViewMatrix() const; + + /** + * Get the zoom rectangle + * @return Zoom rectangle + */ + QRectF getZoomRectangle() const; + public: // Navigation /** @@ -129,11 +142,6 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); -private: - - /** Compute the zoom rectangle in world space */ - void computeZoomRectangle() const; - signals: /** Signals that panning has started */ @@ -173,15 +181,19 @@ Q_OBJECT void isNavigatingChanged(bool isNavigating); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QVector2D _panCoordinates; /** Pan coordinates in world space */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + +private: + QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ + QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 4faa1b9d8..a7af9b170 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -10,24 +10,6 @@ namespace mv { namespace gui { - namespace - { - /** - * Builds an orthographic projection matrix that transforms the given bounds - * to the range [-1, 1] in both directions. - */ - Matrix3f createProjectionMatrix(const Bounds& bounds) - { - Matrix3f m; - m.setIdentity(); - m[0] = 2 / bounds.getWidth(); - m[4] = 2 / bounds.getHeight(); - m[6] = -((bounds.getRight() + bounds.getLeft()) / bounds.getWidth()); - m[7] = -((bounds.getTop() + bounds.getBottom()) / bounds.getHeight()); - return m; - } - } - void PointArrayObject::init() { initializeOpenGLFunctions(); @@ -328,42 +310,6 @@ namespace mv _colormap.loadFromImage(image); } - Bounds PointRenderer::getBounds() const - { - return getViewBounds(); - } - - Bounds PointRenderer::getViewBounds() const - { - return _boundsView; - } - - Bounds PointRenderer::getDataBounds() const - { - return _boundsData; - } - - void PointRenderer::setBounds(const Bounds& bounds) - { - setViewBounds(bounds); - setDataBounds(bounds); - } - - void PointRenderer::setViewBounds(const Bounds& boundsView) - { - _boundsView = boundsView; - } - - void PointRenderer::setDataBounds(const Bounds& boundsData) - { - _boundsData = boundsData; - } - - Matrix3f PointRenderer::getProjectionMatrix() const - { - return _orthoM; - } - const PointArrayObject& PointRenderer::getGpuPoints() const { return _gpuPoints; @@ -484,41 +430,29 @@ namespace mv { beginRender(); { - qDebug() << getZoomRectangle(); - - const float aspectRatio = getRenderSize().width() / static_cast(getRenderSize().height()); - - QMatrix4x4 projection; - + _shader.bind(); - projection.ortho(aspectRatio * getZoomRectangle().left(), aspectRatio * getZoomRectangle().right(), getZoomRectangle().bottom(), getZoomRectangle().top(), -1.f, 1.f); + QMatrix4x4 modelMatrix; - // World to clip transformation - _orthoM = createProjectionMatrix(Bounds(getZoomRectangle().left(), getZoomRectangle().right(), getZoomRectangle().top(), getZoomRectangle().bottom())); - - _shader.bind(); + modelMatrix.setToIdentity(); + const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; - const auto size = std::max(getRenderSize().width(), getRenderSize().height()); + const auto size = static_cast(std::max(getRenderSize().width(), getRenderSize().height())); _shader.uniform1f("pointSize", _pointSettings._pointSize); - _shader.uniform1f("pointSizeScale", absoluteRendering ? (1.0 / size) : 1.0f / size); - - _shader.uniformMatrix3f("orthoM", _orthoM); + _shader.uniform1f("pointSizeScale", absoluteRendering ? 1.0f / size : 1.0f / size); + _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); - - _shader.uniform4f("dataBounds", _boundsData.getLeft(), _boundsData.getRight(), _boundsData.getBottom(), _boundsData.getTop()); - + _shader.uniform4f("dataBounds", getDataRectangle().left(), getDataRectangle().right(), getDataRectangle().bottom(), getDataRectangle().top()); _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); _shader.uniform1i("selectionOutlineOverrideColor", _selectionOutlineOverrideColor); _shader.uniform1f("selectionOutlineOpacity", _selectionOutlineOpacity); _shader.uniform1i("selectionHaloEnabled", _selectionHaloEnabled); - _shader.uniform1i("randomizedDepthEnabled", _randomizedDepthEnabled); - _shader.uniform1i("hasHighlights", _gpuPoints.hasHighlights()); _shader.uniform1i("hasFocusHighlights", _gpuPoints.hasFocusHighlights()); _shader.uniform1i("hasScalars", _gpuPoints.hasColorScalars()); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 20f42507c..306f7f7d2 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -78,6 +78,9 @@ namespace mv } void draw(); + + void updateQuadVertices(const QSizeF& quadSize); + void destroy(); private: @@ -113,6 +116,8 @@ namespace mv bool _dirtySizeScalars = false; bool _dirtyOpacityScalars = false; bool _dirtyColors = false; + + BufferObject _quadBufferObject; }; struct CORE_EXPORT PointSettings @@ -145,26 +150,6 @@ namespace mv void setColormap(const QImage& image); - // Returns getViewBounds() - Bounds getBounds() const; - - // Retuns _boundsView - Bounds getViewBounds() const; - - // Returns _boundsData - Bounds getDataBounds() const; - - // Calls both setViewBounds() and setDataBounds() - void setBounds(const Bounds& bounds); - - // sets _boundsView, used for computing the projection matrix _orthoM - void setViewBounds(const Bounds& boundsView); - - // sets _boundsData, used for scaling the 2d _colormap - void setDataBounds(const Bounds& boundsData); - - Matrix3f getProjectionMatrix() const; - const PointArrayObject& getGpuPoints() const; std::int32_t getNumSelectedPoints() const; @@ -221,14 +206,8 @@ namespace mv /* Rendering variables */ ShaderProgram _shader; - PointArrayObject _gpuPoints; Texture2D _colormap; /** 2D colormap, sets point color based on point position */ - - Matrix3f _orthoM = {}; /** Projection matrix from bounds space to clip space */ - Bounds _boundsView = Bounds(-1, 1, -1, 1); /** Used for computing the projection matrix _orthoM */ - Bounds _boundsData = Bounds(-1, 1, -1, 1); /** Used for scaling the 2d _colormap */ - std::int32_t _numSelectedPoints = 0; /** Number of selected (highlighted points) */ std::int32_t _numberOfFocusHighlights = 0; /** Number of focus highlights */ }; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 60f22d9c0..3397e7f39 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -16,7 +16,7 @@ namespace mv Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), _navigator(*this), - _zoomRectangle(0, 0, 200, 100) + _zoomRectangle(0, 0, 2000, 1000) { } @@ -41,8 +41,6 @@ void Renderer2D::beginRender() qDebug() << __FUNCTION__; #endif - const auto size = _renderSize.width() < _renderSize.height() ? _renderSize.width() : _renderSize.height(); - glViewport(0, 0, _renderSize.width(), _renderSize.height()); } @@ -74,6 +72,19 @@ void Renderer2D::setZoomRectangle(const QRectF& zoomRectangle) emit zoomRectangleChanged(previousZoomRectangle, _zoomRectangle); } +QRectF Renderer2D::getDataRectangle() const +{ + return _dataRectangle; +} + +void Renderer2D::setDataRectangle(const QRectF& dataRectangle) +{ + if (dataRectangle == _dataRectangle) + return; + + _dataRectangle = dataRectangle; +} + QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const { return QVector3D(screenPoint.x(), getRenderSize().height() - screenPoint.y(), 0).unproject(modelViewMatrix, getProjectionMatrix(), QRect(0, 0, getRenderSize().width(), getRenderSize().height())); @@ -81,7 +92,7 @@ QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewM QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const { - const auto clipSpacePos = getProjectionMatrix() * (getViewMatrix() * QVector4D(position, 1.0)); + const auto clipSpacePos = getProjectionMatrix() * (_navigator.getViewMatrix() * QVector4D(position, 1.0)); return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); } @@ -122,30 +133,13 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const return translate * scale; } -QMatrix4x4 Renderer2D::getViewMatrix() const +float Renderer2D::getZoomPercentage() const { - QMatrix4x4 lookAt, scale; - - // Construct look-at parameters - const auto eye = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 1); - const auto center = QVector3D(_zoomRectangle.center().x(), _zoomRectangle.center().y(), 0); - const auto up = QVector3D(0, 1, 0); - - // Create look-at transformation matrix - lookAt.lookAt(eye, center, up); - - const auto viewerSize = getRenderSize(); - const auto factorX = static_cast(viewerSize.width()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.width()) : 1.0f); - const auto factorY = static_cast(viewerSize.height()) / (_zoomRectangle.isValid() ? static_cast(_zoomRectangle.height()) : 1.0f); - const auto scaleFactor = factorX < factorY ? factorX : factorY; - - const auto d = 1.0f - (2 * _zoomMargin) / std::max(viewerSize.width(), viewerSize.height()); - - // Create scale matrix - scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); + const auto factorX = static_cast(getDataRectangle().width()) / static_cast(getZoomRectangle().width()); + const auto factorY = static_cast(getDataRectangle().height()) / static_cast(getZoomRectangle().height()); + const auto scaleFactor = factorX < factorY ? factorX : factorY; - // Return composite matrix of scale and look-at transformation matrix - return scale * lookAt; + return scaleFactor; } QMatrix4x4 Renderer2D::getProjectionMatrix() const diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 32ccb415b..dece49d36 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -93,10 +93,9 @@ Q_OBJECT /** Returns the matrix that converts normalized screen coordinates [-1..1, -1..1] to screen coordinates [0..width, 0..height] */ QMatrix4x4 getNormalizedScreenToScreenMatrix() const; - /** Returns the view matrix */ - QMatrix4x4 getViewMatrix() const; + float getZoomPercentage() const; - /** Returns the projection matrix */ +/** Returns the projection matrix */ QMatrix4x4 getProjectionMatrix() const; /** @@ -125,6 +124,20 @@ Q_OBJECT */ void setZoomRectangle(const QRectF& zoomRectangle); +public: + + /** + * Get data rectangle + * @return Data rectangle + */ + QRectF getDataRectangle() const; + + /** + * Set data rectangle to \p dataRectangle + * @param dataRectangle Data rectangle + */ + void setDataRectangle(const QRectF& dataRectangle); + signals: /** @@ -139,6 +152,7 @@ Q_OBJECT Navigator2D _navigator; /** 2D navigator */ float _zoomMargin = 100.f; /** Margin for zooming */ QRectF _zoomRectangle; /** Zoom rectangle */ + QRectF _dataRectangle; /** Data rectangle */ friend class Navigator2D; }; From 44f24cee479be702b9c585a9ce91ad7bbead4dd6 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 17:01:25 +0100 Subject: [PATCH 52/89] Work on Renderer2D --- ManiVault/res/shaders/PointPlot.vert | 38 +++++++++++++++++++---- ManiVault/src/renderers/Navigator2D.cpp | 7 +++++ ManiVault/src/renderers/Navigator2D.h | 6 ++++ ManiVault/src/renderers/PointRenderer.cpp | 6 +++- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 3561a62f4..9c20a5d34 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -11,9 +11,11 @@ #define EFFECT_OUTLINE 3 #define EFFECT_COLOR_2D 4 + + // Point properties -uniform float pointSize; /** Point size */ -uniform float pointSizeScale; /** Scale factor in absolute point size mode */ +uniform float pointSize; /** Point size in x- and y direction to account for anisotropy of the render canvas */ +uniform vec2 viewportSize; /** (width, height) of viewport */ uniform int scalarEffect; uniform float pointOpacity; /** Point opacity */ uniform mat4 mvp; /** Projection matrix from bounds space to clip space */ @@ -42,6 +44,9 @@ uniform float focusOutlineOpacity; /** Focus outline opacity */ uniform bool randomizedDepthEnabled; /** Whether to randomize the z-order */ +// Miscellaneous +uniform float windowAspectRatio; /** Window aspect ratio (width / height) */ + layout(location = 0) in vec2 vertex; /** Vertex input, always a [-1, 1] quad */ layout(location = 1) in vec2 position; /** 2-Dimensional positions of points */ layout(location = 2) in int highlight; /** Mask of highlights over the points */ @@ -113,7 +118,25 @@ void main() { // The texture coordinates match vertex coordinates vTexCoord = vertex; - + + // Convert quad size from pixels to normalized device coordinates (NDC) + vec2 pixelSize = vec2(pointSize) / viewportSize; + + // Apply aspect ratio correction + pixelSize.x *= viewportSize.y / viewportSize.x; // Correct X-axis scaling + + //vec2 scaledPos = vertex * pixelSize; + //vec2 finalPos = position + scaledPos; + + // Apply projection only to the instance position, NOT to the quad size + vec4 worldPos = mvp * vec4(position, 0.0, 1.0); + + // Keep quad size in screen-space (do NOT apply projection to `pixelSize`) + vec2 finalPos = worldPos.xy + (vertex * pixelSize); + + gl_Position = vec4(finalPos, 0.0, 1.0); + + /* // Selection and focus highlighting vHighlight = hasHighlights ? highlight : 0; vFocusHighlight = hasFocusHighlights ? focusHighlight : 0; @@ -126,16 +149,19 @@ void main() if (hasOpacities) vOpacity = opacity; - + + vec2 windowAspectRatioCorrection = vec2(windowAspectRatio, 1); + // Transform position to clip space vec2 pos = (mvp * vec4(position, 0, 1)).xy; // Resize point quad according to properties - vec2 scaledVertex = vertex * pointSize * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); + vec2 scaledVertex = vertex * pointSize * windowAspectRatioCorrection * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); if (hasSizes) - scaledVertex = vertex * size * pointSizeScale * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); + scaledVertex = vertex * size * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); // Move quad by position and output gl_Position = vec4(scaledVertex + pos, randomizedDepthEnabled ? random(pos) : 0, 1); + */ } diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 88aa60cf2..1a3a2db10 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -154,6 +154,11 @@ QRectF Navigator2D::getZoomRectangle() const }; } +float Navigator2D::getZoomFactor() const +{ + return _zoomFactor; +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) @@ -172,6 +177,8 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) _zoomRectangleTopLeft = p1 + v2; _zoomRectangleSize = getZoomRectangle().size() / factor; + _zoomFactor /= factor; + _renderer.setZoomRectangle(getZoomRectangle()); } endZooming(); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index d5f1c9918..233094eab 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -62,6 +62,12 @@ Q_OBJECT */ QRectF getZoomRectangle() const; + /** + * Get the zoom factor + * @return Zoom factor + */ + float getZoomFactor() const; + public: // Navigation /** diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index a7af9b170..3c13df967 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -439,9 +439,13 @@ namespace mv const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; const auto size = static_cast(std::max(getRenderSize().width(), getRenderSize().height())); + const auto windowAspectRatio = static_cast(getRenderSize().width()) / static_cast(getRenderSize().height()); + const auto pointSizeNDC = QVector2D(_pointSettings._pointSize / size, _pointSettings._pointSize / size); + + qDebug() << "zoom factor: " << getNavigator().getZoomFactor() << "windowAspectRatio" << windowAspectRatio; _shader.uniform1f("pointSize", _pointSettings._pointSize); - _shader.uniform1f("pointSizeScale", absoluteRendering ? 1.0f / size : 1.0f / size); + _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); From 7c984a269cabe922273108419d583d9fcbc107a4 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 17:15:41 +0100 Subject: [PATCH 53/89] Fixed point scaling --- ManiVault/res/shaders/PointPlot.vert | 20 +++++++++++++++++++- ManiVault/src/renderers/PointRenderer.cpp | 5 ----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 9c20a5d34..9832a4f28 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -116,6 +116,21 @@ float random( vec4 v ) { return floatConstruct(hash(floatBitsToUint(v))); } void main() { + // Convert quad size from pixels to normalized device coordinates (NDC) + vec2 pixelSize = vec2(pointSize) / viewportSize; + + // Apply projection only to the instance position, NOT to the quad size + vec4 worldPos = mvp * vec4(position, 0.0, 1.0); + + // Aspect ratio correction for screen-space scaling + vec2 aspectCorrection = vec2(1.0, 1.0); + + // Keep quad size in screen-space while maintaining correct aspect ratio + vec2 finalPos = worldPos.xy + (vertex * pixelSize * aspectCorrection); + + gl_Position = vec4(finalPos, 0.0, 1.0); // Convert to NDC [-1,1] + +/* // The texture coordinates match vertex coordinates vTexCoord = vertex; @@ -128,6 +143,9 @@ void main() //vec2 scaledPos = vertex * pixelSize; //vec2 finalPos = position + scaledPos; + // Aspect ratio correction for screen-space scaling + vec2 aspectCorrection = vec2(viewportSize.y / viewportSize.x, 1.0f); + // Apply projection only to the instance position, NOT to the quad size vec4 worldPos = mvp * vec4(position, 0.0, 1.0); @@ -135,7 +153,7 @@ void main() vec2 finalPos = worldPos.xy + (vertex * pixelSize); gl_Position = vec4(finalPos, 0.0, 1.0); - +*/ /* // Selection and focus highlighting vHighlight = hasHighlights ? highlight : 0; diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 3c13df967..eb9535d87 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -438,11 +438,6 @@ namespace mv const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; - const auto size = static_cast(std::max(getRenderSize().width(), getRenderSize().height())); - const auto windowAspectRatio = static_cast(getRenderSize().width()) / static_cast(getRenderSize().height()); - const auto pointSizeNDC = QVector2D(_pointSettings._pointSize / size, _pointSettings._pointSize / size); - - qDebug() << "zoom factor: " << getNavigator().getZoomFactor() << "windowAspectRatio" << windowAspectRatio; _shader.uniform1f("pointSize", _pointSettings._pointSize); _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); From ce6d270245f9d73ca848a29b5cf0a785522e9568 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 17:41:16 +0100 Subject: [PATCH 54/89] Fixed shaders --- ManiVault/res/shaders/PointPlot.frag | 3 -- ManiVault/res/shaders/PointPlot.vert | 72 ++++++++-------------------- 2 files changed, 21 insertions(+), 54 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.frag b/ManiVault/res/shaders/PointPlot.frag index e6263aa35..7be52f9ce 100644 --- a/ManiVault/res/shaders/PointPlot.frag +++ b/ManiVault/res/shaders/PointPlot.frag @@ -59,9 +59,6 @@ float normalize(float minPixelValue, float maxPixelValue, float pixelValue) void main() { - fragColor = vec4(1, 0, 0, 1); - return; - bool isSelectionHighlighted = vHighlight == 1; bool isFocusHighlighted = vFocusHighlight == 1; bool isHighlighted = isSelectionHighlighted || isFocusHighlighted; diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index 9832a4f28..c2cc95ef9 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -116,70 +116,40 @@ float random( vec4 v ) { return floatConstruct(hash(floatBitsToUint(v))); } void main() { + // Use normalized quad vertices as texture coordinates + vTexCoord = vertex; + // Convert quad size from pixels to normalized device coordinates (NDC) vec2 pixelSize = vec2(pointSize) / viewportSize; // Apply projection only to the instance position, NOT to the quad size vec4 worldPos = mvp * vec4(position, 0.0, 1.0); - - // Aspect ratio correction for screen-space scaling - vec2 aspectCorrection = vec2(1.0, 1.0); - - // Keep quad size in screen-space while maintaining correct aspect ratio - vec2 finalPos = worldPos.xy + (vertex * pixelSize * aspectCorrection); - - gl_Position = vec4(finalPos, 0.0, 1.0); // Convert to NDC [-1,1] -/* - // The texture coordinates match vertex coordinates - vTexCoord = vertex; + vec2 scaledVertex; - // Convert quad size from pixels to normalized device coordinates (NDC) - vec2 pixelSize = vec2(pointSize) / viewportSize; - - // Apply aspect ratio correction - pixelSize.x *= viewportSize.y / viewportSize.x; // Correct X-axis scaling + if (hasSizes) + scaledVertex = vertex * (vec2(size) / viewportSize); + else + scaledVertex = vertex * pixelSize; + + // Scale the vertex based on the selection display mode + scaledVertex *= ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); - //vec2 scaledPos = vertex * pixelSize; - //vec2 finalPos = position + scaledPos; - - // Aspect ratio correction for screen-space scaling - vec2 aspectCorrection = vec2(viewportSize.y / viewportSize.x, 1.0f); + // Keep quad size in screen-space while maintaining correct aspect ratio + vec2 finalPos = worldPos.xy + scaledVertex; - // Apply projection only to the instance position, NOT to the quad size - vec4 worldPos = mvp * vec4(position, 0.0, 1.0); + // Compute random depth + float depth = randomizedDepthEnabled ? random(worldPos.xy) : 0; - // Keep quad size in screen-space (do NOT apply projection to `pixelSize`) - vec2 finalPos = worldPos.xy + (vertex * pixelSize); + // Set the final position + gl_Position = vec4(finalPos, depth, 1.0); // Convert to NDC [-1,1] - gl_Position = vec4(finalPos, 0.0, 1.0); -*/ - /* - // Selection and focus highlighting - vHighlight = hasHighlights ? highlight : 0; + vHighlight = hasHighlights ? highlight : 0; vFocusHighlight = hasFocusHighlights ? focusHighlight : 0; - - vScalar = hasScalars ? (scalar - colorMapRange.x) / colorMapRange.z : 0; - - vColor = hasColors ? color : vec3(0.5); - - vOpacity = pointOpacity; + vScalar = hasScalars ? (scalar - colorMapRange.x) / colorMapRange.z : 0; + vColor = hasColors ? color : vec3(0.5); + vOpacity = pointOpacity; if (hasOpacities) vOpacity = opacity; - - vec2 windowAspectRatioCorrection = vec2(windowAspectRatio, 1); - - // Transform position to clip space - vec2 pos = (mvp * vec4(position, 0, 1)).xy; - - // Resize point quad according to properties - vec2 scaledVertex = vertex * pointSize * windowAspectRatioCorrection * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); - - if (hasSizes) - scaledVertex = vertex * size * ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); - - // Move quad by position and output - gl_Position = vec4(scaledVertex + pos, randomizedDepthEnabled ? random(pos) : 0, 1); - */ } From 24853c54b5d92a351fc3f73fddf7ee392147309f Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 18:19:32 +0100 Subject: [PATCH 55/89] Relative point sizing works --- ManiVault/res/shaders/PointPlot.vert | 17 ++++++++++------- ManiVault/src/renderers/PointRenderer.cpp | 3 ++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index c2cc95ef9..d0a1d4588 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -15,6 +15,7 @@ // Point properties uniform float pointSize; /** Point size in x- and y direction to account for anisotropy of the render canvas */ +uniform bool pointSizeAbsolute; /** Whether the point size is in world or screen coordinates */ uniform vec2 viewportSize; /** (width, height) of viewport */ uniform int scalarEffect; uniform float pointOpacity; /** Point opacity */ @@ -120,17 +121,16 @@ void main() vTexCoord = vertex; // Convert quad size from pixels to normalized device coordinates (NDC) - vec2 pixelSize = vec2(pointSize) / viewportSize; + vec2 pixelSize = vec2(hasSizes ? size : pointSize) / viewportSize; +// if (!pointSizeAbsolute) +// pixelSize /= viewportSize; + // Apply projection only to the instance position, NOT to the quad size vec4 worldPos = mvp * vec4(position, 0.0, 1.0); - vec2 scaledVertex; - - if (hasSizes) - scaledVertex = vertex * (vec2(size) / viewportSize); - else - scaledVertex = vertex * pixelSize; + // Compute the scaled vertex position + vec2 scaledVertex = vertex * pixelSize; // Scale the vertex based on the selection display mode scaledVertex *= ((selectionDisplayMode == 0) ? selectionOutlineScale : 1); @@ -138,6 +138,9 @@ void main() // Keep quad size in screen-space while maintaining correct aspect ratio vec2 finalPos = worldPos.xy + scaledVertex; +// if (pointSizeAbsolute) +// finalPos = (mvp * vec4(position + scaledVertex, 0.0, 1.0)).xy; + // Compute random depth float depth = randomizedDepthEnabled ? random(worldPos.xy) : 0; diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index eb9535d87..b7ae1ac14 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -437,9 +437,10 @@ namespace mv modelMatrix.setToIdentity(); const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; - const bool absoluteRendering = _pointSettings._scalingMode == PointScaling::Absolute; + const bool pointSizeAbsolute = _pointSettings._scalingMode == PointScaling::Absolute; _shader.uniform1f("pointSize", _pointSettings._pointSize); + _shader.uniform1i("pointSizeAbsolute", pointSizeAbsolute); _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); From 74410b1a0419623d4cdd60809efddf5c71c91d7f Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Sun, 23 Mar 2025 20:54:42 +0100 Subject: [PATCH 56/89] Navigation is more or less working properly now, end of day commit --- ManiVault/res/shaders/DensityDraw.frag | 3 + ManiVault/res/shaders/PointPlot.vert | 2 - ManiVault/res/shaders/Quad.vert | 4 +- ManiVault/src/renderers/DensityRenderer.cpp | 46 ++++---- ManiVault/src/renderers/DensityRenderer.h | 3 - ManiVault/src/renderers/Navigator2D.cpp | 111 ++++++++++++++++---- ManiVault/src/renderers/Navigator2D.h | 75 ++++++++++--- ManiVault/src/renderers/PointRenderer.cpp | 2 +- ManiVault/src/renderers/PointRenderer.h | 2 - ManiVault/src/renderers/Renderer2D.cpp | 55 +++++----- ManiVault/src/renderers/Renderer2D.h | 49 +++------ 11 files changed, 226 insertions(+), 126 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index d21a90ea8..30e8c189d 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -14,4 +14,7 @@ out vec4 fragColor; void main() { float f = 1 - (texture(tex, pass_texCoord).r * norm); fragColor = vec4(vec3(f), 1); + + if (pass_texCoord.x < 0.1 || pass_texCoord.x > 0.9) + fragColor = vec4(1,0,0,1); } diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index d0a1d4588..c263861d4 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -11,8 +11,6 @@ #define EFFECT_OUTLINE 3 #define EFFECT_COLOR_2D 4 - - // Point properties uniform float pointSize; /** Point size in x- and y direction to account for anisotropy of the render canvas */ uniform bool pointSizeAbsolute; /** Whether the point size is in world or screen coordinates */ diff --git a/ManiVault/res/shaders/Quad.vert b/ManiVault/res/shaders/Quad.vert index 2fd2f7175..14cbb55e2 100644 --- a/ManiVault/res/shaders/Quad.vert +++ b/ManiVault/res/shaders/Quad.vert @@ -4,9 +4,11 @@ #version 330 core +uniform mat4 mvp; + out vec2 pass_texCoord; void main() { pass_texCoord = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2); - gl_Position = vec4(pass_texCoord * 2 - 1, 0, 1); + gl_Position = mvp * vec4(pass_texCoord * 2 - 1, 0, 1); } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 04f0f238e..382200f72 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -12,7 +12,7 @@ namespace mv DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { - + getNavigator().setZoomRectangle(QRectF(0, 0, 1, 1)); } DensityRenderer::~DensityRenderer() @@ -88,29 +88,23 @@ namespace mv _densityComputation.init(QOpenGLContext::currentContext()); } - void DensityRenderer::resize(QSize renderSize) - { - int w = renderSize.width(); - int h = renderSize.height(); - - _windowSize.setWidth(w); - _windowSize.setHeight(h); - } - void DensityRenderer::render() { - glViewport(0, 0, _windowSize.width(), _windowSize.height()); - - int w = _windowSize.width(); - int h = _windowSize.height(); - int size = w < h ? w : h; - glViewport(w / 2 - size / 2, h / 2 - size / 2, size, size); - - // Draw density or isolines map - switch (_renderMode) { - case DENSITY: drawDensity(); break; - case LANDSCAPE: drawLandscape(); break; + beginRender(); + { + switch (_renderMode) { + case DENSITY: { + drawDensity(); + break; + } + + case LANDSCAPE: { + drawLandscape(); + break; + } + } } + endRender(); } void DensityRenderer::destroy() @@ -138,6 +132,16 @@ namespace mv _shaderDensityDraw.bind(); _densityComputation.getDensityTexture().bind(0); + + QMatrix4x4 modelMatrix; + + modelMatrix.setToIdentity(); + + const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; + + qDebug() << mvp; + + _shaderDensityDraw.uniformMatrix4f("mvp", mvp.data()); _shaderDensityDraw.uniform1i("tex", 0); _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 3ab56fd63..f2568a85f 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -49,7 +49,6 @@ namespace mv void setColormap(const QImage& image); void init() override; - void resize(QSize renderSize) override; void render() override; @@ -64,8 +63,6 @@ namespace mv void drawFullscreenQuad(); private: - QSize _windowSize; - bool _isSelecting = false; bool _hasColorMap = false; diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 1a3a2db10..fa96a18db 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -17,13 +17,15 @@ namespace mv Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : QObject(parent), _renderer(renderer), + _enabled(false), _initialized(false), _isNavigating(false), _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleSize(1000, 1000), - _zoomRectangleMargin(0.f) + _zoomRectangleSize(1, 1), + _zoomRectangleMargin(0.f), + _userHasNavigated() { } @@ -43,7 +45,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) bool Navigator2D::eventFilter(QObject* watched, QEvent* event) { - if (!_initialized) + if (!_initialized || !_enabled) return false; if (event->type() == QEvent::KeyPress) { @@ -154,11 +156,40 @@ QRectF Navigator2D::getZoomRectangle() const }; } +void Navigator2D::setZoomRectangle(const QRectF& zoomRectangle) +{ +#ifdef NAVIGATOR_2D_VERBOSE + qDebug() << __FUNCTION__ << zoomRectangle; +#endif + + const auto previousZoomRectangle = getZoomRectangle(); + + _zoomRectangleTopLeft = zoomRectangle.topLeft(); + _zoomRectangleSize = zoomRectangle.size(); + + emit zoomRectangleChanged(previousZoomRectangle, getZoomRectangle()); +} + float Navigator2D::getZoomFactor() const { return _zoomFactor; } +bool Navigator2D::isEnabled() const +{ + return _enabled; +} + +void Navigator2D::setEnabled(bool enabled) +{ + if (enabled == _enabled) + return; + + _enabled = enabled; + + emit enabledChanged(_enabled); +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) @@ -170,16 +201,20 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) beginZooming(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - const auto v1 = getZoomRectangle().topLeft() - p1; - const auto v2 = v1 / factor; + beginChangeZoomRectangle(); + { + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); + const auto v1 = getZoomRectangle().topLeft() - p1; + const auto v2 = v1 / factor; - _zoomRectangleTopLeft = p1 + v2; - _zoomRectangleSize = getZoomRectangle().size() / factor; + _zoomRectangleTopLeft = p1 + v2; + _zoomRectangleSize = getZoomRectangle().size() / factor; - _zoomFactor /= factor; + _zoomFactor /= factor; - _renderer.setZoomRectangle(getZoomRectangle()); + setZoomRectangle(getZoomRectangle()); + } + endChangeZoomRectangle(); } endZooming(); } @@ -195,7 +230,10 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) beginZooming(); { - + beginChangeZoomRectangle(); + { + } + endChangeZoomRectangle(); } endZooming(); } @@ -211,24 +249,42 @@ void Navigator2D::panBy(const QPointF& delta) beginPanning(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); - const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); + beginChangeZoomRectangle(); + { + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); - - _renderer.setZoomRectangle(getZoomRectangle()); + _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); + } + endChangeZoomRectangle(); } endPanning(); } -void Navigator2D::resetView() +void Navigator2D::resetView(bool force /*= true*/) { if (!_initialized) return; #ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__; + qDebug() << __FUNCTION__ << force; #endif + + beginZooming(); + { + beginChangeZoomRectangle(); + { + setZoomRectangle(_renderer.getDataBounds()); + + if (!_userHasNavigated || force) { + setZoomRectangle(_renderer.getDataBounds()); + + _userHasNavigated = false; + } + } + endChangeZoomRectangle(); + } + endZooming(); } bool Navigator2D::isPanning() const @@ -246,6 +302,11 @@ bool Navigator2D::isNavigating() const return _isNavigating; } +bool Navigator2D::hasUserNavigated() const +{ + return _userHasNavigated; +} + void Navigator2D::setIsPanning(bool isPanning) { if (!_initialized) @@ -296,6 +357,8 @@ void Navigator2D::beginPanning() setIsPanning(true); + _userHasNavigated = true; + emit panningStarted(); } @@ -324,6 +387,8 @@ void Navigator2D::beginZooming() setIsZooming(true); + _userHasNavigated = true; + emit zoomingStarted(); } @@ -369,4 +434,14 @@ void Navigator2D::endNavigation() emit navigationEnded(); } +void Navigator2D::beginChangeZoomRectangle() +{ + _previousZoomRectangle = getZoomRectangle(); +} + +void Navigator2D::endChangeZoomRectangle() +{ + emit zoomRectangleChanged(_previousZoomRectangle, getZoomRectangle()); +} + } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 233094eab..6cd1771a6 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -62,12 +62,30 @@ Q_OBJECT */ QRectF getZoomRectangle() const; + /** + * Set the zoom rectangle to \p zoomRectangle + * @param zoomRectangle Zoom rectangle + */ + void setZoomRectangle(const QRectF& zoomRectangle); + /** * Get the zoom factor * @return Zoom factor */ float getZoomFactor() const; + /** + * Get whether the navigator is enabled + * @return Boolean determining whether the navigator is enabled + */ + bool isEnabled() const; + + /** + * Set enabled to \p enabled + * @param enabled Boolean determining whether the navigator is enabled + */ + void setEnabled(bool enabled); + public: // Navigation /** @@ -89,8 +107,11 @@ Q_OBJECT */ void panBy(const QPointF& delta); - /** Zoom to extents of the data bounds (with a margin around it) */ - void resetView(); + /** + * Reset the view + * @param force Force reset event when the user has navigated + */ + void resetView(bool force = true); /** * Get whether the renderer is panning @@ -110,6 +131,12 @@ Q_OBJECT */ bool isNavigating() const; + /** + * Get whether the user has navigated + * @return Boolean determining whether the user has navigated + */ + bool hasUserNavigated() const; + protected: // Navigation /** @@ -148,6 +175,12 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); + /** Begin changing the zoom rectangle */ + void beginChangeZoomRectangle(); + + /** End changing the zoom rectangle */ + void endChangeZoomRectangle(); + signals: /** Signals that panning has started */ @@ -186,20 +219,34 @@ Q_OBJECT */ void isNavigatingChanged(bool isNavigating); -private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ + /** + * Signals that enabled changed to \p enabled + * @param enabled Boolean determining whether the navigator is enabled + */ + void enabledChanged(bool enabled); + + /** + * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle + * @param previousZoomRectangle Previous zoom rectangle + * @param currentZoomRectangle Current zoom rectangle + */ + void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); private: - QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ - QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ + QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ + QRectF _previousZoomRectangle; /** Previous zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index b7ae1ac14..1f30937fb 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -445,7 +445,7 @@ namespace mv _shader.uniformMatrix4f("mvp", mvp.data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); - _shader.uniform4f("dataBounds", getDataRectangle().left(), getDataRectangle().right(), getDataRectangle().bottom(), getDataRectangle().top()); + _shader.uniform4f("dataBounds", getDataBounds().left(), getDataBounds().right(), getDataBounds().bottom(), getDataBounds().top()); _shader.uniform1i("selectionDisplayMode", static_cast(_selectionDisplayMode)); _shader.uniform1f("selectionOutlineScale", _selectionOutlineScale); _shader.uniform3f("selectionOutlineColor", _selectionOutlineColor); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 306f7f7d2..b0ca4c781 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -79,8 +79,6 @@ namespace mv void draw(); - void updateQuadVertices(const QSizeF& quadSize); - void destroy(); private: diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 3397e7f39..4b944d7bd 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -16,7 +16,7 @@ namespace mv Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), _navigator(*this), - _zoomRectangle(0, 0, 2000, 1000) + _zoomMargin(10.f) { } @@ -35,6 +35,11 @@ Navigator2D& Renderer2D::getNavigator() return _navigator; } +const Navigator2D& Renderer2D::getNavigator() const +{ + return _navigator; +} + void Renderer2D::beginRender() { #ifdef RENDERER_2D_VERBOSE @@ -51,38 +56,24 @@ void Renderer2D::endRender() #endif } -QRectF Renderer2D::getZoomRectangle() const +QRectF Renderer2D::getDataBounds() const { - return _zoomRectangle; + return _dataBounds; } -void Renderer2D::setZoomRectangle(const QRectF& zoomRectangle) +void Renderer2D::setDataBounds(const QRectF& dataBounds) { #ifdef RENDERER_2D_VERBOSE - qDebug() << __FUNCTION__ << zoomRectangle; + qDebug() << __FUNCTION__ << dataBounds; #endif - if (zoomRectangle == _zoomRectangle) - return; - - const auto previousZoomRectangle = _zoomRectangle; - - _zoomRectangle = zoomRectangle; - - emit zoomRectangleChanged(previousZoomRectangle, _zoomRectangle); -} - -QRectF Renderer2D::getDataRectangle() const -{ - return _dataRectangle; -} - -void Renderer2D::setDataRectangle(const QRectF& dataRectangle) -{ - if (dataRectangle == _dataRectangle) + if (dataBounds == _dataBounds) return; - _dataRectangle = dataRectangle; + _dataBounds = dataBounds; + + //if (!_navigator.hasUserNavigated()) + getNavigator().setZoomRectangle(_dataBounds);//.marginsAdded({ _zoomMargin, _zoomMargin, _zoomMargin, _zoomMargin })); } QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const @@ -93,13 +84,14 @@ QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewM QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const { const auto clipSpacePos = getProjectionMatrix() * (_navigator.getViewMatrix() * QVector4D(position, 1.0)); + return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); } QPoint Renderer2D::getWorldPositionToScreenPoint(const QVector3D& position) const { - const auto normalizedScreenPoint = QVector2D(1.0f, -1.0f) * getWorldPositionToNormalizedScreenPoint(position); - const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + const auto normalizedScreenPoint = QVector2D(1.0f, -1.0f) * getWorldPositionToNormalizedScreenPoint(position); + const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); return (viewSize * ((QVector2D(1.0f, 1.0f) + normalizedScreenPoint) / 2.0f)).toPoint(); } @@ -107,6 +99,7 @@ QPoint Renderer2D::getWorldPositionToScreenPoint(const QVector3D& position) cons QVector2D Renderer2D::getScreenPointToNormalizedScreenPoint(const QVector2D& screenPoint) const { const auto viewSize = QVector2D(getRenderSize().width(), getRenderSize().height()); + return QVector2D(-1.f, -1.f) + 2.f * (QVector2D(screenPoint.x(), getRenderSize().height() - screenPoint.y()) / viewSize); } @@ -124,7 +117,7 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const { QMatrix4x4 translate, scale; - const auto size = QSizeF(getRenderSize()); + const auto size = QSizeF(getRenderSize()); const auto halfSize = 0.5f * size; scale.scale(halfSize.width(), halfSize.height(), 1.0f); @@ -135,8 +128,8 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const float Renderer2D::getZoomPercentage() const { - const auto factorX = static_cast(getDataRectangle().width()) / static_cast(getZoomRectangle().width()); - const auto factorY = static_cast(getDataRectangle().height()) / static_cast(getZoomRectangle().height()); + const auto factorX = static_cast(getDataBounds().width()) / static_cast(getNavigator().getZoomRectangle().width()); + const auto factorY = static_cast(getDataBounds().height()) / static_cast(getNavigator().getZoomRectangle().height()); const auto scaleFactor = factorX < factorY ? factorX : factorY; return scaleFactor; @@ -158,8 +151,8 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QRect Renderer2D::getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const { // Compute screen bounding rectangle extremes - const auto topLeftScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.bottomLeft())); - const auto bottomRightScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.topRight())); + const auto topLeftScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.bottomLeft())); + const auto bottomRightScreen = getWorldPositionToScreenPoint(QVector3D(worldBoundingRectangle.topRight())); return { topLeftScreen, diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index dece49d36..c66a210e5 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -51,11 +51,16 @@ Q_OBJECT /** * Get the 2D navigator - * * @return Reference to the 2D navigator */ Navigator2D& getNavigator(); + /** + * Get the 2D navigator + * @return Reference to the 2D navigator + */ + const Navigator2D& getNavigator() const; + public: // Coordinate conversions /** @@ -112,47 +117,25 @@ Q_OBJECT /** End rendering */ void endRender() override; - /** - * Get the zoom rectangle - * @return Zoom rectangle - */ - QRectF getZoomRectangle() const; - - /** - * Set the zoom rectangle to \p zoomRectangle - * @param zoomRectangle Zoom rectangle - */ - void setZoomRectangle(const QRectF& zoomRectangle); - public: /** - * Get data rectangle - * @return Data rectangle + * Get data bounds + * @return Data bounds */ - QRectF getDataRectangle() const; - - /** - * Set data rectangle to \p dataRectangle - * @param dataRectangle Data rectangle - */ - void setDataRectangle(const QRectF& dataRectangle); - -signals: + QRectF getDataBounds() const; /** - * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle - * @param previousZoomRectangle Previous zoom rectangle - * @param currentZoomRectangle Current zoom rectangle + * Set data bounds to \p dataBounds + * @param dataBounds Data bounds */ - void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); + void setDataBounds(const QRectF& dataBounds); private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ - float _zoomMargin = 100.f; /** Margin for zooming */ - QRectF _zoomRectangle; /** Zoom rectangle */ - QRectF _dataRectangle; /** Data rectangle */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + float _zoomMargin; /** Margin for zooming */ + QRectF _dataBounds; /** Bounds of the data */ friend class Navigator2D; }; From 8adb53cfb66aafd0e4d0a029ab450944f450d425 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 26 Mar 2025 07:55:01 +0100 Subject: [PATCH 57/89] Fixes problem with data bounds --- ManiVault/src/renderers/DensityRenderer.cpp | 2 +- ManiVault/src/renderers/Navigator2D.cpp | 81 ++++++++++++--------- ManiVault/src/renderers/Navigator2D.h | 56 +++++++------- ManiVault/src/renderers/PointRenderer.cpp | 2 +- ManiVault/src/renderers/Renderer2D.cpp | 8 +- 5 files changed, 79 insertions(+), 70 deletions(-) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 382200f72..1c871fca6 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -12,7 +12,7 @@ namespace mv DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { - getNavigator().setZoomRectangle(QRectF(0, 0, 1, 1)); + getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); } DensityRenderer::~DensityRenderer() diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index fa96a18db..17070b836 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -23,7 +23,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleSize(1, 1), + _zoomRectangleWorldSize(1, 1), _zoomRectangleMargin(0.f), _userHasNavigated() { @@ -126,17 +126,23 @@ QMatrix4x4 Navigator2D::getViewMatrix() const { QMatrix4x4 lookAt, scale; + const auto zoomRectangle = getZoomRectangleWorld(); + // Construct look-at parameters - const auto eye = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 1); - const auto center = QVector3D(getZoomRectangle().center().x(), getZoomRectangle().center().y(), 0); + const auto eye = QVector3D(zoomRectangle.center().x(), zoomRectangle.center().y(), 1); + const auto center = QVector3D(zoomRectangle.center().x(), zoomRectangle.center().y(), 0); const auto up = QVector3D(0, 1, 0); +#ifdef NAVIGATOR_2D_VERBOSE + qDebug() << __FUNCTION__ << getZoomRectangleWorld() << _renderer.getDataBounds() << eye; +#endif + // Create look-at transformation matrix lookAt.lookAt(eye, center, up); const auto viewerSize = _renderer.getRenderSize(); - const auto factorX = static_cast(viewerSize.width()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().width()) : 1.0f); - const auto factorY = static_cast(viewerSize.height()) / (getZoomRectangle().isValid() ? static_cast(getZoomRectangle().height()) : 1.0f); + const auto factorX = static_cast(viewerSize.width()) / (zoomRectangle.isValid() ? static_cast(zoomRectangle.width()) : 1.0f); + const auto factorY = static_cast(viewerSize.height()) / (zoomRectangle.isValid() ? static_cast(zoomRectangle.height()) : 1.0f); const auto scaleFactor = factorX < factorY ? factorX : factorY; const auto d = 1.0f - (2 * _zoomRectangleMargin) / std::max(viewerSize.width(), viewerSize.height()); @@ -148,26 +154,26 @@ QMatrix4x4 Navigator2D::getViewMatrix() const return scale * lookAt; } -QRectF Navigator2D::getZoomRectangle() const +QRectF Navigator2D::getZoomRectangleWorld() const { return { - _zoomRectangleTopLeft, - _zoomRectangleSize + _zoomRectangleWorldTopLeft, + _zoomRectangleWorldSize }; } -void Navigator2D::setZoomRectangle(const QRectF& zoomRectangle) +void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) { #ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << zoomRectangle; + qDebug() << __FUNCTION__ << zoomRectangleWorld; #endif - const auto previousZoomRectangle = getZoomRectangle(); + const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - _zoomRectangleTopLeft = zoomRectangle.topLeft(); - _zoomRectangleSize = zoomRectangle.size(); + _zoomRectangleWorldTopLeft = zoomRectangleWorld.topLeft(); + _zoomRectangleWorldSize = zoomRectangleWorld.size(); - emit zoomRectangleChanged(previousZoomRectangle, getZoomRectangle()); + emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } float Navigator2D::getZoomFactor() const @@ -201,20 +207,20 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) beginZooming(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - const auto v1 = getZoomRectangle().topLeft() - p1; + const auto v1 = getZoomRectangleWorld().topLeft() - p1; const auto v2 = v1 / factor; - _zoomRectangleTopLeft = p1 + v2; - _zoomRectangleSize = getZoomRectangle().size() / factor; + _zoomRectangleWorldTopLeft = p1 + v2; + _zoomRectangleWorldSize = getZoomRectangleWorld().size() / factor; _zoomFactor /= factor; - setZoomRectangle(getZoomRectangle()); + setZoomRectangleWorld(getZoomRectangleWorld()); } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endZooming(); } @@ -230,10 +236,10 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) beginZooming(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endZooming(); } @@ -249,14 +255,14 @@ void Navigator2D::panBy(const QPointF& delta) beginPanning(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomRectangleTopLeft = getZoomRectangle().topLeft() + (p2 - p1); + _zoomRectangleWorldTopLeft = getZoomRectangleWorld().topLeft() + (p2 - p1); } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endPanning(); } @@ -272,17 +278,20 @@ void Navigator2D::resetView(bool force /*= true*/) beginZooming(); { - beginChangeZoomRectangle(); + beginChangeZoomRectangleWorld(); { - setZoomRectangle(_renderer.getDataBounds()); + _zoomRectangleWorldTopLeft = _renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + _zoomRectangleWorldSize = _renderer.getDataBounds().size(); + + setZoomRectangleWorld(getZoomRectangleWorld()); - if (!_userHasNavigated || force) { - setZoomRectangle(_renderer.getDataBounds()); + // if (!_userHasNavigated || force) { + // setZoomRectangleWorld(_renderer.getDataBounds()); - _userHasNavigated = false; - } + // _userHasNavigated = false; + //} } - endChangeZoomRectangle(); + endChangeZoomRectangleWorld(); } endZooming(); } @@ -434,14 +443,14 @@ void Navigator2D::endNavigation() emit navigationEnded(); } -void Navigator2D::beginChangeZoomRectangle() +void Navigator2D::beginChangeZoomRectangleWorld() { - _previousZoomRectangle = getZoomRectangle(); + _previousZoomRectangleWorld = getZoomRectangleWorld(); } -void Navigator2D::endChangeZoomRectangle() +void Navigator2D::endChangeZoomRectangleWorld() { - emit zoomRectangleChanged(_previousZoomRectangle, getZoomRectangle()); + emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 6cd1771a6..b1f05fd8d 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -57,16 +57,16 @@ Q_OBJECT QMatrix4x4 getViewMatrix() const; /** - * Get the zoom rectangle - * @return Zoom rectangle + * Get the world zoom rectangle + * @return Zoom rectangle in world coordinates */ - QRectF getZoomRectangle() const; + QRectF getZoomRectangleWorld() const; /** - * Set the zoom rectangle to \p zoomRectangle - * @param zoomRectangle Zoom rectangle + * Set the world zoom rectangle to \p zoomRectangleWorld + * @param zoomRectangleWorld Zoom rectangle in world coordinates */ - void setZoomRectangle(const QRectF& zoomRectangle); + void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); /** * Get the zoom factor @@ -175,11 +175,11 @@ Q_OBJECT /** Navigation has ended */ void endNavigation(); - /** Begin changing the zoom rectangle */ - void beginChangeZoomRectangle(); + /** Begin changing the zoom rectangle in world coordinates */ + void beginChangeZoomRectangleWorld(); - /** End changing the zoom rectangle */ - void endChangeZoomRectangle(); + /** End changing the zoom rectangle in world coordinates */ + void endChangeZoomRectangleWorld(); signals: @@ -226,27 +226,27 @@ Q_OBJECT void enabledChanged(bool enabled); /** - * Signals that the zoom rectangle has changed from \p previousZoomRectangle to \p currentZoomRectangle - * @param previousZoomRectangle Previous zoom rectangle - * @param currentZoomRectangle Current zoom rectangle + * Signals that the zoom rectangle in world coordinates has changed from \p previousZoomRectangleWorld to \p currentZoomRectangleWorld + * @param previousZoomRectangleWorld Previous world zoom rectangle + * @param currentZoomRectangleWorld Current world zoom rectangle */ - void zoomRectangleChanged(const QRectF& previousZoomRectangle, const QRectF& currentZoomRectangle); + void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _enabled; /** Enabled flag */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QPointF _zoomRectangleTopLeft; /** Zoom rectangle top-left in world coordinates */ - QSizeF _zoomRectangleSize; /** Zoom rectangle size in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ - QRectF _previousZoomRectangle; /** Previous zoom rectangle */ - bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomRectangleWorldTopLeft; /** Zoom rectangle top-left in world coordinates */ + QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ + QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ }; } diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 1f30937fb..f13305bb0 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -436,7 +436,7 @@ namespace mv modelMatrix.setToIdentity(); - const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; + const auto mvp = QMatrix4x4(getProjectionMatrix())* getNavigator().getViewMatrix()* modelMatrix; const bool pointSizeAbsolute = _pointSettings._scalingMode == PointScaling::Absolute; _shader.uniform1f("pointSize", _pointSettings._pointSize); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 4b944d7bd..d5bb7a9ca 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -72,8 +72,7 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) _dataBounds = dataBounds; - //if (!_navigator.hasUserNavigated()) - getNavigator().setZoomRectangle(_dataBounds);//.marginsAdded({ _zoomMargin, _zoomMargin, _zoomMargin, _zoomMargin })); + getNavigator().resetView(); } QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const @@ -128,8 +127,8 @@ QMatrix4x4 Renderer2D::getNormalizedScreenToScreenMatrix() const float Renderer2D::getZoomPercentage() const { - const auto factorX = static_cast(getDataBounds().width()) / static_cast(getNavigator().getZoomRectangle().width()); - const auto factorY = static_cast(getDataBounds().height()) / static_cast(getNavigator().getZoomRectangle().height()); + const auto factorX = static_cast(getDataBounds().width()) / static_cast(getNavigator().getZoomRectangleWorld().width()); + const auto factorY = static_cast(getDataBounds().height()) / static_cast(getNavigator().getZoomRectangleWorld().height()); const auto scaleFactor = factorX < factorY ? factorX : factorY; return scaleFactor; @@ -144,6 +143,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const // Create an orthogonal transformation matrix matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); + //matrix.ortho(-halfSize.width(), halfSize.width(), -getRenderSize().height(), 0, -1000.0f, +1000.0f); return matrix; } From 7a7e68e4d878843c678e016c912166db7a77fbf2 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 26 Mar 2025 08:00:58 +0100 Subject: [PATCH 58/89] Fixes issues with randomized depth --- ManiVault/res/shaders/PointPlot.vert | 2 +- ManiVault/src/renderers/Navigator2D.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ManiVault/res/shaders/PointPlot.vert b/ManiVault/res/shaders/PointPlot.vert index c263861d4..d77921631 100644 --- a/ManiVault/res/shaders/PointPlot.vert +++ b/ManiVault/res/shaders/PointPlot.vert @@ -140,7 +140,7 @@ void main() // finalPos = (mvp * vec4(position + scaledVertex, 0.0, 1.0)).xy; // Compute random depth - float depth = randomizedDepthEnabled ? random(worldPos.xy) : 0; + float depth = randomizedDepthEnabled ? random(vec2(gl_InstanceID, 0)) : 0; // Set the final position gl_Position = vec4(finalPos, depth, 1.0); // Convert to NDC [-1,1] diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 17070b836..75ffa67dd 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -280,8 +280,8 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - _zoomRectangleWorldTopLeft = _renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); - _zoomRectangleWorldSize = _renderer.getDataBounds().size(); + _zoomRectangleWorldTopLeft = _renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + _zoomRectangleWorldSize = _renderer.getDataBounds().size(); setZoomRectangleWorld(getZoomRectangleWorld()); From e8e2640d32beab1685ba8bf66d3e7715fc650fae Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 10:49:14 +0100 Subject: [PATCH 59/89] Working on selections --- .../src/actions/DecimalRectangleAction.cpp | 15 +++++++++++ .../src/actions/DecimalRectangleAction.h | 12 +++++++++ ManiVault/src/renderers/Navigator2D.cpp | 25 +++++++++++++++++-- ManiVault/src/renderers/Navigator2D.h | 6 +++++ ManiVault/src/renderers/Renderer2D.cpp | 10 +++++--- ManiVault/src/renderers/Renderer2D.h | 3 +-- 6 files changed, 63 insertions(+), 8 deletions(-) diff --git a/ManiVault/src/actions/DecimalRectangleAction.cpp b/ManiVault/src/actions/DecimalRectangleAction.cpp index e6ca6f347..a64dc110c 100644 --- a/ManiVault/src/actions/DecimalRectangleAction.cpp +++ b/ManiVault/src/actions/DecimalRectangleAction.cpp @@ -21,6 +21,21 @@ DecimalRectangleAction::DecimalRectangleAction(QObject * parent, const QString& connect(&getRangeAction(Axis::Y), &DecimalRangeAction::rangeChanged, this, [this]() -> void { _rectangleChanged(); }); } +QRectF DecimalRectangleAction::toRectF() const +{ + return { + getLeft(), + getTop(), + getWidth(), + getHeight() + }; +} + +void DecimalRectangleAction::setRectF(const QRectF& rectangle) +{ + setRectangle(rectangle.left(), rectangle.right(), rectangle.bottom(), rectangle.top()); +} + void DecimalRectangleAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) { auto publicDecimalRectangleAction = dynamic_cast(publicAction); diff --git a/ManiVault/src/actions/DecimalRectangleAction.h b/ManiVault/src/actions/DecimalRectangleAction.h index a6660f1d4..87732ba2e 100644 --- a/ManiVault/src/actions/DecimalRectangleAction.h +++ b/ManiVault/src/actions/DecimalRectangleAction.h @@ -44,6 +44,18 @@ class CORE_EXPORT DecimalRectangleAction : public RectangleAction(_zoomRectangleWorldSize.width()) / aspectRatio); + } + else { + _zoomRectangleWorldSize.setWidth(static_cast(_zoomRectangleWorldSize.height()) * aspectRatio); + } + + const auto zoomRectangleWorldCenter = _renderer.getDataBounds().center(); + const auto zoomRectangleWorldOffset = QPointF(_zoomRectangleWorldSize.width() / 2.f, _zoomRectangleWorldSize.height() / 2.f); + + _zoomRectangleWorldTopLeft = zoomRectangleWorldCenter - zoomRectangleWorldOffset + QPoint(0, -_renderer.getDataBounds().height());//_renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + setZoomRectangleWorld(getZoomRectangleWorld()); // if (!_userHasNavigated || force) { diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index b1f05fd8d..c8b09f7c8 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -68,6 +68,12 @@ Q_OBJECT */ void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); + /** + * Get the zoom rectangle margin + * @return Zoom rectangle margin + */ + float getZoomRectangleMargin() const; + /** * Get the zoom factor * @return Zoom factor diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index d5bb7a9ca..16309d4ee 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -8,15 +8,14 @@ //#define RENDERER_2D_VERBOSE #endif -#define RENDERER_2D_VERBOSE +//#define RENDERER_2D_VERBOSE namespace mv { Renderer2D::Renderer2D(QObject* parent) : Renderer(parent), - _navigator(*this), - _zoomMargin(10.f) + _navigator(*this) { } @@ -71,7 +70,10 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) return; _dataBounds = dataBounds; - + + //_dataBounds.setWidth(std::max(_dataBounds.width(), 0.00000001)); + //_dataBounds.setHeight(std::max(_dataBounds.height(), 0.0001)); + getNavigator().resetView(); } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index c66a210e5..061cf506f 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -100,7 +100,7 @@ Q_OBJECT float getZoomPercentage() const; -/** Returns the projection matrix */ + /** Returns the projection matrix */ QMatrix4x4 getProjectionMatrix() const; /** @@ -134,7 +134,6 @@ Q_OBJECT private: QSize _renderSize; /** Size of the renderer canvas */ Navigator2D _navigator; /** 2D navigator */ - float _zoomMargin; /** Margin for zooming */ QRectF _dataBounds; /** Bounds of the data */ friend class Navigator2D; From 16b80ba6a0bfb122fb2cde21d1193715f5bc0323 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:02:23 +0100 Subject: [PATCH 60/89] Zoom around also working now --- ManiVault/src/renderers/Navigator2D.cpp | 43 ++++++++----------------- ManiVault/src/renderers/Navigator2D.h | 4 +-- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index abf954142..b489d28fb 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -24,7 +24,6 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleWorldSize(1, 1), _zoomRectangleMargin(0.f), _userHasNavigated() { @@ -157,9 +156,12 @@ QMatrix4x4 Navigator2D::getViewMatrix() const QRectF Navigator2D::getZoomRectangleWorld() const { + const auto zoomRectangleWorldSize = _renderer.getRenderSize().toSizeF() / _zoomFactor; + const auto zoomRectangleWorldTopLeft = _zoomCenterWorld - QPointF(.5f * static_cast(zoomRectangleWorldSize.width()), .5f * static_cast(zoomRectangleWorldSize.height())); + return { - _zoomRectangleWorldTopLeft, - _zoomRectangleWorldSize + zoomRectangleWorldTopLeft, + zoomRectangleWorldSize }; } @@ -171,8 +173,8 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - _zoomRectangleWorldTopLeft = zoomRectangleWorld.topLeft(); - _zoomRectangleWorldSize = zoomRectangleWorld.size(); + _zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); + _zoomCenterWorld = zoomRectangleWorld.center(); emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } @@ -215,14 +217,10 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) { beginChangeZoomRectangleWorld(); { - const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - const auto v1 = getZoomRectangleWorld().topLeft() - p1; - const auto v2 = v1 / factor; - - _zoomRectangleWorldTopLeft = p1 + v2; - _zoomRectangleWorldSize = getZoomRectangleWorld().size() / factor; + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - _zoomFactor /= factor; + _zoomFactor /= factor; + _zoomCenterWorld = p1 + (getZoomRectangleWorld().center() - p1) * factor; setZoomRectangleWorld(getZoomRectangleWorld()); } @@ -266,7 +264,7 @@ void Navigator2D::panBy(const QPointF& delta) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomRectangleWorldTopLeft = getZoomRectangleWorld().topLeft() + (p2 - p1); + _zoomCenterWorld = getZoomRectangleWorld().center() + (p2 - p1); } endChangeZoomRectangleWorld(); } @@ -286,23 +284,8 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - - _zoomRectangleWorldSize = _renderer.getDataBounds().size(); - - const auto size = QSizeF(_renderer.getRenderSize().width(), _renderer.getRenderSize().height()); - const auto aspectRatio = size.width() / size.height(); - - if (aspectRatio < 1) { - _zoomRectangleWorldSize.setHeight(static_cast(_zoomRectangleWorldSize.width()) / aspectRatio); - } - else { - _zoomRectangleWorldSize.setWidth(static_cast(_zoomRectangleWorldSize.height()) * aspectRatio); - } - - const auto zoomRectangleWorldCenter = _renderer.getDataBounds().center(); - const auto zoomRectangleWorldOffset = QPointF(_zoomRectangleWorldSize.width() / 2.f, _zoomRectangleWorldSize.height() / 2.f); - - _zoomRectangleWorldTopLeft = zoomRectangleWorldCenter - zoomRectangleWorldOffset + QPoint(0, -_renderer.getDataBounds().height());//_renderer.getDataBounds().topLeft() + QPoint(0, -_renderer.getDataBounds().height()); + _zoomFactor = _renderer.getDataBounds().width() / _renderer.getRenderSize().width(); + _zoomCenterWorld = _renderer.getDataBounds().center(); setZoomRectangleWorld(getZoomRectangleWorld()); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index c8b09f7c8..b270ed41f 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -248,8 +248,8 @@ Q_OBJECT bool _isPanning; /** Panning flag */ bool _isZooming; /** Zooming flag */ float _zoomFactor; /** Zoom factor */ - QPointF _zoomRectangleWorldTopLeft; /** Zoom rectangle top-left in world coordinates */ - QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ + QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ + //QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ float _zoomRectangleMargin; /** Zoom rectangle margin */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ From 5ec247845720770e75cec51690792a2d5d7de83f Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:28:10 +0100 Subject: [PATCH 61/89] Zoom around and panning is working nicely again --- ManiVault/src/renderers/Navigator2D.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index b489d28fb..ffa29d25e 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -156,7 +156,7 @@ QMatrix4x4 Navigator2D::getViewMatrix() const QRectF Navigator2D::getZoomRectangleWorld() const { - const auto zoomRectangleWorldSize = _renderer.getRenderSize().toSizeF() / _zoomFactor; + const auto zoomRectangleWorldSize = _renderer.getRenderSize().toSizeF() * _zoomFactor; const auto zoomRectangleWorldTopLeft = _zoomCenterWorld - QPointF(.5f * static_cast(zoomRectangleWorldSize.width()), .5f * static_cast(zoomRectangleWorldSize.height())); return { @@ -173,8 +173,8 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - _zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); - _zoomCenterWorld = zoomRectangleWorld.center(); + //_zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); + //_zoomCenterWorld = zoomRectangleWorld.center(); emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } @@ -220,8 +220,9 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); _zoomFactor /= factor; - _zoomCenterWorld = p1 + (getZoomRectangleWorld().center() - p1) * factor; + _zoomCenterWorld = p1 + (_zoomCenterWorld - p1) / factor; + qDebug() << "-----------" << _zoomFactor << _zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); } endChangeZoomRectangleWorld(); @@ -284,8 +285,10 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - _zoomFactor = _renderer.getDataBounds().width() / _renderer.getRenderSize().width(); - _zoomCenterWorld = _renderer.getDataBounds().center(); + _zoomFactor = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); + _zoomCenterWorld = _renderer.getDataBounds().center(); + + qDebug() << _renderer.getDataBounds() << _renderer.getRenderSize() << _zoomFactor << _zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); From c411c0c7e80b680bfbf62aebbebea8e5304fe370 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:40:04 +0100 Subject: [PATCH 62/89] Fixed zoom to extents --- ManiVault/src/renderers/Navigator2D.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index ffa29d25e..44c099079 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -222,7 +222,6 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) _zoomFactor /= factor; _zoomCenterWorld = p1 + (_zoomCenterWorld - p1) / factor; - qDebug() << "-----------" << _zoomFactor << _zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); } endChangeZoomRectangleWorld(); @@ -285,10 +284,11 @@ void Navigator2D::resetView(bool force /*= true*/) { beginChangeZoomRectangleWorld(); { - _zoomFactor = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); - _zoomCenterWorld = _renderer.getDataBounds().center(); + const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - qDebug() << _renderer.getDataBounds() << _renderer.getRenderSize() << _zoomFactor << _zoomCenterWorld; + _zoomFactor = std::max(zoomFactorX, zoomFactorY); + _zoomCenterWorld = _renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height()); setZoomRectangleWorld(getZoomRectangleWorld()); From a40c92d3c822ea774c3a266bb0c768e9aac03292 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 27 Mar 2025 11:47:17 +0100 Subject: [PATCH 63/89] Work on margins --- ManiVault/src/renderers/Navigator2D.cpp | 6 +++--- ManiVault/src/renderers/Renderer2D.cpp | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 44c099079..38209f197 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -24,7 +24,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleMargin(0.f), + _zoomRectangleMargin(50.f), _userHasNavigated() { } @@ -145,7 +145,7 @@ QMatrix4x4 Navigator2D::getViewMatrix() const const auto factorY = static_cast(viewerSize.height()) / (zoomRectangle.isValid() ? static_cast(zoomRectangle.height()) : 1.0f); const auto scaleFactor = factorX < factorY ? factorX : factorY; - const auto d = 1.0f - (2 * _zoomRectangleMargin) / std::max(viewerSize.width(), viewerSize.height()); + const auto d = 1.0f - (2 * 0) / std::max(viewerSize.width(), viewerSize.height()); // Create scale matrix scale.scale(scaleFactor * d, scaleFactor * d, scaleFactor * d); @@ -287,7 +287,7 @@ void Navigator2D::resetView(bool force /*= true*/) const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY); + _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; _zoomCenterWorld = _renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height()); setZoomRectangleWorld(getZoomRectangleWorld()); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 16309d4ee..cd9eb4712 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -145,7 +145,6 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const // Create an orthogonal transformation matrix matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); - //matrix.ortho(-halfSize.width(), halfSize.width(), -getRenderSize().height(), 0, -1000.0f, +1000.0f); return matrix; } From 88b2957b67cb50211213fbc83bbe5112941e6209 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 28 Mar 2025 11:13:38 +0100 Subject: [PATCH 64/89] Made some fixes in the view plugin sampler action --- .../src/actions/ViewPluginSamplerAction.cpp | 10 +- ManiVault/src/renderers/DensityRenderer.cpp | 269 +++++++++--------- ManiVault/src/renderers/DensityRenderer.h | 12 +- ManiVault/src/renderers/PointRenderer.cpp | 9 +- ManiVault/src/renderers/Renderer2D.cpp | 22 ++ ManiVault/src/renderers/Renderer2D.h | 33 ++- 6 files changed, 192 insertions(+), 163 deletions(-) diff --git a/ManiVault/src/actions/ViewPluginSamplerAction.cpp b/ManiVault/src/actions/ViewPluginSamplerAction.cpp index 983e06c92..606c84e84 100644 --- a/ManiVault/src/actions/ViewPluginSamplerAction.cpp +++ b/ManiVault/src/actions/ViewPluginSamplerAction.cpp @@ -442,20 +442,26 @@ bool ViewPluginSamplerAction::eventFilter(QObject* target, QEvent* event) case QEvent::MouseButtonPress: { - _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(false); + _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(false); + _toolTipLabel.hide(); break; } case QEvent::MouseButtonRelease: case QEvent::Enter: { - _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(getSamplingMode() == SamplingMode::FocusRegion && getEnabledAction().isChecked() && canView()); + const auto enabled = getSamplingMode() == SamplingMode::FocusRegion && getEnabledAction().isChecked() && canView(); + + _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(enabled); + _toolTipLabel.setVisible(enabled); + break; } case QEvent::Leave: { _samplerPixelSelectionAction->getPixelSelectionTool()->setEnabled(false); + _toolTipLabel.hide(); break; } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 1c871fca6..4ae25c47b 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -4,177 +4,166 @@ #include "DensityRenderer.h" -namespace mv +namespace mv::gui { - namespace gui - { - - DensityRenderer::DensityRenderer(RenderMode renderMode) : - _renderMode(renderMode) - { - getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); - } - - DensityRenderer::~DensityRenderer() - { - // Delete objects - _densityComputation.cleanup(); - } - - void DensityRenderer::setRenderMode(RenderMode renderMode) - { - _renderMode = renderMode; - } - - // Points need to be passed as a pointer as we need to store them locally in order - // to be able to recompute the densities when parameters change. - void DensityRenderer::setData(const std::vector* points) - { - _densityComputation.setData(points); - } - - void DensityRenderer::setWeights(const std::vector* weights) - { - _densityComputation.setWeights(weights); - } - void DensityRenderer::setBounds(const Bounds& bounds) - { - _densityComputation.setBounds(bounds.getLeft(), bounds.getRight(), bounds.getBottom(), bounds.getTop()); - } +DensityRenderer::DensityRenderer(RenderMode renderMode) : + _renderMode(renderMode) +{ + getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); +} - void DensityRenderer::setSigma(const float sigma) - { - _densityComputation.setSigma(sigma); - } +DensityRenderer::~DensityRenderer() +{ + // Delete objects + _densityComputation.cleanup(); +} - void DensityRenderer::computeDensity() - { - _densityComputation.compute(); - } +void DensityRenderer::setRenderMode(RenderMode renderMode) +{ + _renderMode = renderMode; +} - float DensityRenderer::getMaxDensity() const - { - return _densityComputation.getMaxDensity(); - } +// Points need to be passed as a pointer as we need to store them locally in order +// to be able to recompute the densities when parameters change. +void DensityRenderer::setData(const std::vector* points) +{ + _densityComputation.setData(points); +} - mv::Vector3f DensityRenderer::getColorMapRange() const - { - return Vector3f(0.0f, _densityComputation.getMaxDensity(), _densityComputation.getMaxDensity()); - } +void DensityRenderer::setWeights(const std::vector* weights) +{ + _densityComputation.setWeights(weights); +} - void DensityRenderer::setColormap(const QImage& image) - { - _colormap.loadFromImage(image); - _hasColorMap = true; - } +void DensityRenderer::setSigma(const float sigma) +{ + _densityComputation.setSigma(sigma); +} - void DensityRenderer::init() - { - initializeOpenGLFunctions(); +void DensityRenderer::computeDensity() +{ + _densityComputation.compute(); +} - // Create a simple VAO for full-screen quad rendering - glGenVertexArrays(1, &_quad); +float DensityRenderer::getMaxDensity() const +{ + return _densityComputation.getMaxDensity(); +} - // Load the necessary shaders for density drawing - bool loaded = true; - loaded &= _shaderDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/DensityDraw.frag"); - loaded &= _shaderIsoDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/IsoDensityDraw.frag"); - if (!loaded) { - qDebug() << "Failed to load one of the Density shaders"; - } +mv::Vector3f DensityRenderer::getColorMapRange() const +{ + return Vector3f(0.0f, _densityComputation.getMaxDensity(), _densityComputation.getMaxDensity()); +} - // Initialize the density computation - _densityComputation.init(QOpenGLContext::currentContext()); - } +void DensityRenderer::setColormap(const QImage& image) +{ + _colormap.loadFromImage(image); + _hasColorMap = true; +} - void DensityRenderer::render() - { - beginRender(); - { - switch (_renderMode) { - case DENSITY: { - drawDensity(); - break; - } - - case LANDSCAPE: { - drawLandscape(); - break; - } - } - } - endRender(); - } +void DensityRenderer::init() +{ + initializeOpenGLFunctions(); - void DensityRenderer::destroy() - { - _shaderDensityDraw.destroy(); - _shaderIsoDensityDraw.destroy(); - _densityComputation.cleanup(); - _colormap.destroy(); + // Create a simple VAO for full-screen quad rendering + glGenVertexArrays(1, &_quad); - glDeleteVertexArrays(1, &_quad); - } + // Load the necessary shaders for density drawing + bool loaded = true; + loaded &= _shaderDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/DensityDraw.frag"); + loaded &= _shaderIsoDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/IsoDensityDraw.frag"); + if (!loaded) { + qDebug() << "Failed to load one of the Density shaders"; + } - void DensityRenderer::drawFullscreenQuad() - { - glBindVertexArray(_quad); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); - } + // Initialize the density computation + _densityComputation.init(QOpenGLContext::currentContext()); +} - void DensityRenderer::drawDensity() - { - float maxDensity = _densityComputation.getMaxDensity(); - if (maxDensity <= 0) { return; } +void DensityRenderer::render() +{ + beginRender(); + { + switch (_renderMode) { + case DENSITY: { + drawDensity(); + break; + } + + case LANDSCAPE: { + drawLandscape(); + break; + } + } + } + endRender(); +} + +void DensityRenderer::destroy() +{ + _shaderDensityDraw.destroy(); + _shaderIsoDensityDraw.destroy(); + _densityComputation.cleanup(); + _colormap.destroy(); - _shaderDensityDraw.bind(); + glDeleteVertexArrays(1, &_quad); +} - _densityComputation.getDensityTexture().bind(0); +void DensityRenderer::drawFullscreenQuad() +{ + glBindVertexArray(_quad); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); +} - QMatrix4x4 modelMatrix; +void DensityRenderer::drawDensity() +{ + const auto maxDensity = _densityComputation.getMaxDensity(); - modelMatrix.setToIdentity(); + if (maxDensity <= 0) { + return; + } - const auto mvp = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * modelMatrix; + _shaderDensityDraw.bind(); - qDebug() << mvp; + _densityComputation.getDensityTexture().bind(0); - _shaderDensityDraw.uniformMatrix4f("mvp", mvp.data()); - _shaderDensityDraw.uniform1i("tex", 0); - _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); + _shaderDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); + _shaderDensityDraw.uniform1i("tex", 0); + _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); - drawFullscreenQuad(); - } + drawFullscreenQuad(); +} - void DensityRenderer::drawLandscape() - { - if (!_hasColorMap) - return; +void DensityRenderer::drawLandscape() +{ + if (!_hasColorMap) + return; - float maxDensity = _densityComputation.getMaxDensity(); - if (maxDensity <= 0) { return; } + const auto maxDensity = _densityComputation.getMaxDensity(); - _shaderIsoDensityDraw.bind(); + if (maxDensity <= 0) { + return; + } - _densityComputation.getDensityTexture().bind(0); - _shaderIsoDensityDraw.uniform1i("tex", 0); + _shaderIsoDensityDraw.bind(); - _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); - _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); + _densityComputation.getDensityTexture().bind(0); + _shaderIsoDensityDraw.uniform1i("tex", 0); - _colormap.bind(1); - _shaderIsoDensityDraw.uniform1i("colormap", 1); + _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); + _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); - drawFullscreenQuad(); - } + _colormap.bind(1); + _shaderIsoDensityDraw.uniform1i("colormap", 1); - void DensityRenderer::setColorMapRange(const float& min, const float& max) - { - _colorMapRange = Vector3f(min, max, max - min); - } + drawFullscreenQuad(); +} - } // namespace gui +void DensityRenderer::setColorMapRange(const float& min, const float& max) +{ + _colorMapRange = Vector3f(min, max, max - min); +} -} // namespace mv +} diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index f2568a85f..2d3e16f37 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -6,7 +6,6 @@ #include "Renderer2D.h" -#include "graphics/Bounds.h" #include "graphics/Shader.h" #include "graphics/Texture.h" #include "graphics/Vector2f.h" @@ -14,12 +13,8 @@ #include "util/MeanShift.h" -#include - -namespace mv +namespace mv::gui { - namespace gui - { class CORE_EXPORT DensityRenderer : public Renderer2D { @@ -35,7 +30,6 @@ namespace mv void setRenderMode(RenderMode renderMode); void setData(const std::vector* data); void setWeights(const std::vector* weights); - void setBounds(const Bounds& bounds); void setSigma(const float sigma); void computeDensity(); float getMaxDensity() const; @@ -78,6 +72,4 @@ namespace mv GLuint _quad = 0; }; - } // namespace gui - -} // namespace mv +} diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index f13305bb0..1f15a5ea7 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -431,18 +431,13 @@ namespace mv beginRender(); { _shader.bind(); - - QMatrix4x4 modelMatrix; - - modelMatrix.setToIdentity(); - - const auto mvp = QMatrix4x4(getProjectionMatrix())* getNavigator().getViewMatrix()* modelMatrix; + const bool pointSizeAbsolute = _pointSettings._scalingMode == PointScaling::Absolute; _shader.uniform1f("pointSize", _pointSettings._pointSize); _shader.uniform1i("pointSizeAbsolute", pointSizeAbsolute); _shader.uniform2f("viewportSize", static_cast(getRenderSize().width()), static_cast(getRenderSize().height())); - _shader.uniformMatrix4f("mvp", mvp.data()); + _shader.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); _shader.uniform1f("pointOpacity", _pointSettings._alpha); _shader.uniform1i("scalarEffect", _pointEffect); _shader.uniform4f("dataBounds", getDataBounds().left(), getDataBounds().right(), getDataBounds().bottom(), getDataBounds().top()); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index cd9eb4712..50ae9e190 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -46,6 +46,8 @@ void Renderer2D::beginRender() #endif glViewport(0, 0, _renderSize.width(), _renderSize.height()); + + updateModelViewProjectionMatrix(); } void Renderer2D::endRender() @@ -77,6 +79,11 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) getNavigator().resetView(); } +void Renderer2D::updateModelViewProjectionMatrix() +{ + _modelViewProjectionMatrix = QMatrix4x4(getProjectionMatrix()) * getNavigator().getViewMatrix() * _modelMatrix; +} + QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewMatrix, const QPoint& screenPoint) const { return QVector3D(screenPoint.x(), getRenderSize().height() - screenPoint.y(), 0).unproject(modelViewMatrix, getProjectionMatrix(), QRect(0, 0, getRenderSize().width(), getRenderSize().height())); @@ -161,4 +168,19 @@ QRect Renderer2D::getScreenRectangleFromWorldRectangle(const QRectF& worldBoundi }; } +QMatrix4x4 Renderer2D::getModelMatrix() const +{ + return _modelMatrix; +} + +void Renderer2D::setModelMatrix(const QMatrix4x4& modelMatrix) +{ + _modelMatrix = modelMatrix; +} + +QMatrix4x4 Renderer2D::getModelViewProjectionMatrix() const +{ + return _modelViewProjectionMatrix; +} + } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 061cf506f..ae76f98bd 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -109,9 +109,27 @@ Q_OBJECT */ QRect getScreenRectangleFromWorldRectangle(const QRectF& worldBoundingRectangle) const; + /** + * Get model matrix + * @return Model matrix + */ + QMatrix4x4 getModelMatrix() const; + + /** + * Set model matrix to \p modelMatrix + * @param modelMatrix Model matrix + */ + void setModelMatrix(const QMatrix4x4& modelMatrix); + + /** + * Get model-view-projection matrix + * @return Model-view-projection matrix + */ + QMatrix4x4 getModelViewProjectionMatrix() const; + protected: - /** Begin rendering */ + /** Begin rendering (sets up the OpenGL viewport and computes the model-view-projection matrix) */ void beginRender() override; /** End rendering */ @@ -132,9 +150,16 @@ Q_OBJECT void setDataBounds(const QRectF& dataBounds); private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ - QRectF _dataBounds; /** Bounds of the data */ + + /** Update the model-view-projection matrix */ + void updateModelViewProjectionMatrix(); + +private: + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + QRectF _dataBounds; /** Bounds of the data */ + QMatrix4x4 _modelMatrix; + QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ friend class Navigator2D; }; From 5c86d3db2d369efd91d3250c9b29d5718a7fe3c3 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 31 Mar 2025 13:32:12 +0200 Subject: [PATCH 65/89] Work on density renderer --- ManiVault/res/shaders/DensityDraw.frag | 13 +- ManiVault/res/shaders/IsoDensityDraw.frag | 12 +- ManiVault/res/shaders/Quad.vert | 10 +- ManiVault/src/renderers/DensityRenderer.cpp | 130 +++++-- ManiVault/src/renderers/DensityRenderer.h | 123 ++++--- ManiVault/src/renderers/Navigator2D.cpp | 64 +++- ManiVault/src/renderers/Navigator2D.h | 360 ++++++++++---------- ManiVault/src/renderers/Renderer2D.h | 3 +- 8 files changed, 443 insertions(+), 272 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index 30e8c189d..34a8f1b7b 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -7,14 +7,13 @@ uniform sampler2D tex; uniform float norm; -in vec2 pass_texCoord; +in vec2 outUv; out vec4 fragColor; void main() { - float f = 1 - (texture(tex, pass_texCoord).r * norm); - fragColor = vec4(vec3(f), 1); - - if (pass_texCoord.x < 0.1 || pass_texCoord.x > 0.9) - fragColor = vec4(1,0,0,1); -} + float f = 1 - (texture(tex, outUv).r * norm); + fragColor = vec4(vec3(f, 0, 1), 1); + //fragColor = vec4(outUv.y, 0, 0, 1); + //fragColor = vec4(1, 0, 0, 1); +} \ No newline at end of file diff --git a/ManiVault/res/shaders/IsoDensityDraw.frag b/ManiVault/res/shaders/IsoDensityDraw.frag index a34e3c3e2..8f0650894 100644 --- a/ManiVault/res/shaders/IsoDensityDraw.frag +++ b/ManiVault/res/shaders/IsoDensityDraw.frag @@ -10,7 +10,7 @@ uniform sampler2D densityMap; uniform vec2 renderParams; uniform vec3 colorMapRange; -in vec2 pass_texCoord; +in vec2 uv; out vec4 fragColor; @@ -22,7 +22,7 @@ float getNormalizedDensity(vec2 uv) } void main() { - float density = getNormalizedDensity(pass_texCoord); + float density = getNormalizedDensity(uv); if (density < renderParams.y) discard; @@ -40,10 +40,10 @@ void main() { // Central differences to find out if we draw the iso contour instead of the color vec4 neighborDensities; - neighborDensities.x = getNormalizedDensity(pass_texCoord + texelSize.xz); - neighborDensities.y = getNormalizedDensity(pass_texCoord - texelSize.xz); - neighborDensities.z = getNormalizedDensity(pass_texCoord + texelSize.zy); - neighborDensities.w = getNormalizedDensity(pass_texCoord - texelSize.zy); + neighborDensities.x = getNormalizedDensity(uv + texelSize.xz); + neighborDensities.y = getNormalizedDensity(uv - texelSize.xz); + neighborDensities.z = getNormalizedDensity(uv + texelSize.zy); + neighborDensities.w = getNormalizedDensity(uv - texelSize.zy); ivec4 stepId = min(ivec4(floor(neighborDensities * vec4(numSteps+1))), ivec4(numSteps)); isBoundary = (any(notEqual(stepId.xxx, stepId.yzw))); diff --git a/ManiVault/res/shaders/Quad.vert b/ManiVault/res/shaders/Quad.vert index 14cbb55e2..80d4b7ea3 100644 --- a/ManiVault/res/shaders/Quad.vert +++ b/ManiVault/res/shaders/Quad.vert @@ -4,11 +4,15 @@ #version 330 core +layout(location = 0) in vec2 vertex; +layout(location = 1) in vec2 uv; + uniform mat4 mvp; -out vec2 pass_texCoord; +out vec2 outUv; void main() { - pass_texCoord = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2); - gl_Position = mvp * vec4(pass_texCoord * 2 - 1, 0, 1); + gl_Position = mvp * vec4(vertex, 0, 1); + + outUv = (vertex / vec2(128.f, 128.f)); } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 4ae25c47b..55d8e0a2a 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -4,6 +4,12 @@ #include "DensityRenderer.h" +#ifdef _DEBUG + //#define DENSITY_RENDERER_VERBOSE +#endif + +#define DENSITY_RENDERER_VERBOSE + namespace mv::gui { @@ -19,6 +25,24 @@ DensityRenderer::~DensityRenderer() _densityComputation.cleanup(); } +void DensityRenderer::resize(QSize renderSize) +{ + Renderer2D::resize(renderSize); + + updateQuad(); +} + +void DensityRenderer::setDataBounds(const QRectF& dataBounds) +{ + Renderer2D::setDataBounds(dataBounds); + + qDebug() << "DensityRenderer::setDataBounds()" << dataBounds; + _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); + //_densityComputation.setBounds(-100, -100, -100, 100); + + updateQuad(); +} + void DensityRenderer::setRenderMode(RenderMode renderMode) { _renderMode = renderMode; @@ -53,7 +77,11 @@ float DensityRenderer::getMaxDensity() const mv::Vector3f DensityRenderer::getColorMapRange() const { - return Vector3f(0.0f, _densityComputation.getMaxDensity(), _densityComputation.getMaxDensity()); + return { + 0.0f, + _densityComputation.getMaxDensity(), + _densityComputation.getMaxDensity() + }; } void DensityRenderer::setColormap(const QImage& image) @@ -66,14 +94,14 @@ void DensityRenderer::init() { initializeOpenGLFunctions(); - // Create a simple VAO for full-screen quad rendering - glGenVertexArrays(1, &_quad); + updateQuad(); // Load the necessary shaders for density drawing bool loaded = true; loaded &= _shaderDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/DensityDraw.frag"); loaded &= _shaderIsoDensityDraw.loadShaderFromFile(":shaders/Quad.vert", ":shaders/IsoDensityDraw.frag"); - if (!loaded) { + + if (!loaded) { qDebug() << "Failed to load one of the Density shaders"; } @@ -83,6 +111,7 @@ void DensityRenderer::init() void DensityRenderer::render() { + qDebug() << "DensityRenderer::render()" << _renderMode; beginRender(); { switch (_renderMode) { @@ -107,33 +136,84 @@ void DensityRenderer::destroy() _densityComputation.cleanup(); _colormap.destroy(); - glDeleteVertexArrays(1, &_quad); + glDeleteVertexArrays(1, &_VAO); + glDeleteVertexArrays(1, &_VBO); + glDeleteVertexArrays(1, &_EBO); } -void DensityRenderer::drawFullscreenQuad() +void DensityRenderer::updateQuad() { - glBindVertexArray(_quad); - glDrawArrays(GL_TRIANGLES, 0, 3); + const float width = 128.f;//static_cast(getDataBounds().width()); + const float height = 128.f;//static_cast(getDataBounds().height()); + + float vertices[] = { + 0.f, 0.f, 0.0f, 0.0f, + width, 0.f, 1.0f, 0.0f, + width, height, 1.0f, 1.0f, + 0.f, height, 0.0f, 1.0f + }; + + unsigned int indices[] = { + 0, 1, 2, + 2, 3, 0 + }; + + glGenVertexArrays(1, &_VAO); + glGenBuffers(1, &_VBO); + glGenBuffers(1, &_EBO); + + glBindVertexArray(_VAO); + + glBindBuffer(GL_ARRAY_BUFFER, _VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + // Position attribute + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // Texture coordinates attribute + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + glEnableVertexAttribArray(1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void DensityRenderer::drawQuad() +{ + glBindVertexArray(_VAO); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); } void DensityRenderer::drawDensity() { +#ifdef DENSITY_RENDERER_VERBOSE + qDebug() << __FUNCTION__; +#endif + const auto maxDensity = _densityComputation.getMaxDensity(); if (maxDensity <= 0) { return; } - _shaderDensityDraw.bind(); + qDebug() << "DensityRenderer::drawDensity()" << maxDensity; - _densityComputation.getDensityTexture().bind(0); + _shaderDensityDraw.bind(); + { + _densityComputation.getDensityTexture().bind(0); - _shaderDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); - _shaderDensityDraw.uniform1i("tex", 0); - _shaderDensityDraw.uniform1f("norm", 1 / maxDensity); + _shaderDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); + _shaderDensityDraw.uniform1i("tex", 0); + _shaderDensityDraw.uniform1f("norm", 1.0f / maxDensity); - drawFullscreenQuad(); + drawQuad(); + } + //_shaderDensityDraw.release(); } void DensityRenderer::drawLandscape() @@ -141,6 +221,10 @@ void DensityRenderer::drawLandscape() if (!_hasColorMap) return; +#ifdef DENSITY_RENDERER_VERBOSE + qDebug() << __FUNCTION__; +#endif + const auto maxDensity = _densityComputation.getMaxDensity(); if (maxDensity <= 0) { @@ -148,17 +232,21 @@ void DensityRenderer::drawLandscape() } _shaderIsoDensityDraw.bind(); + { + _densityComputation.getDensityTexture().bind(0); - _densityComputation.getDensityTexture().bind(0); - _shaderIsoDensityDraw.uniform1i("tex", 0); + _shaderIsoDensityDraw.uniformMatrix4f("mvp", getModelViewProjectionMatrix().data()); + _shaderIsoDensityDraw.uniform1i("tex", 0); + _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); + _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); - _shaderIsoDensityDraw.uniform2f("renderParams", 1.0f / maxDensity, 1.0f / _densityComputation.getNumPoints()); - _shaderIsoDensityDraw.uniform3f("colorMapRange", _colorMapRange); + _colormap.bind(1); - _colormap.bind(1); - _shaderIsoDensityDraw.uniform1i("colormap", 1); + _shaderIsoDensityDraw.uniform1i("colormap", 1); - drawFullscreenQuad(); + drawQuad(); + } + _shaderIsoDensityDraw.release(); } void DensityRenderer::setColorMapRange(const float& min, const float& max) diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 2d3e16f37..09f4c9e51 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -16,60 +16,75 @@ namespace mv::gui { - class CORE_EXPORT DensityRenderer : public Renderer2D - { - - public: - enum RenderMode { - DENSITY, LANDSCAPE - }; - - DensityRenderer(RenderMode renderMode); - ~DensityRenderer() override; - - void setRenderMode(RenderMode renderMode); - void setData(const std::vector* data); - void setWeights(const std::vector* weights); - void setSigma(const float sigma); - void computeDensity(); - float getMaxDensity() const; - Vector3f getColorMapRange() const; - - /** - * Loads a colormap from an image and loads as - *the current colormap for the landscape view. - * @param image Color map image - */ - void setColormap(const QImage& image); - - void init() override; - - void render() override; - - void destroy() override; - - void setColorMapRange(const float& min, const float& max); - - private: - void drawDensity(); - void drawLandscape(); - - void drawFullscreenQuad(); - - private: - bool _isSelecting = false; - bool _hasColorMap = false; - - ShaderProgram _shaderDensityDraw; - ShaderProgram _shaderIsoDensityDraw; - DensityComputation _densityComputation; - Texture2D _colormap; - - Vector3f _colorMapRange; - - RenderMode _renderMode; +class CORE_EXPORT DensityRenderer : public Renderer2D +{ - GLuint _quad = 0; - }; +public: + enum RenderMode { + DENSITY, LANDSCAPE + }; + + DensityRenderer(RenderMode renderMode); + ~DensityRenderer() override; + + /** + * Resize the renderer to \p renderSize + * @param renderSize New size of the renderer + */ + void resize(QSize renderSize) override; + + /** + * Set data bounds to \p dataBounds + * @param dataBounds Data bounds + */ + void setDataBounds(const QRectF& dataBounds) override; + + void setRenderMode(RenderMode renderMode); + void setData(const std::vector* data); + void setWeights(const std::vector* weights); + void setSigma(const float sigma); + void computeDensity(); + float getMaxDensity() const; + Vector3f getColorMapRange() const; + + /** + * Loads a colormap from an image and loads as + *the current colormap for the landscape view. + * @param image Color map image + */ + void setColormap(const QImage& image); + + void init() override; + + void render() override; + + void destroy() override; + + void setColorMapRange(const float& min, const float& max); + +private: + void drawDensity(); + void drawLandscape(); + + void updateQuad(); + void drawQuad(); + +private: + bool _isSelecting = false; + bool _hasColorMap = false; + + ShaderProgram _shaderDensityDraw; + ShaderProgram _shaderIsoDensityDraw; + DensityComputation _densityComputation; + Texture2D _colormap; + + Vector3f _colorMapRange; + + RenderMode _renderMode; + + GLuint _VAO = 0; + GLuint _VBO = 0; + GLuint _EBO = 0; +}; } diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 38209f197..277dcc5a1 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -39,6 +39,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); + connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); + + _updateNavigationTimer.start(16); + _initialized = true; } } @@ -220,9 +224,8 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); _zoomFactor /= factor; - _zoomCenterWorld = p1 + (_zoomCenterWorld - p1) / factor; - setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomCenterWorld(p1 + (_zoomCenterWorld - p1) / factor); } endChangeZoomRectangleWorld(); } @@ -264,13 +267,32 @@ void Navigator2D::panBy(const QPointF& delta) const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint()).toPointF(); const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), delta.toPoint()).toPointF(); - _zoomCenterWorld = getZoomRectangleWorld().center() + (p2 - p1); + setZoomCenterWorld(getZoomRectangleWorld().center() + (p2 - p1)); } endChangeZoomRectangleWorld(); } endPanning(); } +void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) +{ + if (zoomCenterWorld == _zoomCenterWorld) + return; + + _zoomCenterWorld = zoomCenterWorld; + + setZoomRectangleWorld(getZoomRectangleWorld()); + + //if (std::isnan(zoomCenterWorld.x()) || std::isnan(zoomCenterWorld.y())) + // return; + + //_zoomCenterHistory.push_front(zoomCenterWorld); + + //if (_zoomCenterHistory.size() > Navigator2D::maxZoomHistorySize) { + // _zoomCenterHistory.pop_back(); + //} +} + void Navigator2D::resetView(bool force /*= true*/) { if (!_initialized) @@ -287,10 +309,11 @@ void Navigator2D::resetView(bool force /*= true*/) const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - _zoomCenterWorld = _renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height()); + _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; + + setZoomCenterWorld(_renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height())); - setZoomRectangleWorld(getZoomRectangleWorld()); + // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); @@ -460,4 +483,33 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } +void Navigator2D::updateNavigation() +{ + + //QPointF smoothedZoomCenterWorld{}; + + //if (!_zoomCenterHistory.empty()) { + // QPointF sum(0, 0); + + // for (const auto& zoomCenterWorld : _zoomCenterHistory) { + // sum += zoomCenterWorld; + // } + + // smoothedZoomCenterWorld = sum / static_cast(_zoomCenterHistory.size()); + + // _zoomCenterWorld += (smoothedZoomCenterWorld - _zoomCenterWorld); + + // if (!_zoomCenterHistory.empty()) { + // _zoomCenterHistory.pop_back(); + // } + //} + + //qDebug() << __FUNCTION__ << _zoomCenterWorld; + + + + + //setZoomRectangleWorld(getZoomRectangleWorld()); +} + } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index b270ed41f..e38e14917 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -5,7 +5,7 @@ #pragma once #include -#include +#include #include namespace mv @@ -22,237 +22,251 @@ class Renderer2D; */ class CORE_EXPORT Navigator2D : public QObject { - -Q_OBJECT + Q_OBJECT public: - /** - * Construct a new two-dimensional navigator - * - * @param renderer Reference to parent renderer - * @param parent Pointer to the parent object - */ - explicit Navigator2D(Renderer2D& renderer, QObject* parent = nullptr); + /** + * Construct a new two-dimensional navigator + * + * @param renderer Reference to parent renderer + * @param parent Pointer to the parent object + */ + explicit Navigator2D(Renderer2D& renderer, QObject* parent = nullptr); - /** - * Initializes the two-dimensional navigator with a \p sourceWidget - * @param sourceWidget Pointer to the renderer widget - */ - void initialize(QWidget* sourceWidget); + /** + * Initializes the two-dimensional navigator with a \p sourceWidget + * @param sourceWidget Pointer to the renderer widget + */ + void initialize(QWidget* sourceWidget); - /** - * Watch \p watched for events - * - * @param watched Watched object - * @param event Event - * @return True if the event was handled, false otherwise - */ - bool eventFilter(QObject* watched, QEvent* event) override; + /** + * Watch \p watched for events + * + * @param watched Watched object + * @param event Event + * @return True if the event was handled, false otherwise + */ + bool eventFilter(QObject* watched, QEvent* event) override; - /** - * Get the view matrix - * @return View matrix - */ - QMatrix4x4 getViewMatrix() const; + /** + * Get the view matrix + * @return View matrix + */ + QMatrix4x4 getViewMatrix() const; - /** - * Get the world zoom rectangle - * @return Zoom rectangle in world coordinates - */ - QRectF getZoomRectangleWorld() const; + /** + * Get the world zoom rectangle + * @return Zoom rectangle in world coordinates + */ + QRectF getZoomRectangleWorld() const; - /** - * Set the world zoom rectangle to \p zoomRectangleWorld - * @param zoomRectangleWorld Zoom rectangle in world coordinates - */ - void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); + /** + * Set the world zoom rectangle to \p zoomRectangleWorld + * @param zoomRectangleWorld Zoom rectangle in world coordinates + */ + void setZoomRectangleWorld(const QRectF& zoomRectangleWorld); - /** - * Get the zoom rectangle margin - * @return Zoom rectangle margin - */ - float getZoomRectangleMargin() const; + /** + * Get the zoom rectangle margin + * @return Zoom rectangle margin + */ + float getZoomRectangleMargin() const; /** - * Get the zoom factor + * Get the zoom factor * @return Zoom factor */ float getZoomFactor() const; - /** - * Get whether the navigator is enabled - * @return Boolean determining whether the navigator is enabled - */ - bool isEnabled() const; + /** + * Get whether the navigator is enabled + * @return Boolean determining whether the navigator is enabled + */ + bool isEnabled() const; - /** - * Set enabled to \p enabled - * @param enabled Boolean determining whether the navigator is enabled - */ - void setEnabled(bool enabled); + /** + * Set enabled to \p enabled + * @param enabled Boolean determining whether the navigator is enabled + */ + void setEnabled(bool enabled); public: // Navigation - /** - * Zoom by \p factor around \p center - * @param center Point to zoom around - * @param factor Zoom factor - */ - void zoomAround(const QPoint& center, float factor); + /** + * Zoom by \p factor around \p center + * @param center Point to zoom around + * @param factor Zoom factor + */ + void zoomAround(const QPoint& center, float factor); - /** - * Zoom to \p zoomRectangle - * @param zoomRectangle Zoom to this rectangle - */ - void zoomToRectangle(const QRectF& zoomRectangle); + /** + * Zoom to \p zoomRectangle + * @param zoomRectangle Zoom to this rectangle + */ + void zoomToRectangle(const QRectF& zoomRectangle); - /** - * Pan by \p delta - * @param delta Pan by this amount - */ - void panBy(const QPointF& delta); + /** + * Pan by \p delta + * @param delta Pan by this amount + */ + void panBy(const QPointF& delta); /** - * Reset the view - * @param force Force reset event when the user has navigated + * Set the zoom center in world coordinates to \p zoomCenterWorld + * @param zoomCenterWorld Zoom center in world coordinates */ - void resetView(bool force = true); + void setZoomCenterWorld(const QPointF& zoomCenterWorld); - /** - * Get whether the renderer is panning - * @return Boolean determining whether the renderer is panning - */ - bool isPanning() const; + /** + * Reset the view + * @param force Force reset event when the user has navigated + */ + void resetView(bool force = true); - /** - * Get whether the renderer is zooming - * @return Boolean determining whether the renderer is zooming - */ - bool isZooming() const; + /** + * Get whether the renderer is panning + * @return Boolean determining whether the renderer is panning + */ + bool isPanning() const; - /** - * Get whether the renderer is navigating - * @return Boolean determining whether the renderer is navigating - */ - bool isNavigating() const; + /** + * Get whether the renderer is zooming + * @return Boolean determining whether the renderer is zooming + */ + bool isZooming() const; - /** - * Get whether the user has navigated - * @return Boolean determining whether the user has navigated - */ - bool hasUserNavigated() const; + /** + * Get whether the renderer is navigating + * @return Boolean determining whether the renderer is navigating + */ + bool isNavigating() const; + + /** + * Get whether the user has navigated + * @return Boolean determining whether the user has navigated + */ + bool hasUserNavigated() const; protected: // Navigation - /** - * Set whether the renderer is panning to \p isPanning - * @param isPanning Boolean determining whether the renderer is panning - */ - void setIsPanning(bool isPanning); + /** + * Set whether the renderer is panning to \p isPanning + * @param isPanning Boolean determining whether the renderer is panning + */ + void setIsPanning(bool isPanning); - /** - * Set whether the renderer is zooming to \p isZooming - * @param isZooming Boolean determining whether the renderer is zooming - */ - void setIsZooming(bool isZooming); + /** + * Set whether the renderer is zooming to \p isZooming + * @param isZooming Boolean determining whether the renderer is zooming + */ + void setIsZooming(bool isZooming); - /** - * Set whether the renderer is navigating to \p isNavigating - * @param isNavigating Boolean determining whether the renderer is navigating - */ - void setIsNavigating(bool isNavigating); + /** + * Set whether the renderer is navigating to \p isNavigating + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void setIsNavigating(bool isNavigating); + + /** Panning has begun */ + void beginPanning(); - /** Panning has begun */ - void beginPanning(); + /** Panning has ended */ + void endPanning(); - /** Panning has ended */ - void endPanning(); + /** Zooming has begun */ + void beginZooming(); - /** Zooming has begun */ - void beginZooming(); + /** Zooming has ended */ + void endZooming(); - /** Zooming has ended */ - void endZooming(); + /** Navigation has begun */ + void beginNavigation(); - /** Navigation has begun */ - void beginNavigation(); + /** Navigation has ended */ + void endNavigation(); - /** Navigation has ended */ - void endNavigation(); + /** Begin changing the zoom rectangle in world coordinates */ + void beginChangeZoomRectangleWorld(); - /** Begin changing the zoom rectangle in world coordinates */ - void beginChangeZoomRectangleWorld(); + /** End changing the zoom rectangle in world coordinates */ + void endChangeZoomRectangleWorld(); - /** End changing the zoom rectangle in world coordinates */ - void endChangeZoomRectangleWorld(); +private: + + /** Smoothes the navigation over time */ + void updateNavigation(); signals: - /** Signals that panning has started */ - void panningStarted(); + /** Signals that panning has started */ + void panningStarted(); - /** Signals that panning has ended */ - void panningEnded(); + /** Signals that panning has ended */ + void panningEnded(); - /** - * Signals that is panning changed to \p isPanning - * @param isPanning - */ - void isPanningChanged(bool isPanning); + /** + * Signals that is panning changed to \p isPanning + * @param isPanning + */ + void isPanningChanged(bool isPanning); - /** Signals that zooming has started */ - void zoomingStarted(); + /** Signals that zooming has started */ + void zoomingStarted(); - /** Signals that zooming has ended */ - void zoomingEnded(); + /** Signals that zooming has ended */ + void zoomingEnded(); - /** - * Signals that is zooming changed to \p isZooming - * @param isZooming Boolean determining whether the renderer is zooming - */ - void isZoomingChanged(bool isZooming); + /** + * Signals that is zooming changed to \p isZooming + * @param isZooming Boolean determining whether the renderer is zooming + */ + void isZoomingChanged(bool isZooming); - /** Signals that navigation has started */ - void navigationStarted(); + /** Signals that navigation has started */ + void navigationStarted(); - /** Signals that navigation has ended */ - void navigationEnded(); + /** Signals that navigation has ended */ + void navigationEnded(); - /** - * Signals that is navigating changed to \p isNavigating - * @param isNavigating Boolean determining whether the renderer is navigating - */ - void isNavigatingChanged(bool isNavigating); + /** + * Signals that is navigating changed to \p isNavigating + * @param isNavigating Boolean determining whether the renderer is navigating + */ + void isNavigatingChanged(bool isNavigating); /** - * Signals that enabled changed to \p enabled - * @param enabled Boolean determining whether the navigator is enabled + * Signals that enabled changed to \p enabled + * @param enabled Boolean determining whether the navigator is enabled */ void enabledChanged(bool enabled); - /** - * Signals that the zoom rectangle in world coordinates has changed from \p previousZoomRectangleWorld to \p currentZoomRectangleWorld - * @param previousZoomRectangleWorld Previous world zoom rectangle - * @param currentZoomRectangleWorld Current world zoom rectangle - */ - void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); + /** + * Signals that the zoom rectangle in world coordinates has changed from \p previousZoomRectangleWorld to \p currentZoomRectangleWorld + * @param previousZoomRectangleWorld Previous world zoom rectangle + * @param currentZoomRectangleWorld Current world zoom rectangle + */ + void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _enabled; /** Enabled flag */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ - //QSizeF _zoomRectangleWorldSize; /** Zoom rectangle size in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ - QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ - bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ + float _zoomRectangleMargin; /** Zoom rectangle margin */ + QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + QTimer _updateNavigationTimer; /** Timer for updating the navigation */ + QList _zoomCenterHistory; /** History of zoom centers */ + QList _zoomFactorHistory; /** History of zoom factors */ + + static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ }; } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index ae76f98bd..49dfe1a44 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -38,7 +38,6 @@ Q_OBJECT /** * Resize the renderer to \p renderSize - * * @param renderSize New size of the renderer */ void resize(QSize renderSize) override; @@ -147,7 +146,7 @@ Q_OBJECT * Set data bounds to \p dataBounds * @param dataBounds Data bounds */ - void setDataBounds(const QRectF& dataBounds); + virtual void setDataBounds(const QRectF& dataBounds); private: From a78ca34054c544e525b51c2868373190d62158fc Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 31 Mar 2025 14:28:36 +0200 Subject: [PATCH 66/89] Work on density renderer --- ManiVault/res/shaders/DensityDraw.frag | 4 +--- ManiVault/src/renderers/DensityRenderer.cpp | 9 ++++----- ManiVault/src/util/DensityComputation.h | 3 +++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index 34a8f1b7b..d1b6db1be 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -13,7 +13,5 @@ out vec4 fragColor; void main() { float f = 1 - (texture(tex, outUv).r * norm); - fragColor = vec4(vec3(f, 0, 1), 1); - //fragColor = vec4(outUv.y, 0, 0, 1); - //fragColor = vec4(1, 0, 0, 1); + fragColor = vec4(vec3(f), 1); } \ No newline at end of file diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 55d8e0a2a..0de7d8185 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -143,9 +143,10 @@ void DensityRenderer::destroy() void DensityRenderer::updateQuad() { - const float width = 128.f;//static_cast(getDataBounds().width()); - const float height = 128.f;//static_cast(getDataBounds().height()); - + const auto textureSize = _densityComputation.getDensityTextureSize().toSizeF(); + const auto width = static_cast(textureSize.width()); + const auto height = static_cast(textureSize.height()); + float vertices[] = { 0.f, 0.f, 0.0f, 0.0f, width, 0.f, 1.0f, 0.0f, @@ -201,8 +202,6 @@ void DensityRenderer::drawDensity() return; } - qDebug() << "DensityRenderer::drawDensity()" << maxDensity; - _shaderDensityDraw.bind(); { _densityComputation.getDensityTexture().bind(0); diff --git a/ManiVault/src/util/DensityComputation.h b/ManiVault/src/util/DensityComputation.h index af1d283e4..4ae580e28 100644 --- a/ManiVault/src/util/DensityComputation.h +++ b/ManiVault/src/util/DensityComputation.h @@ -43,6 +43,9 @@ class CORE_EXPORT DensityComputation : protected QOpenGLFunctions_3_3_Core void compute(); + + QSize getDensityTextureSize() const { return QSize(RESOLUTION, RESOLUTION); } + private: bool hasData() const; float calculateMaxKDE(); From 3e860c8aadeed497bbe4c11eb49e5b830d207d77 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 31 Mar 2025 15:24:37 +0200 Subject: [PATCH 67/89] Landscape rendering is working --- ManiVault/res/shaders/DensityDraw.frag | 4 ++-- ManiVault/res/shaders/IsoDensityDraw.frag | 16 ++++++++-------- ManiVault/res/shaders/Quad.vert | 4 ++-- ManiVault/src/renderers/DensityRenderer.cpp | 5 +++-- ManiVault/src/renderers/Renderer2D.cpp | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ManiVault/res/shaders/DensityDraw.frag b/ManiVault/res/shaders/DensityDraw.frag index d1b6db1be..b1855dca3 100644 --- a/ManiVault/res/shaders/DensityDraw.frag +++ b/ManiVault/res/shaders/DensityDraw.frag @@ -7,11 +7,11 @@ uniform sampler2D tex; uniform float norm; -in vec2 outUv; +in vec2 passUv; out vec4 fragColor; void main() { - float f = 1 - (texture(tex, outUv).r * norm); + float f = 1 - (texture(tex, passUv).r * norm); fragColor = vec4(vec3(f), 1); } \ No newline at end of file diff --git a/ManiVault/res/shaders/IsoDensityDraw.frag b/ManiVault/res/shaders/IsoDensityDraw.frag index 8f0650894..3299a083e 100644 --- a/ManiVault/res/shaders/IsoDensityDraw.frag +++ b/ManiVault/res/shaders/IsoDensityDraw.frag @@ -10,19 +10,19 @@ uniform sampler2D densityMap; uniform vec2 renderParams; uniform vec3 colorMapRange; -in vec2 uv; +in vec2 passUv; out vec4 fragColor; // Get the normalized density from the color map range -float getNormalizedDensity(vec2 uv) +float getNormalizedDensity(vec2 passUv) { - float density = texture(densityMap, uv).r; + float density = texture(densityMap, passUv).r; return (density - colorMapRange.x) / colorMapRange.z; } void main() { - float density = getNormalizedDensity(uv); + float density = getNormalizedDensity(passUv); if (density < renderParams.y) discard; @@ -40,10 +40,10 @@ void main() { // Central differences to find out if we draw the iso contour instead of the color vec4 neighborDensities; - neighborDensities.x = getNormalizedDensity(uv + texelSize.xz); - neighborDensities.y = getNormalizedDensity(uv - texelSize.xz); - neighborDensities.z = getNormalizedDensity(uv + texelSize.zy); - neighborDensities.w = getNormalizedDensity(uv - texelSize.zy); + neighborDensities.x = getNormalizedDensity(passUv + texelSize.xz); + neighborDensities.y = getNormalizedDensity(passUv - texelSize.xz); + neighborDensities.z = getNormalizedDensity(passUv + texelSize.zy); + neighborDensities.w = getNormalizedDensity(passUv - texelSize.zy); ivec4 stepId = min(ivec4(floor(neighborDensities * vec4(numSteps+1))), ivec4(numSteps)); isBoundary = (any(notEqual(stepId.xxx, stepId.yzw))); diff --git a/ManiVault/res/shaders/Quad.vert b/ManiVault/res/shaders/Quad.vert index 80d4b7ea3..92b6da723 100644 --- a/ManiVault/res/shaders/Quad.vert +++ b/ManiVault/res/shaders/Quad.vert @@ -9,10 +9,10 @@ layout(location = 1) in vec2 uv; uniform mat4 mvp; -out vec2 outUv; +out vec2 passUv; void main() { gl_Position = mvp * vec4(vertex, 0, 1); - outUv = (vertex / vec2(128.f, 128.f)); + passUv = uv; } diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 0de7d8185..5b66a6d95 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -41,6 +41,8 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) //_densityComputation.setBounds(-100, -100, -100, 100); updateQuad(); + + getNavigator().setZoomRectangleWorld(dataBounds); } void DensityRenderer::setRenderMode(RenderMode renderMode) @@ -111,7 +113,6 @@ void DensityRenderer::init() void DensityRenderer::render() { - qDebug() << "DensityRenderer::render()" << _renderMode; beginRender(); { switch (_renderMode) { @@ -212,7 +213,7 @@ void DensityRenderer::drawDensity() drawQuad(); } - //_shaderDensityDraw.release(); + _shaderDensityDraw.release(); } void DensityRenderer::drawLandscape() diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 50ae9e190..a404b641f 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -151,7 +151,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QMatrix4x4 matrix; // Create an orthogonal transformation matrix - matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); + matrix.ortho(-halfSize.width(), halfSize.width(),0, 2 * halfSize.height(), -1000.0f, +1000.0f); return matrix; } From 1074473dc2f66f42866995b06213bbe426f63197 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 09:36:33 +0200 Subject: [PATCH 68/89] Add generic naviagtion action and use it in the navigator 2D action --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 2 + ManiVault/src/actions/NavigationAction.cpp | 55 +++++++++++++++++++ ManiVault/src/actions/NavigationAction.h | 58 +++++++++++++++++++++ ManiVault/src/renderers/DensityRenderer.cpp | 2 - ManiVault/src/renderers/Navigator2D.cpp | 34 ++++++++---- ManiVault/src/renderers/Navigator2D.h | 17 +++++- 6 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 ManiVault/src/actions/NavigationAction.cpp create mode 100644 ManiVault/src/actions/NavigationAction.h diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index 74c6b9859..f65681709 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -355,6 +355,7 @@ set(PUBLIC_ACTIONS_INTERNAL_HEADERS src/actions/PaletteColorRoleAction.h src/actions/ColorSchemeAction.h src/actions/EditColorSchemeAction.h + src/actions/NavigationAction.h ) set(PUBLIC_ACTIONS_INTERNAL_SOURCES @@ -393,6 +394,7 @@ set(PUBLIC_ACTIONS_INTERNAL_SOURCES src/actions/PaletteColorRoleAction.cpp src/actions/ColorSchemeAction.cpp src/actions/EditColorSchemeAction.cpp + src/actions/NavigationAction.cpp ) set(PUBLIC_ACTIONS_INTERNAL_FILES diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp new file mode 100644 index 000000000..3f6047ac1 --- /dev/null +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "NavigationAction.h" + +#ifdef _DEBUG + //#define NAVIGATION_ACTION_VERBOSE +#endif + +//#define NAVIGATION_ACTION_VERBOSE + +namespace mv::gui +{ + +NavigationAction::NavigationAction(QObject* parent, const QString& title) : + HorizontalGroupAction(parent, title), + _zoomOutAction(this, "Zoom out"), + _zoomPercentageAction(this, "Zoom Percentage", 10.0f, 1000.0f, 100.0f, 1), + _zoomInAction(this, "Zoom In"), + _zoomExtentsAction(this, "Zoom All"), + _zoomSelectionAction(this, "Zoom Around Selection"), + _zoomRegionAction(this, "Zoom Region") +{ + setShowLabels(false); + + _zoomOutAction.setToolTip("Zoom out by 10% (-)"); + _zoomPercentageAction.setToolTip("Zoom in/out (+)"); + _zoomInAction.setToolTip("Zoom in by 10%"); + _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); + _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); + + _zoomOutAction.setIconByName("search-minus"); + _zoomInAction.setIconByName("search-plus"); + _zoomExtentsAction.setIconByName("compress"); + _zoomSelectionAction.setIconByName("search-location"); + + _zoomOutAction.setShortcut(QKeySequence("-")); + _zoomInAction.setShortcut(QKeySequence("+")); + _zoomExtentsAction.setShortcut(QKeySequence("z")); + _zoomSelectionAction.setShortcut(QKeySequence("d")); + + _zoomSelectionAction.setEnabled(false); + + _zoomPercentageAction.setSuffix("%"); + _zoomPercentageAction.setUpdateDuringDrag(false); + + addAction(&_zoomOutAction, gui::TriggerAction::Icon); + addAction(&_zoomPercentageAction); + addAction(&_zoomInAction, gui::TriggerAction::Icon); + addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); + addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); +} + +} diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h new file mode 100644 index 000000000..a33fa20b6 --- /dev/null +++ b/ManiVault/src/actions/NavigationAction.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include +#include +#include +#include + +#include + +namespace mv::gui +{ + +/** + * Navigation action class + * + * Provides actions for navigating in a renderer (the business logic + * should be handled elsewhere depending on the renderer). + * + * Note: This action is developed for internal use only + * + * @author Thomas Kroes + */ +class CORE_EXPORT NavigationAction : public HorizontalGroupAction +{ + Q_OBJECT + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + */ + Q_INVOKABLE NavigationAction(QObject* parent, const QString& title); + +public: // Action getters + + TriggerAction& getZoomOutAction() { return _zoomOutAction; } + DecimalAction& getZoomPercentageAction() { return _zoomPercentageAction; } + TriggerAction& getZoomInAction() { return _zoomInAction; } + TriggerAction& getZoomExtentsAction() { return _zoomExtentsAction; } + TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } + TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } + +private: + TriggerAction _zoomOutAction; /** Zoom out action */ + DecimalAction _zoomPercentageAction; /** Zoom action */ + TriggerAction _zoomInAction; /** Zoom in action */ + TriggerAction _zoomExtentsAction; /** Zoom extents action */ + TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ + TriggerAction _zoomRegionAction; /** Zoom to region action */ +}; + +} diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 5b66a6d95..ffe8d9f39 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -36,9 +36,7 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); - qDebug() << "DensityRenderer::setDataBounds()" << dataBounds; _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); - //_densityComputation.setBounds(-100, -100, -100, 100); updateQuad(); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 277dcc5a1..6d5964d17 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -12,6 +12,8 @@ //#define NAVIGATOR_2D_VERBOSE +using namespace mv::gui; + namespace mv { @@ -25,7 +27,8 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isZooming(false), _zoomFactor(1.0f), _zoomRectangleMargin(50.f), - _userHasNavigated() + _userHasNavigated(), + _navigationAction(this, "Navigation") { } @@ -33,18 +36,21 @@ void Navigator2D::initialize(QWidget* sourceWidget) { Q_ASSERT(sourceWidget); - if (sourceWidget) { - _sourceWidget = sourceWidget; + if (!sourceWidget) + return; - _sourceWidget->installEventFilter(this); - _sourceWidget->setFocusPolicy(Qt::StrongFocus); + _sourceWidget = sourceWidget; - connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); + _sourceWidget->installEventFilter(this); + _sourceWidget->setFocusPolicy(Qt::StrongFocus); - _updateNavigationTimer.start(16); + connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); - _initialized = true; - } + _updateNavigationTimer.start(16); + + connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + + _initialized = true; } bool Navigator2D::eventFilter(QObject* watched, QEvent* event) @@ -208,6 +214,16 @@ void Navigator2D::setEnabled(bool enabled) emit enabledChanged(_enabled); } +gui::NavigationAction& Navigator2D::getNavigationAction() +{ + return _navigationAction; +} + +const gui::NavigationAction& Navigator2D::getNavigationAction() const +{ + return _navigationAction; +} + void Navigator2D::zoomAround(const QPoint& center, float factor) { if (!_initialized) diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e38e14917..e045e1991 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include #include @@ -91,6 +93,18 @@ class CORE_EXPORT Navigator2D : public QObject */ void setEnabled(bool enabled); + /** + * Get the navigation action + * @return Reference to the navigation action + */ + gui::NavigationAction& getNavigationAction(); + + /** + * Get the navigation action + * @return Reference to the navigation action + */ + const gui::NavigationAction& getNavigationAction() const; + public: // Navigation /** @@ -263,8 +277,7 @@ class CORE_EXPORT Navigator2D : public QObject QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ QTimer _updateNavigationTimer; /** Timer for updating the navigation */ - QList _zoomCenterHistory; /** History of zoom centers */ - QList _zoomFactorHistory; /** History of zoom factors */ + gui::NavigationAction _navigationAction; /** Navigation group action */ static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ }; From 9b138b305cf564a5381427050a1d0220023f899b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 10:02:44 +0200 Subject: [PATCH 69/89] Work on navigation action --- ManiVault/src/actions/NavigationAction.cpp | 25 +++++++++++++++++++++- ManiVault/src/actions/NavigationAction.h | 18 ++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 3f6047ac1..d91842340 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -10,6 +10,8 @@ //#define NAVIGATION_ACTION_VERBOSE +using namespace mv::util; + namespace mv::gui { @@ -20,7 +22,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomInAction(this, "Zoom In"), _zoomExtentsAction(this, "Zoom All"), _zoomSelectionAction(this, "Zoom Around Selection"), - _zoomRegionAction(this, "Zoom Region") + _zoomRegionAction(this, "Zoom Region"), + _zoomRectangleAction(this, "Zoom Rectangle") { setShowLabels(false); @@ -45,6 +48,10 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomPercentageAction.setSuffix("%"); _zoomPercentageAction.setUpdateDuringDrag(false); + _zoomRectangleAction.setToolTip("Extents of the current view"); + _zoomRectangleAction.setIcon(combineIcons(StyledIcon("expand"), StyledIcon("ellipsis-h"))); + _zoomRectangleAction.setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup); + addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); @@ -52,4 +59,20 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); } +void NavigationAction::fromVariantMap(const QVariantMap& variantMap) +{ + HorizontalGroupAction::fromVariantMap(variantMap); + + _zoomRectangleAction.fromParentVariantMap(variantMap); +} + +QVariantMap NavigationAction::toVariantMap() const +{ + auto variantMap = HorizontalGroupAction::toVariantMap(); + + _zoomRectangleAction.insertIntoVariantMap(variantMap); + + return variantMap; +} + } diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index a33fa20b6..7450269a2 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -37,6 +38,21 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction */ Q_INVOKABLE NavigationAction(QObject* parent, const QString& title); +public: // Serialization + + /** + * Load navigation action from variant map + * @param variantMap Variant map representation of the navigation 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 TriggerAction& getZoomOutAction() { return _zoomOutAction; } @@ -45,6 +61,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction& getZoomExtentsAction() { return _zoomExtentsAction; } TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } + DecimalRectangleAction& getZoomRectangleAction() { return _zoomRectangleAction; } private: TriggerAction _zoomOutAction; /** Zoom out action */ @@ -53,6 +70,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction _zoomExtentsAction; /** Zoom extents action */ TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ TriggerAction _zoomRegionAction; /** Zoom to region action */ + DecimalRectangleAction _zoomRectangleAction; /** Rectangle action for setting the current zoom bounds */ }; } From 16c384637c88b6154a15ebc5af48cdaa302e157f Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 10:38:13 +0200 Subject: [PATCH 70/89] Work on navigation action --- ManiVault/src/renderers/Navigator2D.cpp | 81 ++++++++++++++----------- ManiVault/src/renderers/Navigator2D.h | 12 ++-- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 6d5964d17..65276e5e5 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -44,11 +44,38 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); - connect(&_updateNavigationTimer, &QTimer::timeout, this, &Navigator2D::updateNavigation); + connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + + const auto zoomRectangleChanged = [this]() -> void { + setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); + }; - _updateNavigationTimer.start(16); + zoomRectangleChanged(); - connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + + connect(this, &Navigator2D::zoomRectangleWorldChanged, this, [this, zoomRectangleChanged](const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld) -> void { + disconnect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, nullptr); + { + _navigationAction.getZoomExtentsAction().setEnabled(hasUserNavigated()); + + _navigationAction.getZoomRectangleAction().setLeft(static_cast(currentZoomRectangleWorld.left())); + _navigationAction.getZoomRectangleAction().setRight(static_cast(currentZoomRectangleWorld.right())); + _navigationAction.getZoomRectangleAction().setTop(static_cast(currentZoomRectangleWorld.bottom())); + _navigationAction.getZoomRectangleAction().setBottom(static_cast(currentZoomRectangleWorld.top())); + } + connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + + _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); + }); + + connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { + zoomAround(_sourceWidget->rect().center(), 1.1f); + }); + + connect(&_navigationAction.getZoomOutAction(), &TriggerAction::triggered, this, [this]() -> void { + zoomAround(_sourceWidget->rect().center(), .9f); + }); _initialized = true; } @@ -199,6 +226,23 @@ float Navigator2D::getZoomFactor() const return _zoomFactor; } +float Navigator2D::getZoomPercentage() const +{ + const auto& dataBounds = _renderer.getDataBounds(); + + float zoomPercentage = 1.f; + + const auto zoomRectangleMarginWorld = (_renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint(_zoomRectangleMargin, 0)) - _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint())).x(); + const auto zoomRectangleWorldWithMarginsAdded = getZoomRectangleWorld().marginsRemoved(QMarginsF(zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld)); + + if (dataBounds.width() < dataBounds.height()) + zoomPercentage = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorldWithMarginsAdded.height()); + else + zoomPercentage = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorldWithMarginsAdded.width()); + + return zoomPercentage * 100.f; +} + bool Navigator2D::isEnabled() const { return _enabled; @@ -329,8 +373,6 @@ void Navigator2D::resetView(bool force /*= true*/) setZoomCenterWorld(_renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height())); - - // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); @@ -499,33 +541,4 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } -void Navigator2D::updateNavigation() -{ - - //QPointF smoothedZoomCenterWorld{}; - - //if (!_zoomCenterHistory.empty()) { - // QPointF sum(0, 0); - - // for (const auto& zoomCenterWorld : _zoomCenterHistory) { - // sum += zoomCenterWorld; - // } - - // smoothedZoomCenterWorld = sum / static_cast(_zoomCenterHistory.size()); - - // _zoomCenterWorld += (smoothedZoomCenterWorld - _zoomCenterWorld); - - // if (!_zoomCenterHistory.empty()) { - // _zoomCenterHistory.pop_back(); - // } - //} - - //qDebug() << __FUNCTION__ << _zoomCenterWorld; - - - - - //setZoomRectangleWorld(getZoomRectangleWorld()); -} - } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e045e1991..abf994846 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -81,6 +81,12 @@ class CORE_EXPORT Navigator2D : public QObject */ float getZoomFactor() const; + /** + * Get the zoom percentage + * @return Zoom percentage + */ + float getZoomPercentage() const; + /** * Get whether the navigator is enabled * @return Boolean determining whether the navigator is enabled @@ -206,11 +212,6 @@ class CORE_EXPORT Navigator2D : public QObject /** End changing the zoom rectangle in world coordinates */ void endChangeZoomRectangleWorld(); -private: - - /** Smoothes the navigation over time */ - void updateNavigation(); - signals: /** Signals that panning has started */ @@ -276,7 +277,6 @@ class CORE_EXPORT Navigator2D : public QObject float _zoomRectangleMargin; /** Zoom rectangle margin */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ - QTimer _updateNavigationTimer; /** Timer for updating the navigation */ gui::NavigationAction _navigationAction; /** Navigation group action */ static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ From 458979ef51332f7651995ffcb3ab1e6a12393c52 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 11:59:40 +0200 Subject: [PATCH 71/89] getZoomPercentage is working --- ManiVault/src/actions/NavigationAction.cpp | 9 +++-- ManiVault/src/renderers/Navigator2D.cpp | 43 ++++++++++++---------- ManiVault/src/renderers/Navigator2D.h | 6 +++ 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index d91842340..87538485f 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -18,10 +18,10 @@ namespace mv::gui NavigationAction::NavigationAction(QObject* parent, const QString& title) : HorizontalGroupAction(parent, title), _zoomOutAction(this, "Zoom out"), - _zoomPercentageAction(this, "Zoom Percentage", 10.0f, 1000.0f, 100.0f, 1), + _zoomPercentageAction(this, "Zoom Percentage", 0.01f, 1000.0f, 100.0f, 1), _zoomInAction(this, "Zoom In"), _zoomExtentsAction(this, "Zoom All"), - _zoomSelectionAction(this, "Zoom Around Selection"), + _zoomSelectionAction(this, "Zoom Selection"), _zoomRegionAction(this, "Zoom Region"), _zoomRectangleAction(this, "Zoom Rectangle") { @@ -55,8 +55,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); - addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); - addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); + addAction(&_zoomExtentsAction); + //addAction(&_zoomSelectionAction); + //addAction(&_zoomRegionAction); } void NavigationAction::fromVariantMap(const QVariantMap& variantMap) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 65276e5e5..a92537e2b 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -26,7 +26,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleMargin(50.f), + _zoomRectangleMargin(0.f), _userHasNavigated(), _navigationAction(this, "Navigation") { @@ -66,6 +66,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) } connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); + qDebug() << __FUNCTION__ << getZoomPercentage(); _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); }); @@ -73,6 +74,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) zoomAround(_sourceWidget->rect().center(), 1.1f); }); + connect(&_navigationAction.getZoomPercentageAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + setZoomPercentage(value); + }); + connect(&_navigationAction.getZoomOutAction(), &TriggerAction::triggered, this, [this]() -> void { zoomAround(_sourceWidget->rect().center(), .9f); }); @@ -228,19 +233,26 @@ float Navigator2D::getZoomFactor() const float Navigator2D::getZoomPercentage() const { - const auto& dataBounds = _renderer.getDataBounds(); + const auto dataBounds = _renderer.getDataBounds(); + const auto zoomRectangleWorld = getZoomRectangleWorld(); - float zoomPercentage = 1.f; + if (!dataBounds.isValid() || !zoomRectangleWorld.isValid()) + return 1.0f; - const auto zoomRectangleMarginWorld = (_renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint(_zoomRectangleMargin, 0)) - _renderer.getScreenPointToWorldPosition(getViewMatrix(), QPoint())).x(); - const auto zoomRectangleWorldWithMarginsAdded = getZoomRectangleWorld().marginsRemoved(QMarginsF(zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld, zoomRectangleMarginWorld)); + const auto viewerSize = _sourceWidget->size(); + const auto totalMargins = 2 * _zoomRectangleMargin; + const auto factorX = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorld.width()); + const auto factorY = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorld.height()); + const auto scaleFactor = factorX > factorY ? factorX : factorY; + + return scaleFactor * 100.f; +} - if (dataBounds.width() < dataBounds.height()) - zoomPercentage = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorldWithMarginsAdded.height()); - else - zoomPercentage = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorldWithMarginsAdded.width()); +void Navigator2D::setZoomPercentage(float zoomPercentage) +{ + //_zoomFactor = 100.f / zoomPercentage; - return zoomPercentage * 100.f; + //setZoomRectangleWorld(getZoomRectangleWorld()); } bool Navigator2D::isEnabled() const @@ -342,15 +354,6 @@ void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) _zoomCenterWorld = zoomCenterWorld; setZoomRectangleWorld(getZoomRectangleWorld()); - - //if (std::isnan(zoomCenterWorld.x()) || std::isnan(zoomCenterWorld.y())) - // return; - - //_zoomCenterHistory.push_front(zoomCenterWorld); - - //if (_zoomCenterHistory.size() > Navigator2D::maxZoomHistorySize) { - // _zoomCenterHistory.pop_back(); - //} } void Navigator2D::resetView(bool force /*= true*/) @@ -371,7 +374,7 @@ void Navigator2D::resetView(bool force /*= true*/) _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - setZoomCenterWorld(_renderer.getDataBounds().center() - QPointF(0.f, _renderer.getDataBounds().height())); + setZoomCenterWorld(QPoint()); // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index abf994846..2bf4690a1 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -87,6 +87,12 @@ class CORE_EXPORT Navigator2D : public QObject */ float getZoomPercentage() const; + /** + * Set the zoom percentage to \p zoomPercentage + * @param zoomPercentage Zoom percentage + */ + void setZoomPercentage(float zoomPercentage); + /** * Get whether the navigator is enabled * @return Boolean determining whether the navigator is enabled From 0d7ca303cf433f3b473647d794be99264a3c79fc Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 13:15:20 +0200 Subject: [PATCH 72/89] Fix setZoomPercentage --- ManiVault/src/renderers/DensityRenderer.cpp | 1 + ManiVault/src/renderers/Navigator2D.cpp | 41 +++++++++++++++++---- ManiVault/src/renderers/Navigator2D.h | 2 +- ManiVault/src/renderers/PointRenderer.cpp | 7 ++++ ManiVault/src/renderers/PointRenderer.h | 6 +++ ManiVault/src/renderers/Renderer2D.cpp | 28 ++++++++++++-- ManiVault/src/renderers/Renderer2D.h | 31 +++++++++++++++- 7 files changed, 102 insertions(+), 14 deletions(-) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index ffe8d9f39..71c3e0fc2 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -17,6 +17,7 @@ DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); + setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); } DensityRenderer::~DensityRenderer() diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index a92537e2b..1b93d2ab1 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -44,7 +44,9 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->installEventFilter(this); _sourceWidget->setFocusPolicy(Qt::StrongFocus); - connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, &Navigator2D::resetView); + connect(&getNavigationAction().getZoomExtentsAction(), &TriggerAction::triggered, this, [this]() -> void { + resetView(true); + }); const auto zoomRectangleChanged = [this]() -> void { setZoomRectangleWorld(_navigationAction.getZoomRectangleAction().toRectF()); @@ -66,8 +68,12 @@ void Navigator2D::initialize(QWidget* sourceWidget) } connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); - qDebug() << __FUNCTION__ << getZoomPercentage(); _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); + + //const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); + //const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); + + //qDebug() << "-----" << _zoomFactor << 0.01f * getZoomPercentage() << zoomFactorY / (0.01f * getZoomPercentage()); }); connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { @@ -82,6 +88,11 @@ void Navigator2D::initialize(QWidget* sourceWidget) zoomAround(_sourceWidget->rect().center(), .9f); }); + connect(&_renderer, &Renderer2D::worldBoundsChanged, this, [this](const QRectF& worldBounds) -> void { + if (!hasUserNavigated()) + resetView(); + }); + _initialized = true; } @@ -239,8 +250,6 @@ float Navigator2D::getZoomPercentage() const if (!dataBounds.isValid() || !zoomRectangleWorld.isValid()) return 1.0f; - const auto viewerSize = _sourceWidget->size(); - const auto totalMargins = 2 * _zoomRectangleMargin; const auto factorX = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorld.width()); const auto factorY = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorld.height()); const auto scaleFactor = factorX > factorY ? factorX : factorY; @@ -250,9 +259,16 @@ float Navigator2D::getZoomPercentage() const void Navigator2D::setZoomPercentage(float zoomPercentage) { - //_zoomFactor = 100.f / zoomPercentage; + if (zoomPercentage < 0.05f) + return; + + const auto zoomPercentageNormalized = .01f * zoomPercentage; + const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); + + _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; - //setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomRectangleWorld(getZoomRectangleWorld()); } bool Navigator2D::isEnabled() const @@ -356,11 +372,14 @@ void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) setZoomRectangleWorld(getZoomRectangleWorld()); } -void Navigator2D::resetView(bool force /*= true*/) +void Navigator2D::resetView(bool force /*= false*/) { if (!_initialized) return; + if (!force && hasUserNavigated()) + return; + #ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << force; #endif @@ -374,17 +393,21 @@ void Navigator2D::resetView(bool force /*= true*/) _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - setZoomCenterWorld(QPoint()); + setZoomCenterWorld(_renderer.getDataBounds().center()); // if (!_userHasNavigated || force) { // setZoomRectangleWorld(_renderer.getDataBounds()); // _userHasNavigated = false; //} + + } endChangeZoomRectangleWorld(); } endZooming(); + + _userHasNavigated = false; } bool Navigator2D::isPanning() const @@ -515,6 +538,8 @@ void Navigator2D::beginNavigation() qDebug() << __FUNCTION__; #endif + _userHasNavigated = true; + setIsNavigating(true); emit navigationStarted(); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 2bf4690a1..5e744c02a 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -148,7 +148,7 @@ class CORE_EXPORT Navigator2D : public QObject * Reset the view * @param force Force reset event when the user has navigated */ - void resetView(bool force = true); + void resetView(bool force = false); /** * Get whether the renderer is panning diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 1f15a5ea7..7da14ad71 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -256,6 +256,13 @@ namespace mv _positionBuffer.destroy(); } + void PointRenderer::setDataBounds(const QRectF& dataBounds) + { + Renderer2D::setDataBounds(dataBounds); + + setWorldBounds(dataBounds); + } + void PointRenderer::setData(const std::vector& positions) { _gpuPoints.setPositions(positions); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index b0ca4c781..3ff62840b 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -135,6 +135,12 @@ namespace mv public: using Renderer2D::Renderer2D; + /** + * Set data bounds to \p dataBounds + * @param dataBounds Data bounds + */ + void setDataBounds(const QRectF& dataBounds) override; + void setData(const std::vector& points); void setHighlights(const std::vector& highlights, const std::int32_t& numSelectedPoints); void setFocusHighlights(const std::vector& focusHighlights, const std::int32_t& numberOfFocusHighlights); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index a404b641f..8adda12e9 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -68,15 +68,35 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) qDebug() << __FUNCTION__ << dataBounds; #endif + const auto previousDataBounds = _dataBounds; + if (dataBounds == _dataBounds) return; _dataBounds = dataBounds; - //_dataBounds.setWidth(std::max(_dataBounds.width(), 0.00000001)); - //_dataBounds.setHeight(std::max(_dataBounds.height(), 0.0001)); + emit dataBoundsChanged(previousDataBounds, _dataBounds); +} + +QRectF Renderer2D::getWorldBounds() const +{ + return _worldBounds; +} + +void Renderer2D::setWorldBounds(const QRectF& worldBounds) +{ +#ifdef RENDERER_2D_VERBOSE + qDebug() << __FUNCTION__ << _worldBounds; +#endif + + const auto previousWorldBounds = _worldBounds; + + if (worldBounds == _worldBounds) + return; + + _worldBounds = worldBounds; - getNavigator().resetView(); + emit worldBoundsChanged(previousWorldBounds, _worldBounds); } void Renderer2D::updateModelViewProjectionMatrix() @@ -151,7 +171,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QMatrix4x4 matrix; // Create an orthogonal transformation matrix - matrix.ortho(-halfSize.width(), halfSize.width(),0, 2 * halfSize.height(), -1000.0f, +1000.0f); + matrix.ortho(-halfSize.width(), halfSize.width(), halfSize.height(), -halfSize.height(), -1000.0f, +1000.0f); return matrix; } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 49dfe1a44..40e4c548e 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -148,16 +148,45 @@ Q_OBJECT */ virtual void setDataBounds(const QRectF& dataBounds); + /** + * Get world bounds + * @return World bounds + */ + QRectF getWorldBounds() const; + + /** + * Set world bounds to \p worldBounds + * @param worldBounds World bounds + */ + virtual void setWorldBounds(const QRectF& worldBounds); + private: /** Update the model-view-projection matrix */ void updateModelViewProjectionMatrix(); +signals: + + /** + * Signals that the data bounds have changed from \p previousDataBounds to \p currentDataBounds + * @param previousDataBounds Previous data bounds + * @param currentDataBounds Current data bounds + */ + void dataBoundsChanged(const QRectF& previousDataBounds, const QRectF& currentDataBounds); + + /** + * Signals that the world bounds have changed from \p previousWorldBounds to \p currentWorldBounds + * @param previousWorldBounds Previous world bounds + * @param currentWorldBounds Current world bounds + */ + void worldBoundsChanged(const QRectF& previousWorldBounds, const QRectF& currentWorldBounds); + private: QSize _renderSize; /** Size of the renderer canvas */ Navigator2D _navigator; /** 2D navigator */ QRectF _dataBounds; /** Bounds of the data */ - QMatrix4x4 _modelMatrix; + QRectF _worldBounds; /** Bounds of the world */ + QMatrix4x4 _modelMatrix; /** Model matrix */ QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ friend class Navigator2D; From 770512efc89411e66345b91ac1f5f6affb756fd7 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 13:23:21 +0200 Subject: [PATCH 73/89] Re-instate widget action override size hint --- ManiVault/src/actions/NavigationAction.cpp | 2 ++ ManiVault/src/actions/WidgetAction.h | 2 -- ManiVault/src/actions/WidgetActionWidget.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 87538485f..12c75aca4 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -33,6 +33,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); + _zoomPercentageAction.setOverrideSizeHint(QSize(500, 0)); + _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); _zoomExtentsAction.setIconByName("compress"); diff --git a/ManiVault/src/actions/WidgetAction.h b/ManiVault/src/actions/WidgetAction.h index 979dbcfdf..6210ed45c 100644 --- a/ManiVault/src/actions/WidgetAction.h +++ b/ManiVault/src/actions/WidgetAction.h @@ -772,14 +772,12 @@ class CORE_EXPORT WidgetAction : public QWidgetAction, public util::Serializable * Get override size hint * @return Override size hint */ - [[deprecated("This method is a placeholder and not operational yet")]] QSize getOverrideSizeHint() const; /** * Set override size hint * @param overrideSizeHint Override size hint */ - [[deprecated("This method is a placeholder and not operational yet")]] void setOverrideSizeHint(const QSize& overrideSizeHint); public: // Configuration flags diff --git a/ManiVault/src/actions/WidgetActionWidget.cpp b/ManiVault/src/actions/WidgetActionWidget.cpp index 27945d924..46efb24fe 100644 --- a/ManiVault/src/actions/WidgetActionWidget.cpp +++ b/ManiVault/src/actions/WidgetActionWidget.cpp @@ -30,8 +30,8 @@ QSize WidgetActionWidget::sizeHint() const return popupSizeHint; } - //if (!action->getOverrideSizeHint().isNull()) - // return action->getOverrideSizeHint(); + if (action->getOverrideSizeHint().width() > 0 || action->getOverrideSizeHint().height() > 0) + return action->getOverrideSizeHint(); return QWidget::sizeHint(); } From c2cf9a19c457e827ef7f9851d5e8ad528ea74554 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 13:48:44 +0200 Subject: [PATCH 74/89] Work on view alignment --- ManiVault/src/actions/NavigationAction.cpp | 2 +- ManiVault/src/renderers/DensityRenderer.cpp | 4 ---- ManiVault/src/renderers/Navigator2D.cpp | 23 ++++++++++++++------- ManiVault/src/renderers/Renderer2D.cpp | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 12c75aca4..0a092bce3 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -33,7 +33,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); - _zoomPercentageAction.setOverrideSizeHint(QSize(500, 0)); + _zoomPercentageAction.setOverrideSizeHint(QSize(300, 0)); _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 71c3e0fc2..40f482383 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -8,8 +8,6 @@ //#define DENSITY_RENDERER_VERBOSE #endif -#define DENSITY_RENDERER_VERBOSE - namespace mv::gui { @@ -40,8 +38,6 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); updateQuad(); - - getNavigator().setZoomRectangleWorld(dataBounds); } void DensityRenderer::setRenderMode(RenderMode renderMode) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 1b93d2ab1..b6d26169e 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -81,6 +81,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) }); connect(&_navigationAction.getZoomPercentageAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + _userHasNavigated = true; setZoomPercentage(value); }); @@ -259,16 +260,24 @@ float Navigator2D::getZoomPercentage() const void Navigator2D::setZoomPercentage(float zoomPercentage) { - if (zoomPercentage < 0.05f) - return; + beginZooming(); + { + beginChangeZoomRectangleWorld(); + { + if (zoomPercentage < 0.05f) + return; - const auto zoomPercentageNormalized = .01f * zoomPercentage; - const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); - const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); + const auto zoomPercentageNormalized = .01f * zoomPercentage; + const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; + _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; - setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomRectangleWorld(getZoomRectangleWorld()); + } + endChangeZoomRectangleWorld(); + } + endZooming(); } bool Navigator2D::isEnabled() const diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 8adda12e9..5994c734c 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -171,7 +171,7 @@ QMatrix4x4 Renderer2D::getProjectionMatrix() const QMatrix4x4 matrix; // Create an orthogonal transformation matrix - matrix.ortho(-halfSize.width(), halfSize.width(), halfSize.height(), -halfSize.height(), -1000.0f, +1000.0f); + matrix.ortho(-halfSize.width(), halfSize.width(), -halfSize.height(), halfSize.height(), -1000.0f, +1000.0f); return matrix; } From 897c3b4ef54136e6557e822366279916f7a0e2ff Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 1 Apr 2025 16:27:15 +0200 Subject: [PATCH 75/89] Fix zoom percentage --- ManiVault/src/renderers/Navigator2D.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index b6d26169e..0837b78a1 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -69,15 +69,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); - - //const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); - //const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - - //qDebug() << "-----" << _zoomFactor << 0.01f * getZoomPercentage() << zoomFactorY / (0.01f * getZoomPercentage()); }); connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { - zoomAround(_sourceWidget->rect().center(), 1.1f); + setZoomPercentage(getZoomPercentage() + 10.f); }); connect(&_navigationAction.getZoomPercentageAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { @@ -86,7 +81,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) }); connect(&_navigationAction.getZoomOutAction(), &TriggerAction::triggered, this, [this]() -> void { - zoomAround(_sourceWidget->rect().center(), .9f); + setZoomPercentage(getZoomPercentage() - 10.f); }); connect(&_renderer, &Renderer2D::worldBoundsChanged, this, [this](const QRectF& worldBounds) -> void { From bd377219aeb6e0879cba578f87f940ebdd2eeb9b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 2 Apr 2025 08:36:22 +0200 Subject: [PATCH 76/89] Fix shortcuts --- ManiVault/src/actions/NavigationAction.cpp | 13 ++++++++---- ManiVault/src/actions/NavigationAction.h | 6 ++++++ ManiVault/src/renderers/Navigator2D.cpp | 23 ++++++++++++++++------ ManiVault/src/renderers/Navigator2D.h | 2 -- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 0a092bce3..d08bcc13e 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -40,10 +40,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction.setIconByName("compress"); _zoomSelectionAction.setIconByName("search-location"); - _zoomOutAction.setShortcut(QKeySequence("-")); - _zoomInAction.setShortcut(QKeySequence("+")); - _zoomExtentsAction.setShortcut(QKeySequence("z")); - _zoomSelectionAction.setShortcut(QKeySequence("d")); + _zoomSelectionAction.setEnabled(false); @@ -62,6 +59,14 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : //addAction(&_zoomRegionAction); } +void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) +{ + _zoomOutAction.setShortcut(shortcutsEnabled ? QKeySequence("-") : QKeySequence()); + _zoomInAction.setShortcut(shortcutsEnabled ? QKeySequence("+") : QKeySequence()); + _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("z") : QKeySequence()); + //_zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("d") : QKeySequence()); +} + void NavigationAction::fromVariantMap(const QVariantMap& variantMap) { HorizontalGroupAction::fromVariantMap(variantMap); diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index 7450269a2..8691ddc43 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -38,6 +38,12 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction */ Q_INVOKABLE NavigationAction(QObject* parent, const QString& title); + /** + * Set whether shortcuts are enabled + * @param shortcutsEnabled Boolean determining whether shortcuts are enabled + */ + void setShortcutsEnabled(bool shortcutsEnabled); + public: // Serialization /** diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 0837b78a1..ff3d14eb7 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -61,10 +61,7 @@ void Navigator2D::initialize(QWidget* sourceWidget) { _navigationAction.getZoomExtentsAction().setEnabled(hasUserNavigated()); - _navigationAction.getZoomRectangleAction().setLeft(static_cast(currentZoomRectangleWorld.left())); - _navigationAction.getZoomRectangleAction().setRight(static_cast(currentZoomRectangleWorld.right())); - _navigationAction.getZoomRectangleAction().setTop(static_cast(currentZoomRectangleWorld.bottom())); - _navigationAction.getZoomRectangleAction().setBottom(static_cast(currentZoomRectangleWorld.top())); + _navigationAction.getZoomRectangleAction().setRectangle(currentZoomRectangleWorld.left(), currentZoomRectangleWorld.right(), currentZoomRectangleWorld.bottom(), currentZoomRectangleWorld.top()); } connect(&_navigationAction.getZoomRectangleAction(), &DecimalRectangleAction::rectangleChanged, this, zoomRectangleChanged); @@ -89,6 +86,10 @@ void Navigator2D::initialize(QWidget* sourceWidget) resetView(); }); + _sourceWidget->addAction(&_navigationAction.getZoomInAction()); + _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); + _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); + _initialized = true; } @@ -222,8 +223,15 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) const auto previousZoomRectangleWorld = getZoomRectangleWorld(); - //_zoomFactor = _renderer.getRenderSize().width() / zoomRectangleWorld.width(); - //_zoomCenterWorld = zoomRectangleWorld.center(); + if (zoomRectangleWorld == previousZoomRectangleWorld) + return; + + _zoomFactor = std::max( + static_cast(zoomRectangleWorld.width()) / static_cast(_renderer.getRenderSize().width()), + static_cast(zoomRectangleWorld.height()) / static_cast(_renderer.getRenderSize().height()) + ); + + _zoomCenterWorld = zoomRectangleWorld.center(); emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } @@ -287,6 +295,8 @@ void Navigator2D::setEnabled(bool enabled) _enabled = enabled; + _navigationAction.setShortcutsEnabled(_enabled); + emit enabledChanged(_enabled); } @@ -337,6 +347,7 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) { beginChangeZoomRectangleWorld(); { + setZoomRectangleWorld(zoomRectangle); } endChangeZoomRectangleWorld(); } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 5e744c02a..e980acdcb 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -284,8 +284,6 @@ class CORE_EXPORT Navigator2D : public QObject QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ gui::NavigationAction _navigationAction; /** Navigation group action */ - - static constexpr int maxZoomHistorySize = 10; /** Maximum history size */ }; } From 447bd4b1e4f545618c45860c1e649ad9113132e9 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 2 Apr 2025 09:15:01 +0200 Subject: [PATCH 77/89] All shortcuts are working and the shortcuts dialog has also been updated --- ManiVault/src/actions/NavigationAction.cpp | 5 +- .../src/actions/PixelSelectionAction.cpp | 63 +++++++------------ 2 files changed, 24 insertions(+), 44 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index d08bcc13e..bb8d29f0e 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -39,9 +39,6 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomInAction.setIconByName("search-plus"); _zoomExtentsAction.setIconByName("compress"); _zoomSelectionAction.setIconByName("search-location"); - - - _zoomSelectionAction.setEnabled(false); _zoomPercentageAction.setSuffix("%"); @@ -63,7 +60,7 @@ void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) { _zoomOutAction.setShortcut(shortcutsEnabled ? QKeySequence("-") : QKeySequence()); _zoomInAction.setShortcut(shortcutsEnabled ? QKeySequence("+") : QKeySequence()); - _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("z") : QKeySequence()); + _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("O") : QKeySequence()); //_zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("d") : QKeySequence()); } diff --git a/ManiVault/src/actions/PixelSelectionAction.cpp b/ManiVault/src/actions/PixelSelectionAction.cpp index 2c6c5441f..024cc8e73 100644 --- a/ManiVault/src/actions/PixelSelectionAction.cpp +++ b/ManiVault/src/actions/PixelSelectionAction.cpp @@ -61,32 +61,26 @@ PixelSelectionAction::PixelSelectionAction(QObject* parent, const QString& title _rectangleAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Rectangle)); _rectangleAction.setToolTip("Select pixels inside a rectangle (R)"); _rectangleAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _rectangleAction.setShortcut(QKeySequence("R")); _brushAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Brush)); _brushAction.setToolTip("Select pixels using a brush tool (B)"); _brushAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _brushAction.setShortcut(QKeySequence("B")); _lassoAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Lasso)); _lassoAction.setToolTip("Select pixels using a lasso (L)"); _lassoAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _lassoAction.setShortcut(QKeySequence("L")); _polygonAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Polygon)); _polygonAction.setToolTip("Select pixels by drawing a polygon (P)"); _polygonAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _polygonAction.setShortcut(QKeySequence("P")); _sampleAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::Sample)); _sampleAction.setToolTip("Sample pixel by dragging over the image (S)"); _sampleAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _sampleAction.setShortcut(QKeySequence("S")); _roiAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); _roiAction.setIcon(getPixelSelectionTypeIcon(PixelSelectionType::ROI)); _roiAction.setToolTip("Sample within region of interest (I)"); - _roiAction.setShortcut(QKeySequence("I")); _modifierAction.setToolTip("Type of selection modifier"); _modifierAction.setCurrentIndex(static_cast(PixelSelectionModifierType::Replace)); @@ -113,22 +107,20 @@ PixelSelectionAction::PixelSelectionAction(QObject* parent, const QString& title _clearSelectionAction.setToolTip("Clears the selection (E)"); _clearSelectionAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _clearSelectionAction.setShortcut(QKeySequence("E")); - _selectAllAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _selectAllAction.setShortcut(QKeySequence("A")); _selectAllAction.setToolTip("Select all data points (A)"); - + _invertSelectionAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _invertSelectionAction.setShortcut(QKeySequence("I")); _invertSelectionAction.setToolTip("Invert the selection (I)"); + _invertSelectionAction.setToolTip("Invert the selection (I)"); + _notifyDuringSelectionAction.setShortcut(QKeySequence("U")); + _brushRadiusAction.setToolTip("Brush selection tool radius"); _brushRadiusAction.setSuffix("px"); _notifyDuringSelectionAction.setDefaultWidgetFlags(ToggleAction::CheckBox); _notifyDuringSelectionAction.setShortcutContext(Qt::WidgetWithChildrenShortcut); - _notifyDuringSelectionAction.setShortcut(QKeySequence("U")); _notifyDuringSelectionAction.setToolTip("Notify during selection or only at the end of the selection process (U)"); const auto updatePixelSelectionTypesModel = [this]() { @@ -207,6 +199,16 @@ void PixelSelectionAction::initialize(QWidget* targetWidget, util::PixelSelectio setShortcutsEnabled(true); + _targetWidget->addAction(&_rectangleAction); + _targetWidget->addAction(&_brushAction); + _targetWidget->addAction(&_lassoAction); + _targetWidget->addAction(&_polygonAction); + _targetWidget->addAction(&_sampleAction); + _targetWidget->addAction(&_roiAction); + _targetWidget->addAction(&_selectAllAction); + _targetWidget->addAction(&_clearSelectionAction); + _targetWidget->addAction(&_invertSelectionAction); + _targetWidget->installEventFilter(this); _initialized = true; } @@ -226,34 +228,15 @@ void PixelSelectionAction::setShortcutsEnabled(const bool& shortcutsEnabled) if (!isInitialized()) return; - if (shortcutsEnabled) { - _targetWidget->addAction(&_rectangleAction); - _targetWidget->addAction(&_brushAction); - _targetWidget->addAction(&_lassoAction); - _targetWidget->addAction(&_polygonAction); - _targetWidget->addAction(&_sampleAction); - _targetWidget->addAction(&_modifierAddAction); - _targetWidget->addAction(&_modifierSubtractAction); - _targetWidget->addAction(&_clearSelectionAction); - _targetWidget->addAction(&_selectAllAction); - _targetWidget->addAction(&_invertSelectionAction); - _targetWidget->addAction(&_brushRadiusAction); - _targetWidget->addAction(&_notifyDuringSelectionAction); - } - else { - _targetWidget->removeAction(&_rectangleAction); - _targetWidget->removeAction(&_brushAction); - _targetWidget->removeAction(&_lassoAction); - _targetWidget->removeAction(&_polygonAction); - _targetWidget->removeAction(&_sampleAction); - _targetWidget->removeAction(&_modifierAddAction); - _targetWidget->removeAction(&_modifierSubtractAction); - _targetWidget->removeAction(&_clearSelectionAction); - _targetWidget->removeAction(&_selectAllAction); - _targetWidget->removeAction(&_invertSelectionAction); - _targetWidget->removeAction(&_brushRadiusAction); - _targetWidget->removeAction(&_notifyDuringSelectionAction); - } + _rectangleAction.setShortcut(shortcutsEnabled ? QKeySequence("R") : QKeySequence()); + _brushAction.setShortcut(shortcutsEnabled ? QKeySequence("B") : QKeySequence()); + _lassoAction.setShortcut(shortcutsEnabled ? QKeySequence("L") : QKeySequence()); + _polygonAction.setShortcut(shortcutsEnabled ? QKeySequence("P") : QKeySequence()); + _sampleAction.setShortcut(shortcutsEnabled ? QKeySequence("U") : QKeySequence()); + _roiAction.setShortcut(shortcutsEnabled ? QKeySequence("W") : QKeySequence()); + _selectAllAction.setShortcut(shortcutsEnabled ? QKeySequence("A") : QKeySequence()); + _clearSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("E") : QKeySequence()); + _invertSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("I") : QKeySequence()); } void PixelSelectionAction::initType() From 080db935a0eea39b1f4b7af79be2d58b1b3ffeac Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 2 Apr 2025 14:13:21 +0200 Subject: [PATCH 78/89] Added numerical, decimal and integral point action --- ManiVault/cmake/CMakeMvSourcesPublic.cmake | 32 ++- ManiVault/src/actions/DecimalPointAction.cpp | 63 ++++++ ManiVault/src/actions/DecimalPointAction.h | 66 ++++++ ManiVault/src/actions/IntegralPointAction.cpp | 61 ++++++ ManiVault/src/actions/IntegralPointAction.h | 62 ++++++ ManiVault/src/actions/NavigationAction.cpp | 35 ++- ManiVault/src/actions/NavigationAction.h | 5 + ManiVault/src/actions/NumericalAction.h | 51 ++--- .../src/actions/NumericalPointAction.cpp | 9 + ManiVault/src/actions/NumericalPointAction.h | 201 ++++++++++++++++++ ManiVault/src/renderers/DensityRenderer.cpp | 6 +- ManiVault/src/renderers/Navigator2D.cpp | 84 +++++--- ManiVault/src/renderers/Navigator2D.h | 20 ++ ManiVault/src/renderers/Renderer.h | 13 +- ManiVault/src/renderers/Renderer2D.h | 5 +- 15 files changed, 626 insertions(+), 87 deletions(-) create mode 100644 ManiVault/src/actions/DecimalPointAction.cpp create mode 100644 ManiVault/src/actions/DecimalPointAction.h create mode 100644 ManiVault/src/actions/IntegralPointAction.cpp create mode 100644 ManiVault/src/actions/IntegralPointAction.h create mode 100644 ManiVault/src/actions/NumericalPointAction.cpp create mode 100644 ManiVault/src/actions/NumericalPointAction.h diff --git a/ManiVault/cmake/CMakeMvSourcesPublic.cmake b/ManiVault/cmake/CMakeMvSourcesPublic.cmake index f65681709..5b19041f6 100644 --- a/ManiVault/cmake/CMakeMvSourcesPublic.cmake +++ b/ManiVault/cmake/CMakeMvSourcesPublic.cmake @@ -93,6 +93,12 @@ set(PUBLIC_NUMERICAL_ACTIONS_HEADERS src/actions/NumericalRangeAction.h src/actions/DecimalRangeAction.h src/actions/IntegralRangeAction.h + src/actions/NumericalPointAction.h + src/actions/DecimalPointAction.h + src/actions/IntegralPointAction.h + src/actions/RectangleAction.h + src/actions/IntegralRectangleAction.h + src/actions/DecimalRectangleAction.h ) set(PUBLIC_NUMERICAL_ACTIONS_SOURCES @@ -102,6 +108,12 @@ set(PUBLIC_NUMERICAL_ACTIONS_SOURCES src/actions/NumericalRangeAction.cpp src/actions/DecimalRangeAction.cpp src/actions/IntegralRangeAction.cpp + src/actions/NumericalPointAction.cpp + src/actions/DecimalPointAction.cpp + src/actions/IntegralPointAction.cpp + src/actions/RectangleAction.cpp + src/actions/IntegralRectangleAction.cpp + src/actions/DecimalRectangleAction.cpp ) set(PUBLIC_NUMERICAL_ACTIONS_FILES @@ -109,23 +121,6 @@ set(PUBLIC_NUMERICAL_ACTIONS_FILES ${PUBLIC_NUMERICAL_ACTIONS_SOURCES} ) -set(PUBLIC_RECTANGLE_ACTIONS_HEADERS - src/actions/RectangleAction.h - src/actions/IntegralRectangleAction.h - src/actions/DecimalRectangleAction.h -) - -set(PUBLIC_RECTANGLE_ACTIONS_SOURCES - src/actions/RectangleAction.cpp - src/actions/IntegralRectangleAction.cpp - src/actions/DecimalRectangleAction.cpp -) - -set(PUBLIC_RECTANGLE_ACTIONS_FILES - ${PUBLIC_RECTANGLE_ACTIONS_HEADERS} - ${PUBLIC_RECTANGLE_ACTIONS_SOURCES} -) - set(PUBLIC_TEXTUAL_ACTIONS_HEADERS src/actions/StringAction.h src/actions/StringsAction.h @@ -1073,7 +1068,6 @@ set(PUBLIC_HEADERS ${PUBLIC_EVENT_HEADERS} ${PUBLIC_COLOR_MAP_ACTION_HEADERS} ${PUBLIC_NUMERICAL_ACTIONS_HEADERS} - ${PUBLIC_RECTANGLE_ACTIONS_HEADERS} ${PUBLIC_TEXTUAL_ACTIONS_HEADERS} ${PUBLIC_GROUPING_ACTIONS_HEADERS} ${PUBLIC_TRIGGER_ACTIONS_HEADERS} @@ -1122,7 +1116,6 @@ set(PUBLIC_SOURCES ${PUBLIC_EVENT_SOURCES} ${PUBLIC_COLOR_MAP_ACTION_SOURCES} ${PUBLIC_NUMERICAL_ACTIONS_SOURCES} - ${PUBLIC_RECTANGLE_ACTIONS_SOURCES} ${PUBLIC_TEXTUAL_ACTIONS_SOURCES} ${PUBLIC_GROUPING_ACTIONS_SOURCES} ${PUBLIC_TRIGGER_ACTIONS_SOURCES} @@ -1184,7 +1177,6 @@ source_group(CoreInterface FILES ${PUBLIC_CORE_INTERFACE_FILES}) source_group(Event FILES ${PUBLIC_EVENT_FILES}) source_group(Actions\\Colormap FILES ${PUBLIC_COLOR_MAP_ACTION_FILES}) source_group(Actions\\Numerical FILES ${PUBLIC_NUMERICAL_ACTIONS_FILES}) -source_group(Actions\\Rectangle FILES ${PUBLIC_RECTANGLE_ACTIONS_FILES}) source_group(Actions\\Textual FILES ${PUBLIC_TEXTUAL_ACTIONS_FILES}) source_group(Actions\\Grouping FILES ${PUBLIC_GROUPING_ACTIONS_FILES}) source_group(Actions\\Trigger FILES ${PUBLIC_TRIGGER_ACTIONS_FILES}) diff --git a/ManiVault/src/actions/DecimalPointAction.cpp b/ManiVault/src/actions/DecimalPointAction.cpp new file mode 100644 index 000000000..f89e90b73 --- /dev/null +++ b/ManiVault/src/actions/DecimalPointAction.cpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "DecimalPointAction.h" + +using namespace mv::util; + +namespace mv::gui { + +DecimalPointAction::DecimalPointAction(QObject* parent, const QString& title) : + NumericalPointAction(parent, title, std::numeric_limits::lowest(), std::numeric_limits::max()) +{ + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) { + getAction(static_cast(axisIndex)).setNumberOfDecimals(2); + + connect(&getAction(static_cast(axisIndex)), &DecimalAction::valueChanged, this, [this](float value) -> void { + emit valueChanged(getX(), getY()); + }); + } +} + +void DecimalPointAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) +{ + auto publicDecimalPointAction = dynamic_cast(publicAction); + + Q_ASSERT(publicDecimalPointAction); + + if (!publicDecimalPointAction) + return; + + connect(this, &DecimalPointAction::valueChanged, publicDecimalPointAction, [publicDecimalPointAction](float x, float y) -> void { + publicDecimalPointAction->set(x, y); + }); + + connect(publicDecimalPointAction, &DecimalPointAction::valueChanged, this, [this](float x, float y) -> void { + set(x, y); + }); + + set(publicDecimalPointAction->get()); + + NumericalPointAction::connectToPublicAction(publicAction, recursive); +} + +void DecimalPointAction::disconnectFromPublicAction(bool recursive) +{ + if (!isConnected()) + return; + + auto publicDecimalPointAction = dynamic_cast(getPublicAction()); + + Q_ASSERT(publicDecimalPointAction); + + if (!publicDecimalPointAction) + return; + + disconnect(this, &DecimalPointAction::valueChanged, publicDecimalPointAction, nullptr); + disconnect(publicDecimalPointAction, &DecimalPointAction::valueChanged, this, nullptr); + + NumericalPointAction::disconnectFromPublicAction(recursive); +} + +} diff --git a/ManiVault/src/actions/DecimalPointAction.h b/ManiVault/src/actions/DecimalPointAction.h new file mode 100644 index 000000000..0a41ea9dd --- /dev/null +++ b/ManiVault/src/actions/DecimalPointAction.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include "NumericalPointAction.h" + +namespace mv::gui { + +/** + * Decimal action class + * + * Stores a two-dimensional decimal point value. + * + * @author Thomas Kroes + */ +class CORE_EXPORT DecimalPointAction : public NumericalPointAction +{ + Q_OBJECT + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + */ + Q_INVOKABLE explicit DecimalPointAction(QObject* parent, const QString& title); + +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; + +signals: + + /** + * Signals that the coordinate values changed to \p x and \p y + * @param x X coordinate value + * @param y Y coordinate value + */ + void valueChanged(float x, float y); + +protected: + static constexpr float INIT_VALUE = 0.0; /** Initialization value */ + static constexpr int INIT_DECIMALS = 1; /** Initialization number of decimals */ + + friend class AbstractActionsManager; +}; + +} + +Q_DECLARE_METATYPE(mv::gui::DecimalPointAction) + +inline const auto decimalPointActionMetaTypeId = qRegisterMetaType("mv::gui::DecimalPointAction"); diff --git a/ManiVault/src/actions/IntegralPointAction.cpp b/ManiVault/src/actions/IntegralPointAction.cpp new file mode 100644 index 000000000..f7ed69040 --- /dev/null +++ b/ManiVault/src/actions/IntegralPointAction.cpp @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "IntegralPointAction.h" + +using namespace mv::util; + +namespace mv::gui { + +IntegralPointAction::IntegralPointAction(QObject* parent, const QString& title) : + NumericalPointAction(parent, title, std::numeric_limits::lowest(), std::numeric_limits::max()) +{ + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) { + connect(&getAction(static_cast(axisIndex)), &IntegralAction::valueChanged, this, [this](std::int32_t value) -> void { + emit valueChanged(getX(), getY()); + }); + } +} + +void IntegralPointAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) +{ + auto publicIntegralPointAction = dynamic_cast(publicAction); + + Q_ASSERT(publicIntegralPointAction); + + if (!publicIntegralPointAction) + return; + + connect(this, &IntegralPointAction::valueChanged, publicIntegralPointAction, [publicIntegralPointAction](std::int32_t x, std::int32_t y) -> void { + publicIntegralPointAction->set(x, y); + }); + + connect(publicIntegralPointAction, &IntegralPointAction::valueChanged, this, [this](std::int32_t x, std::int32_t y) -> void { + set(x, y); + }); + + set(publicIntegralPointAction->get()); + + NumericalPointAction::connectToPublicAction(publicAction, recursive); +} + +void IntegralPointAction::disconnectFromPublicAction(bool recursive) +{ + if (!isConnected()) + return; + + auto publicIntegralPointAction = dynamic_cast(getPublicAction()); + + Q_ASSERT(publicIntegralPointAction); + + if (!publicIntegralPointAction) + return; + + disconnect(this, &IntegralPointAction::valueChanged, publicIntegralPointAction, nullptr); + disconnect(publicIntegralPointAction, &IntegralPointAction::valueChanged, this, nullptr); + + NumericalPointAction::disconnectFromPublicAction(recursive); +} + +} diff --git a/ManiVault/src/actions/IntegralPointAction.h b/ManiVault/src/actions/IntegralPointAction.h new file mode 100644 index 000000000..b96992eae --- /dev/null +++ b/ManiVault/src/actions/IntegralPointAction.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include "NumericalPointAction.h" + +namespace mv::gui { + +/** + * Integral action class + * + * Stores a two-dimensional integral point value. + * + * @author Thomas Kroes + */ +class CORE_EXPORT IntegralPointAction : public NumericalPointAction +{ + Q_OBJECT + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + */ + Q_INVOKABLE explicit IntegralPointAction(QObject* parent, const QString& title); + +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; + +signals: + + /** + * Signals that the coordinate values changed to \p x and \p y + * @param x X coordinate value + * @param y Y coordinate value + */ + void valueChanged(std::int32_t x, std::int32_t y); + + friend class AbstractActionsManager; +}; + +} + +Q_DECLARE_METATYPE(mv::gui::IntegralPointAction) + +inline const auto integralPointActionMetaTypeId = qRegisterMetaType("mv::gui::IntegralPointAction"); diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index bb8d29f0e..f69f921c9 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -4,6 +4,8 @@ #include "NavigationAction.h" +#include "GroupSectionTreeItem.h" + #ifdef _DEBUG //#define NAVIGATION_ACTION_VERBOSE #endif @@ -23,7 +25,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction(this, "Zoom All"), _zoomSelectionAction(this, "Zoom Selection"), _zoomRegionAction(this, "Zoom Region"), - _zoomRectangleAction(this, "Zoom Rectangle") + _zoomRectangleAction(this, "Zoom Rectangle"), + _zoomCenterAction(this, "Zoom Center"), + _zoomFactorAction(this, "Zoom Factor") { setShowLabels(false); @@ -48,12 +52,33 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomRectangleAction.setIcon(combineIcons(StyledIcon("expand"), StyledIcon("ellipsis-h"))); _zoomRectangleAction.setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup); + _zoomCenterAction.setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup); + _zoomCenterAction.setIconByName("ruler"); + _zoomCenterAction.setDefaultWidgetFlags(GroupAction::WidgetFlag::Vertical); + _zoomCenterAction.setPopupSizeHint(QSize(250, 0)); + + _zoomCenterAction.getXAction().setDefaultWidgetFlags(DecimalAction::WidgetFlag::SpinBox); + _zoomCenterAction.getYAction().setDefaultWidgetFlags(DecimalAction::WidgetFlag::SpinBox); + addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); addAction(&_zoomExtentsAction); - //addAction(&_zoomSelectionAction); + addAction(&_zoomCenterAction); + //addAction(&_zoomRegionAction); + + // connect(&_zoomCenterXAction, &DecimalAction::valueChanged, this, [this](float value) -> void { + // qDebug() << "Zoom center x: " << value; + //}); + + // connect(&_zoomCenterYAction, &DecimalAction::valueChanged, this, [this](float value) -> void { + // qDebug() << "Zoom center y: " << value; + //}); + + connect(&_zoomFactorAction, &DecimalAction::valueChanged, this, [this](float value) -> void { + qDebug() << "Zoom factor: " << value; + }); } void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) @@ -68,14 +93,16 @@ void NavigationAction::fromVariantMap(const QVariantMap& variantMap) { HorizontalGroupAction::fromVariantMap(variantMap); - _zoomRectangleAction.fromParentVariantMap(variantMap); + _zoomCenterAction.fromParentVariantMap(variantMap); + _zoomFactorAction.fromParentVariantMap(variantMap); } QVariantMap NavigationAction::toVariantMap() const { auto variantMap = HorizontalGroupAction::toVariantMap(); - _zoomRectangleAction.insertIntoVariantMap(variantMap); + _zoomCenterAction.insertIntoVariantMap(variantMap); + _zoomFactorAction.insertIntoVariantMap(variantMap); return variantMap; } diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index 8691ddc43..d7164d57c 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -68,6 +69,8 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } DecimalRectangleAction& getZoomRectangleAction() { return _zoomRectangleAction; } + DecimalPointAction& getZoomCenterAction() { return _zoomCenterAction; } + DecimalAction& getZoomFactorAction() { return _zoomFactorAction; } private: TriggerAction _zoomOutAction; /** Zoom out action */ @@ -77,6 +80,8 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ TriggerAction _zoomRegionAction; /** Zoom to region action */ DecimalRectangleAction _zoomRectangleAction; /** Rectangle action for setting the current zoom bounds */ + DecimalPointAction _zoomCenterAction; /** Zoom center action */ + DecimalAction _zoomFactorAction; /** Zoom factor action */ }; } diff --git a/ManiVault/src/actions/NumericalAction.h b/ManiVault/src/actions/NumericalAction.h index 126ac0c65..ad9202dfb 100644 --- a/ManiVault/src/actions/NumericalAction.h +++ b/ManiVault/src/actions/NumericalAction.h @@ -59,23 +59,15 @@ class NumericalAction : public WidgetAction _value(), _minimum(std::numeric_limits::lowest()), _maximum(std::numeric_limits::max()), - _prefix(), - _suffix(), _numberOfDecimals(), - _updateDuringDrag(true), - _valueChanged(), - _minimumChanged(), - _maximumChanged(), - _prefixChanged(), - _suffixChanged(), - _numberOfDecimalsChanged() + _updateDuringDrag(true) { setText(title); setDefaultWidgetFlags(WidgetFlag::Default); } /** Gets the current value */ - virtual NumericalType getValue() const final { + virtual NumericalType getValue() const { return _value; } @@ -101,7 +93,7 @@ class NumericalAction : public WidgetAction } /** Gets the minimum value */ - virtual NumericalType getMinimum() const final { + virtual NumericalType getMinimum() const { return _minimum; } @@ -109,7 +101,7 @@ class NumericalAction : public WidgetAction * Sets the minimum value * @param minimum Minimum value */ - virtual void setMinimum(NumericalType minimum) final { + virtual void setMinimum(NumericalType minimum) { if (minimum == _minimum) return; @@ -119,7 +111,7 @@ class NumericalAction : public WidgetAction } /** Gets the maximum value */ - virtual NumericalType getMaximum() const final { + virtual NumericalType getMaximum() const { return _maximum; } @@ -127,7 +119,7 @@ class NumericalAction : public WidgetAction * Sets the maximum value * @param maximum Maximum value */ - virtual void setMaximum(NumericalType maximum) final { + virtual void setMaximum(NumericalType maximum) { if (maximum == _maximum) return; @@ -140,7 +132,7 @@ class NumericalAction : public WidgetAction * Gets the value range * @return Range */ - virtual util::NumericalRange getRange() const final { + virtual util::NumericalRange getRange() const { return { getMinimum(), getMaximum() }; } @@ -148,7 +140,7 @@ class NumericalAction : public WidgetAction * Sets the value range * @param range Range */ - virtual void setRange(util::NumericalRange range) final { + virtual void setRange(util::NumericalRange range) { setMinimum(range.getMinimum()); setMaximum(range.getMaximum()); } @@ -158,13 +150,13 @@ class NumericalAction : public WidgetAction * @param minimum Minimum value * @param maximum Maximum value */ - virtual void setRange(NumericalType minimum, NumericalType maximum) final { + virtual void setRange(NumericalType minimum, NumericalType maximum) { setMinimum(minimum); setMaximum(maximum); } /** Gets the prefix */ - virtual QString getPrefix() const final { + virtual QString getPrefix() const { return _prefix; } @@ -172,7 +164,7 @@ class NumericalAction : public WidgetAction * Sets the prefix * @param prefix Prefix */ - virtual void setPrefix(const QString& prefix) final { + virtual void setPrefix(const QString& prefix) { if (prefix == _prefix) return; @@ -182,7 +174,7 @@ class NumericalAction : public WidgetAction } /** Gets the suffix */ - virtual QString getSuffix() const final { + virtual QString getSuffix() const { return _suffix; } @@ -190,7 +182,7 @@ class NumericalAction : public WidgetAction * Sets the suffix * @param suffix Suffix */ - virtual void setSuffix(const QString& suffix) final { + virtual void setSuffix(const QString& suffix) { if (suffix == _suffix) return; @@ -200,7 +192,7 @@ class NumericalAction : public WidgetAction } /** Gets the number of decimals */ - virtual std::uint32_t getNumberOfDecimals() const final { + virtual std::uint32_t getNumberOfDecimals() const { return _numberOfDecimals; } @@ -208,7 +200,7 @@ class NumericalAction : public WidgetAction * Sets the number of decimals * @param numberOfDecimals number of decimals */ - virtual void setNumberOfDecimals(std::uint32_t numberOfDecimals) final { + virtual void setNumberOfDecimals(std::uint32_t numberOfDecimals) { if (numberOfDecimals == _numberOfDecimals) return; @@ -218,7 +210,7 @@ class NumericalAction : public WidgetAction } /** Gets whether the value should update during interaction */ - virtual bool getUpdateDuringDrag() const final { + virtual bool getUpdateDuringDrag() const { return _updateDuringDrag; } @@ -226,7 +218,7 @@ class NumericalAction : public WidgetAction * Sets whether the value should update during interaction * @param updateDuringDrag Whether the value should update during interaction */ - virtual void setUpdateDuringDrag(bool updateDuringDrag) final { + virtual void setUpdateDuringDrag(bool updateDuringDrag) { if (updateDuringDrag == _updateDuringDrag) return; @@ -234,23 +226,24 @@ class NumericalAction : public WidgetAction } /** Returns whether the current value is at its minimum */ - virtual bool isAtMinimum() const final { + virtual bool isAtMinimum() const { return _value == _minimum; } /** Returns whether the current value is at its maximum */ - virtual bool isAtMaximum() const final { + virtual bool isAtMaximum() const { return _value == _maximum; } /** Returns the length of the interval defined by the minimum and maximum value */ - virtual double getIntervalLength() const final { + virtual double getIntervalLength() const { return static_cast(_maximum) - static_cast(_minimum); } /** Returns the normalized value */ - virtual double getNormalized() const final { + virtual double getNormalized() const { const auto offset = static_cast(_value) - static_cast(_minimum); + return static_cast(offset / getIntervalLength()); } diff --git a/ManiVault/src/actions/NumericalPointAction.cpp b/ManiVault/src/actions/NumericalPointAction.cpp new file mode 100644 index 000000000..d3bca223e --- /dev/null +++ b/ManiVault/src/actions/NumericalPointAction.cpp @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#include "NumericalPointAction.h" + +namespace mv::gui { + +} \ No newline at end of file diff --git a/ManiVault/src/actions/NumericalPointAction.h b/ManiVault/src/actions/NumericalPointAction.h new file mode 100644 index 000000000..4626af3b6 --- /dev/null +++ b/ManiVault/src/actions/NumericalPointAction.h @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// A corresponding LICENSE file is located in the root directory of this source tree +// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft) + +#pragma once + +#include "GroupAction.h" + +#include +#include + +namespace mv::gui { + +/** + * Numerical point action base class + * + * Stores a two-dimensional point value. + * + * @author Thomas Kroes + */ +template +class NumericalPointAction : public GroupAction +{ +public: + + /** Axis enum */ + enum class Axis { + X = 0, /** X axis */ + Y, /** Y axis */ + + Count + }; + +public: + + /** + * Construct with pointer to \p parent object and \p title + * @param parent Pointer to parent object + * @param title Title of the action + * @param minimum Minimum value + * @param maximum Maximum value + */ + NumericalPointAction(QObject* parent, const QString& title, NumericalType minimum, NumericalType maximum) : + GroupAction(parent, title), + _elementActions{ + NumericalActionType(this, "X", minimum, maximum), + NumericalActionType(this, "Y", minimum, maximum) + } + { + setText(title); + setDefaultWidgetFlags(WidgetFlag::Default); + + addAction(&getXAction()); + addAction(&getYAction()); + } + + /** + * Retrieves the X coordinate value + * @return The X coordinate + */ + NumericalType getX() const { return getXAction().getValue(); } + + /** + * Set the X coordinate value + * @param x X coordinate value + */ + void setX(const NumericalType& x) { + getXAction().setValue(x); + } + + /** + * Set the Y coordinate value + * @param y Y coordinate value + */ + void setY(const NumericalType& y) { + getYAction().setValue(y); + } + + /** + * Retrieves the Y coordinate value + * @return The Y coordinate + */ + NumericalType getY() const { return getYAction().getValue(); } + + /** + * Set the X and Y coordinate values + * @param x X coordinate value + * @param y Y coordinate value + */ + void set(const NumericalType& x, const NumericalType& y) { + setX(x); + setY(y); + } + + /** + * Retrieves the X and Y coordinate values as a QPoint + * @return QPoint value + */ + QPoint getPoint() const { + return QPoint(static_cast(getX()), static_cast(getY())); + } + + /** + * Set the X and Y coordinate values from a QPoint + * @param point QPoint value + */ + void set(const QPoint& point) { + set(static_cast(point.x()), static_cast(point.y())); + } + + /** + * Retrieves the X and Y coordinate values as a QPointF + * @return QPointF value + */ + QPointF getPointF() const { + return QPointF(getX(), getY()); + } + + /** + * Set the X and Y coordinate values from a QPointF + * @param point QPointF value + */ + void set(const QPointF& point) { + set(static_cast(point.x()), static_cast(point.y())); + } + + /** + * Retrieves the X and Y coordinate values as a std::pair + * @return Pair of X and Y coordinate values + */ + std::pair get() const { + return std::make_pair(getX(), getY()); + } + + /** + * Set the X and Y coordinate values from a std::pair + * @param pair Pair of X and Y coordinate values + */ + void set(const std::pair& pair) { + set(pair.first, pair.second); + } + + /** + * Retrieves the X and Y coordinate values as a QVector2D + * @return QVector2D value + */ + QVector2D getVector() const { + return QVector2D(getX(), getY()); + } + + /** + * Set the X and Y coordinate values from a Qt vector + * @param vector QVector2D value + */ + void set(const QVector2D& vector) { + set(static_cast(vector.x()), static_cast(vector.y())); + } + +public: // Serialization + + /** + * Load numerical point action from variant map + * @param variantMap Variant map representation of the numerical point action + */ + void fromVariantMap(const QVariantMap& variantMap) override + { + WidgetAction::fromVariantMap(variantMap); + + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) + _elementActions[axisIndex].fromParentVariantMap(variantMap); + } + + /** + * Save numerical point action to variant map + * @return Variant map representation of the numerical point action + */ + QVariantMap toVariantMap() const override + { + auto variantMap = WidgetAction::toVariantMap(); + + for (int axisIndex = 0; axisIndex < static_cast(Axis::Count); ++axisIndex) + _elementActions[axisIndex].insertIntoVariantMap(variantMap); + + return variantMap; + } + +public: // Action getters + + const NumericalActionType& getAction(const Axis& axis) const { return _elementActions[static_cast(axis)]; } + const NumericalActionType& getXAction() const { return getAction(Axis::X); } + const NumericalActionType& getYAction() const { return getAction(Axis::Y); } + + NumericalActionType& getAction(const Axis& axis) { return _elementActions[static_cast(axis)]; } + NumericalActionType& getXAction() { return getAction(Axis::X); } + NumericalActionType& getYAction() { return getAction(Axis::Y); } + +private: + NumericalActionType _elementActions[static_cast(Axis::Count)]; /** Elements actions */ +}; + +} diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 40f482383..5498f9cb3 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -14,8 +14,6 @@ namespace mv::gui DensityRenderer::DensityRenderer(RenderMode renderMode) : _renderMode(renderMode) { - getNavigator().setZoomRectangleWorld(QRectF(0, 0, 1, 1)); - setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); } DensityRenderer::~DensityRenderer() @@ -37,6 +35,10 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); + // .0f, -_densityComputation.getDensityTextureSize().height() + + setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); + updateQuad(); } diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index ff3d14eb7..c747c2281 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -68,6 +68,28 @@ void Navigator2D::initialize(QWidget* sourceWidget) _navigationAction.getZoomPercentageAction().setValue(getZoomPercentage()); }); + connect(this, &Navigator2D::zoomCenterWorldChanged, this, [this](const QPointF& previousZoomCenterWorld, const QPointF& currentZoomCenterWorld) -> void { + _navigationAction.getZoomCenterAction().set(currentZoomCenterWorld); + + qDebug() << currentZoomCenterWorld; + }); + + connect(this, &Navigator2D::zoomFactorChanged, this, [this](float previousZoomFactor, float currentZoomFactor) -> void { + _navigationAction.getZoomFactorAction().setValue(currentZoomFactor); + }); + + connect(&_navigationAction.getZoomCenterAction(), &DecimalPointAction::valueChanged, this, [this](float x, float y) -> void { + beginChangeZoomRectangleWorld(); + { + setZoomCenterWorld(QPointF(x, y)); + } + endChangeZoomRectangleWorld(); + }); + + // connect(&_navigationAction.getZoomCenterYAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + // setZoomCenterWorld(QPointF(_zoomCenterWorld.x(), value)); + //}); + connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { setZoomPercentage(getZoomPercentage() + 10.f); }); @@ -81,11 +103,17 @@ void Navigator2D::initialize(QWidget* sourceWidget) setZoomPercentage(getZoomPercentage() - 10.f); }); + connect(&_navigationAction.getZoomFactorAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { + setZoomFactor(value); + }); + connect(&_renderer, &Renderer2D::worldBoundsChanged, this, [this](const QRectF& worldBounds) -> void { if (!hasUserNavigated()) resetView(); }); + setZoomFactor(_navigationAction.getZoomFactorAction().getValue()); + _sourceWidget->addAction(&_navigationAction.getZoomInAction()); _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); @@ -246,16 +274,29 @@ float Navigator2D::getZoomFactor() const return _zoomFactor; } +void Navigator2D::setZoomFactor(float zoomFactor) +{ + qDebug() << __FUNCTION__ << zoomFactor; + if (zoomFactor == _zoomFactor) + return; + + const auto previousZoomFactor = _zoomFactor; + + _zoomFactor = zoomFactor; + + emit zoomFactorChanged(previousZoomFactor, _zoomFactor); +} + float Navigator2D::getZoomPercentage() const { - const auto dataBounds = _renderer.getDataBounds(); + const auto worldBounds = _renderer.getWorldBounds(); const auto zoomRectangleWorld = getZoomRectangleWorld(); - if (!dataBounds.isValid() || !zoomRectangleWorld.isValid()) + if (!worldBounds.isValid() || !zoomRectangleWorld.isValid()) return 1.0f; - const auto factorX = static_cast(dataBounds.width()) / static_cast(zoomRectangleWorld.width()); - const auto factorY = static_cast(dataBounds.height()) / static_cast(zoomRectangleWorld.height()); + const auto factorX = static_cast(worldBounds.width()) / static_cast(zoomRectangleWorld.width()); + const auto factorY = static_cast(worldBounds.height()) / static_cast(zoomRectangleWorld.height()); const auto scaleFactor = factorX > factorY ? factorX : factorY; return scaleFactor * 100.f; @@ -271,12 +312,10 @@ void Navigator2D::setZoomPercentage(float zoomPercentage) return; const auto zoomPercentageNormalized = .01f * zoomPercentage; - const auto zoomFactorX = static_cast(_renderer.getDataBounds().width()) / static_cast(_renderer.getRenderSize().width()); - const auto zoomFactorY = static_cast(_renderer.getDataBounds().height()) / static_cast(_renderer.getRenderSize().height()); + const auto zoomFactorX = static_cast(_renderer.getWorldBounds().width()) / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = static_cast(_renderer.getWorldBounds().height()) / static_cast(_renderer.getRenderSize().height()); - _zoomFactor = std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized; - - setZoomRectangleWorld(getZoomRectangleWorld()); + setZoomFactor(std::max(zoomFactorX, zoomFactorY) / zoomPercentageNormalized); } endChangeZoomRectangleWorld(); } @@ -325,8 +364,7 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) { const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), center).toPointF(); - _zoomFactor /= factor; - + setZoomFactor(_zoomFactor /= factor); setZoomCenterWorld(p1 + (_zoomCenterWorld - p1) / factor); } endChangeZoomRectangleWorld(); @@ -382,13 +420,18 @@ void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) if (zoomCenterWorld == _zoomCenterWorld) return; + const auto previousZoomCenterWorld = _zoomCenterWorld; + _zoomCenterWorld = zoomCenterWorld; - setZoomRectangleWorld(getZoomRectangleWorld()); + emit zoomCenterWorldChanged(previousZoomCenterWorld, _zoomCenterWorld); } void Navigator2D::resetView(bool force /*= false*/) { + if (mv::projects().isOpeningProject() || mv::projects().isImportingProject()) + return; + if (!_initialized) return; @@ -403,20 +446,11 @@ void Navigator2D::resetView(bool force /*= false*/) { beginChangeZoomRectangleWorld(); { - const auto zoomFactorX = _renderer.getDataBounds().width() / static_cast(_renderer.getRenderSize().width()); - const auto zoomFactorY = _renderer.getDataBounds().height() / static_cast(_renderer.getRenderSize().height()); - - _zoomFactor = std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f; - - setZoomCenterWorld(_renderer.getDataBounds().center()); - - // if (!_userHasNavigated || force) { - // setZoomRectangleWorld(_renderer.getDataBounds()); - - // _userHasNavigated = false; - //} + const auto zoomFactorX = _renderer.getWorldBounds().width() / static_cast(_renderer.getRenderSize().width()); + const auto zoomFactorY = _renderer.getWorldBounds().height() / static_cast(_renderer.getRenderSize().height()); - + setZoomFactor(std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f); + setZoomCenterWorld(_renderer.getWorldBounds().center()); } endChangeZoomRectangleWorld(); } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index e980acdcb..8648fbda8 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -81,6 +81,12 @@ class CORE_EXPORT Navigator2D : public QObject */ float getZoomFactor() const; + /** + * Set the zoom factor to \p zoomFactor + * @param zoomFactor Zoom factor + */ + void setZoomFactor(float zoomFactor); + /** * Get the zoom percentage * @return Zoom percentage @@ -269,6 +275,20 @@ class CORE_EXPORT Navigator2D : public QObject */ void zoomRectangleWorldChanged(const QRectF& previousZoomRectangleWorld, const QRectF& currentZoomRectangleWorld); + /** + * Signals that the zoom center in world coordinates has changed from \p previousZoomCenterWorld to \p currentZoomCenterWorld + * @param previousZoomCenterWorld Previous world zoom center + * @param currentZoomCenterWorld Current world zoom center + */ + void zoomCenterWorldChanged(const QPointF& previousZoomCenterWorld, const QPointF& currentZoomCenterWorld); + + /** + * Signals that the zoom factor has changed from \p previousZoomFactor to \p currentZoomFactor + * @param previousZoomFactor Previous zoom factor + * @param currentZoomFactor Current zoom factor + */ + void zoomFactorChanged(float previousZoomFactor, float currentZoomFactor); + private: QPointer _sourceWidget; /** Source widget for panning and zooming */ Renderer2D& _renderer; /** Reference to parent renderer */ diff --git a/ManiVault/src/renderers/Renderer.h b/ManiVault/src/renderers/Renderer.h index f556b8b6f..9f0ae2b49 100644 --- a/ManiVault/src/renderers/Renderer.h +++ b/ManiVault/src/renderers/Renderer.h @@ -12,6 +12,8 @@ #include "ManiVaultGlobals.h" +#include "actions/WidgetAction.h" + #include #include @@ -24,11 +26,14 @@ class CORE_EXPORT Renderer : public QObject, protected QOpenGLFunctions_3_3_Core { protected: - /** - * Construct a new renderer - * @param parent Pointer to the parent object + /** + * Construct with pointer to \p parent object + * @param parent Pointer to parent object */ - explicit Renderer(QObject* parent = nullptr) : QObject(parent) {} + explicit Renderer(QObject* parent = nullptr) : + QObject(parent) + { + } virtual void init() = 0; virtual void resize(QSize renderSize) = 0; diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 40e4c548e..c3f0c49c6 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -30,9 +30,8 @@ Q_OBJECT public: /** - * Construct a new two-dimensional renderer - * - * @param parent Pointer to the parent object + * Construct with pointer to \p parent object + * @param parent Pointer to parent object */ explicit Renderer2D(QObject* parent = nullptr); From 82a5158cfc46efbf4e4ce9256da3fb279b486dfc Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 09:34:54 +0200 Subject: [PATCH 79/89] Synchronize zoom percentage --- ManiVault/src/renderers/Navigator2D.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index c747c2281..4e3912731 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -285,6 +285,8 @@ void Navigator2D::setZoomFactor(float zoomFactor) _zoomFactor = zoomFactor; emit zoomFactorChanged(previousZoomFactor, _zoomFactor); + + setZoomPercentage(getZoomPercentage()); } float Navigator2D::getZoomPercentage() const From 43172157d45c290e7b00a5d90618ff7a0b2879e0 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 10:24:56 +0200 Subject: [PATCH 80/89] Zoom to selection is working --- ManiVault/src/actions/NavigationAction.cpp | 1 + ManiVault/src/util/NumericalRange.h | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index f69f921c9..5026f6e09 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -64,6 +64,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); addAction(&_zoomExtentsAction); + addAction(&_zoomSelectionAction); addAction(&_zoomCenterAction); //addAction(&_zoomRegionAction); diff --git a/ManiVault/src/util/NumericalRange.h b/ManiVault/src/util/NumericalRange.h index 408c37507..95e006857 100644 --- a/ManiVault/src/util/NumericalRange.h +++ b/ManiVault/src/util/NumericalRange.h @@ -85,6 +85,7 @@ class NumericalRange : public QPair /** * Addition operator + * @param other Other range * @return Added range */ NumericalRange& operator += (const NumericalRange& other) @@ -95,11 +96,24 @@ class NumericalRange : public QPair return *this; } + /** + * Addition operator + * @param value Value to add + * @return Added range + */ + NumericalRange& operator += (float value) + { + this->first = std::min(this->first, value); + this->second = std::max(this->second, value); + + return *this; + } + /** * Equality operator * @param rhs Right-hand-side operator */ - const bool operator == (const NumericalRange& rhs) const { + bool operator == (const NumericalRange& rhs) const { return rhs.getMinimum() == getMinimum() && rhs.getMaximum() == getMaximum(); } @@ -107,7 +121,7 @@ class NumericalRange : public QPair * Inequality operator * @param rhs Right-hand-side operator */ - const bool operator != (const NumericalRange& rhs) const { + bool operator != (const NumericalRange& rhs) const { return rhs.getMinimum() != getMinimum() || rhs.getMaximum() != getMaximum(); } }; From ef8b44ee6648f9a67a00bf417e6b55cb7748babc Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 10:52:44 +0200 Subject: [PATCH 81/89] Fix cursors --- ManiVault/src/renderers/Navigator2D.cpp | 44 ++++++++++++++++++++----- ManiVault/src/renderers/Navigator2D.h | 14 +++++++- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 4e3912731..924351c08 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -150,12 +150,16 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (event->type() == QEvent::Wheel) { if (auto* wheelEvent = dynamic_cast(event)) { - constexpr auto zoomSensitivity = .1f; - - if (wheelEvent->angleDelta().x() < 0) - zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); - else - zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + changeCursor(Qt::ClosedHandCursor); + { + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + } + restoreCursor(); } } @@ -165,7 +169,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) resetView(); if (mouseEvent->buttons() == Qt::LeftButton) { - _sourceWidget->setCursor(Qt::ClosedHandCursor); + changeCursor(Qt::ClosedHandCursor); _mousePositions << mouseEvent->pos(); @@ -175,7 +179,7 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) } if (event->type() == QEvent::MouseButtonRelease) { - _sourceWidget->setCursor(Qt::ArrowCursor); + restoreCursor(); _mousePositions.clear(); @@ -276,7 +280,6 @@ float Navigator2D::getZoomFactor() const void Navigator2D::setZoomFactor(float zoomFactor) { - qDebug() << __FUNCTION__ << zoomFactor; if (zoomFactor == _zoomFactor) return; @@ -591,6 +594,8 @@ void Navigator2D::beginNavigation() _userHasNavigated = true; + changeCursor(Qt::OpenHandCursor); + setIsNavigating(true); emit navigationStarted(); @@ -605,6 +610,8 @@ void Navigator2D::endNavigation() qDebug() << __FUNCTION__; #endif + restoreCursor(); + setIsNavigating(false); emit navigationEnded(); @@ -620,4 +627,23 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } +void Navigator2D::changeCursor(const QCursor& cursor) const +{ + Q_ASSERT(_sourceWidget); + + if (!_sourceWidget) + return; + + _sourceWidget->setCursor(cursor); +} + +void Navigator2D::restoreCursor() const +{ + Q_ASSERT(_sourceWidget); + + if (!_sourceWidget) + return; + + _sourceWidget->setCursor(_cachedCursor); +} } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 8648fbda8..115748bd6 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -224,6 +224,17 @@ class CORE_EXPORT Navigator2D : public QObject /** End changing the zoom rectangle in world coordinates */ void endChangeZoomRectangleWorld(); +protected: // Cursor + + /** + * Change the cursor to \p cursor + * @param cursor Cursor + */ + void changeCursor(const QCursor& cursor) const; + + /** Restore cached cursor */ + void restoreCursor() const; + signals: /** Signals that panning has started */ @@ -303,7 +314,8 @@ class CORE_EXPORT Navigator2D : public QObject float _zoomRectangleMargin; /** Zoom rectangle margin */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ - gui::NavigationAction _navigationAction; /** Navigation group action */ + gui::NavigationAction _navigationAction; /** Navigation group action */ + QCursor _cachedCursor; /** Cached cursor */ }; } From a4819a9c40c151e6e64579fdcd7979b857444921 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 12:17:32 +0200 Subject: [PATCH 82/89] Add icon modifier support --- ManiVault/src/actions/NavigationAction.cpp | 13 +++++----- ManiVault/src/renderers/Navigator2D.cpp | 22 ++++++++-------- ManiVault/src/renderers/Navigator2D.h | 2 +- ManiVault/src/util/StyledIcon.cpp | 30 +++++++++++++++++++++- ManiVault/src/util/StyledIcon.h | 22 +++++++++++----- ManiVault/src/util/StyledIconCommon.h | 1 + ManiVault/src/util/StyledIconEngine.cpp | 18 +++++++++++++ 7 files changed, 82 insertions(+), 26 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 5026f6e09..146112ef2 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -34,15 +34,16 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomOutAction.setToolTip("Zoom out by 10% (-)"); _zoomPercentageAction.setToolTip("Zoom in/out (+)"); _zoomInAction.setToolTip("Zoom in by 10%"); - _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (z)"); - _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (d)"); + _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (o)"); + _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (b)"); _zoomPercentageAction.setOverrideSizeHint(QSize(300, 0)); _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); - _zoomExtentsAction.setIconByName("compress"); - _zoomSelectionAction.setIconByName("search-location"); + _zoomExtentsAction.setIconByName("expand"); + + _zoomSelectionAction.setIcon(StyledIcon("expand").withModifier("mouse-pointer")); _zoomSelectionAction.setEnabled(false); _zoomPercentageAction.setSuffix("%"); @@ -63,8 +64,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); - addAction(&_zoomExtentsAction); - addAction(&_zoomSelectionAction); + addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); + addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); addAction(&_zoomCenterAction); //addAction(&_zoomRegionAction); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 924351c08..38f389570 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -150,16 +150,12 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (event->type() == QEvent::Wheel) { if (auto* wheelEvent = dynamic_cast(event)) { - changeCursor(Qt::ClosedHandCursor); - { - constexpr auto zoomSensitivity = .1f; - - if (wheelEvent->angleDelta().x() < 0) - zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); - else - zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); - } - restoreCursor(); + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); } } @@ -610,7 +606,7 @@ void Navigator2D::endNavigation() qDebug() << __FUNCTION__; #endif - restoreCursor(); + changeCursor(Qt::ArrowCursor); setIsNavigating(false); @@ -627,13 +623,15 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } -void Navigator2D::changeCursor(const QCursor& cursor) const +void Navigator2D::changeCursor(const QCursor& cursor) { Q_ASSERT(_sourceWidget); if (!_sourceWidget) return; + _cachedCursor = _sourceWidget->cursor(); + _sourceWidget->setCursor(cursor); } diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 115748bd6..c7e357f35 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -230,7 +230,7 @@ class CORE_EXPORT Navigator2D : public QObject * Change the cursor to \p cursor * @param cursor Cursor */ - void changeCursor(const QCursor& cursor) const; + void changeCursor(const QCursor& cursor); /** Restore cached cursor */ void restoreCursor() const; diff --git a/ManiVault/src/util/StyledIcon.cpp b/ManiVault/src/util/StyledIcon.cpp index 07477c28f..348cb15cc 100644 --- a/ManiVault/src/util/StyledIcon.cpp +++ b/ManiVault/src/util/StyledIcon.cpp @@ -52,7 +52,7 @@ QMap StyledIcon::pixmaps = {} QVector StyledIcon::iconFontPreferenceGroups = { { "FontAwesomeRegular", "FontAwesomeSolid", "FontAwesomeBrandsRegular" } }; QMap> StyledIcon::iconFontVersions = {}; -StyledIcon::StyledIcon(const QString& iconName /*= ""*/, const QString& iconFontName /*= defaultIconFontName*/, const Version& iconFontVersion /*= defaultIconFontVersion*/, QWidget* parent /*= nullptr*/) +StyledIcon::StyledIcon(const QString& iconName /*= ""*/, const QString& iconFontName /*= defaultIconFontName*/, const Version& iconFontVersion /*= defaultIconFontVersion*/) { if (!iconName.isEmpty() && !iconFontName.isEmpty()) set(iconName, iconFontName, iconFontVersion); @@ -260,6 +260,34 @@ StyledIcon StyledIcon::withMode(const StyledIconMode& mode) return *this; } +StyledIcon StyledIcon::withModifier(const QString& iconName, const QString& iconFontName, const Version& iconFontVersion) +{ + try + { + if (iconName.isEmpty() || iconFontName.isEmpty()) { + return *this; + } + + _modifierIconName = iconName; + _modifierIconFontName = iconFontName; + _modifierIconFontVersion = iconFontVersion; + _iconSettings._modifierSha = generateSha(_modifierIconName, _modifierIconFontName, _modifierIconFontVersion); + + const auto iconFontResourcePath = getIconFontResourcePath(_modifierIconFontName, _modifierIconFontVersion); + + if (!QFile::exists(iconFontResourcePath)) + throw std::runtime_error(QString("Font resource not found: %1").arg(iconFontResourcePath).toStdString()); + + pixmaps[_iconSettings._modifierSha] = createIconPixmap(_modifierIconName, _modifierIconFontName, _modifierIconFontVersion, _iconSettings._mode == StyledIconMode::FixedColor ? Qt::black : _iconSettings._fixedColor); + } + catch (std::exception& e) + { + qWarning() << "Unable to set icon modifier: " << e.what(); + } + + return *this; +} + QFont StyledIcon::getIconFont(std::int32_t fontPointSize /*= -1*/, const QString& iconFontName /*= defaultIconFontName*/, const Version& iconFontVersion /*= defaultIconFontVersion*/) { const auto iconFontResourceName = getIconFontResourceName(iconFontName, iconFontVersion); diff --git a/ManiVault/src/util/StyledIcon.h b/ManiVault/src/util/StyledIcon.h index f31be3f16..5b0144335 100644 --- a/ManiVault/src/util/StyledIcon.h +++ b/ManiVault/src/util/StyledIcon.h @@ -39,9 +39,8 @@ class CORE_EXPORT StyledIcon * @param iconName Name of the icon * @param iconFontName Name of the icon font * @param iconFontVersion Version of the icon font - * @param parent Pointer to parent object (maybe nullptr) */ - explicit StyledIcon(const QString& iconName = "", const QString& iconFontName = defaultIconFontName, const Version& iconFontVersion = defaultIconFontVersion, QWidget* parent = nullptr); + explicit StyledIcon(const QString& iconName = "", const QString& iconFontName = defaultIconFontName, const Version& iconFontVersion = defaultIconFontVersion); /** * Copy construct from \p other styled icon @@ -150,6 +149,14 @@ class CORE_EXPORT StyledIcon */ StyledIcon withMode(const StyledIconMode& mode); + /** + * Set icon modifier + * @param iconName Name of the modifier icon + * @param iconFontName Name of the modifier icon font + * @param iconFontVersion Version of the modifier icon font + */ + StyledIcon withModifier(const QString& iconName, const QString& iconFontName = defaultIconFontName, const Version& iconFontVersion = defaultIconFontVersion); + /** * Get icon font for \p iconFontName at \p iconFontVersion * @param fontPointSize Point size of the font @@ -260,10 +267,13 @@ class CORE_EXPORT StyledIcon static void updateIconFontVersions(const QString& iconFontName); private: - QString _iconName; /** Name of the icon */ - QString _iconFontName; /** Name of the icon font */ - Version _iconFontVersion; /** Version of the icon font */ - StyledIconSettings _iconSettings; /** Icon settings */ + QString _iconName; /** Name of the icon */ + QString _iconFontName; /** Name of the icon font */ + Version _iconFontVersion; /** Version of the icon font */ + StyledIconSettings _iconSettings; /** Icon settings */ + QString _modifierIconName; /** Name of the modifier icon */ + QString _modifierIconFontName; /** Name of the modifier icon font */ + Version _modifierIconFontVersion; /** Version of the modifier icon font */ protected: static QMap fontMetadata; /** Font-specific metadata */ diff --git a/ManiVault/src/util/StyledIconCommon.h b/ManiVault/src/util/StyledIconCommon.h index 20aa6db61..a9fe8881f 100644 --- a/ManiVault/src/util/StyledIconCommon.h +++ b/ManiVault/src/util/StyledIconCommon.h @@ -42,6 +42,7 @@ struct StyledIconSettings QPalette::ColorRole getColorRoleForCurrentTheme() const; QString _sha; /** Icon key */ + QString _modifierSha; /** Modifier icon key */ StyledIconMode _mode; /** Styled icon coloring mode */ QPalette::ColorGroup _colorGroupLightTheme; /** Color group for light theme */ QPalette::ColorGroup _colorGroupDarkTheme; /** Color group for dark theme */ diff --git a/ManiVault/src/util/StyledIconEngine.cpp b/ManiVault/src/util/StyledIconEngine.cpp index f083edf6d..a0eb19748 100644 --- a/ManiVault/src/util/StyledIconEngine.cpp +++ b/ManiVault/src/util/StyledIconEngine.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace mv::util { @@ -52,6 +53,18 @@ QPixmap StyledIconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::Sta break; } } + + if (!_iconSettings._modifierSha.isEmpty()) { + const auto recolorColor = qApp->palette().color(static_cast(mode), _iconSettings.getColorRoleForCurrentTheme()); + const auto modifierIconPixmap = recolorPixmap(StyledIcon::pixmaps[_iconSettings._modifierSha], size / 2, recolorColor); + + QPainter modifierIconPixmapPainter(&result); + + modifierIconPixmapPainter.setRenderHint(QPainter::Antialiasing); + modifierIconPixmapPainter.setRenderHint(QPainter::SmoothPixmapTransform, true); + modifierIconPixmapPainter.setRenderHint(QPainter::LosslessImageRendering, true); + modifierIconPixmapPainter.drawPixmap(QPoint(size.width() / 2, size.height() / 2), modifierIconPixmap); + } auto& badgeParameters = _iconSettings._badgeParameters; @@ -124,6 +137,11 @@ QPixmap StyledIconEngine::recolorPixmap(const QPixmap& pixmap, const QSize& size coloredPixmap.fill(Qt::transparent); QPainter painter(&coloredPixmap); + + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::LosslessImageRendering, true); + painter.drawPixmap(0, 0, size.width(), size.height(), pixmap); painter.setCompositionMode(QPainter::CompositionMode_SourceIn); From 1f0c93cd668cd75fd8f05da2ff3e5a1c579ea6b1 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 13:33:10 +0200 Subject: [PATCH 83/89] Added zoom margins back in --- ManiVault/src/actions/NavigationAction.cpp | 20 +++----------------- ManiVault/src/renderers/DensityRenderer.cpp | 13 ++++++++++--- ManiVault/src/renderers/DensityRenderer.h | 3 +++ ManiVault/src/renderers/Navigator2D.cpp | 9 +++++---- ManiVault/src/renderers/Navigator2D.h | 5 +++-- ManiVault/src/renderers/PointRenderer.cpp | 10 +++++++++- ManiVault/src/renderers/PointRenderer.h | 3 +++ ManiVault/src/renderers/Renderer2D.cpp | 10 ++++++++-- ManiVault/src/renderers/Renderer2D.h | 5 +++++ 9 files changed, 49 insertions(+), 29 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 146112ef2..dea3ca25b 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -41,9 +41,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomOutAction.setIconByName("search-minus"); _zoomInAction.setIconByName("search-plus"); - _zoomExtentsAction.setIconByName("expand"); + _zoomExtentsAction.setIconByName("compress"); - _zoomSelectionAction.setIcon(StyledIcon("expand").withModifier("mouse-pointer")); + _zoomSelectionAction.setIcon(StyledIcon("compress").withModifier("mouse-pointer")); _zoomSelectionAction.setEnabled(false); _zoomPercentageAction.setSuffix("%"); @@ -64,23 +64,9 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomOutAction, gui::TriggerAction::Icon); addAction(&_zoomPercentageAction); addAction(&_zoomInAction, gui::TriggerAction::Icon); + addAction(&_zoomCenterAction); addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); - addAction(&_zoomCenterAction); - - //addAction(&_zoomRegionAction); - - // connect(&_zoomCenterXAction, &DecimalAction::valueChanged, this, [this](float value) -> void { - // qDebug() << "Zoom center x: " << value; - //}); - - // connect(&_zoomCenterYAction, &DecimalAction::valueChanged, this, [this](float value) -> void { - // qDebug() << "Zoom center y: " << value; - //}); - - connect(&_zoomFactorAction, &DecimalAction::valueChanged, this, [this](float value) -> void { - qDebug() << "Zoom factor: " << value; - }); } void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 5498f9cb3..9e702914d 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -35,11 +35,18 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); - // .0f, -_densityComputation.getDensityTextureSize().height() + updateQuad(); +} - setWorldBounds(QRectF(QPointF(), _densityComputation.getDensityTextureSize())); +QRectF DensityRenderer::computeWorldBounds() const +{ + const auto textureSize = static_cast(_densityComputation.getDensityTextureSize().height()); + const auto marginX = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); + const auto marginY = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); + const auto margin = std::max(marginX, marginY); + const auto margins = QMarginsF(margin, margin, margin, margin); - updateQuad(); + return QRectF(QPointF(),_densityComputation.getDensityTextureSize()).marginsAdded(margins); } void DensityRenderer::setRenderMode(RenderMode renderMode) diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index 09f4c9e51..b42b91a52 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -39,6 +39,9 @@ class CORE_EXPORT DensityRenderer : public Renderer2D */ void setDataBounds(const QRectF& dataBounds) override; + /** Update the world bounds */ + QRectF computeWorldBounds() const override; + void setRenderMode(RenderMode renderMode); void setData(const std::vector* data); void setWeights(const std::vector* weights); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 38f389570..ae200f867 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -26,7 +26,8 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _isPanning(false), _isZooming(false), _zoomFactor(1.0f), - _zoomRectangleMargin(0.f), + _zoomMarginScreen(100.f), + _zoomMarginWorld(.0f), _userHasNavigated(), _navigationAction(this, "Navigation") { @@ -264,9 +265,9 @@ void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) emit zoomRectangleWorldChanged(previousZoomRectangleWorld, getZoomRectangleWorld()); } -float Navigator2D::getZoomRectangleMargin() const +float Navigator2D::getZoomMarginScreen() const { - return _zoomRectangleMargin; + return _zoomMarginScreen; } float Navigator2D::getZoomFactor() const @@ -450,7 +451,7 @@ void Navigator2D::resetView(bool force /*= false*/) const auto zoomFactorX = _renderer.getWorldBounds().width() / static_cast(_renderer.getRenderSize().width()); const auto zoomFactorY = _renderer.getWorldBounds().height() / static_cast(_renderer.getRenderSize().height()); - setZoomFactor(std::max(zoomFactorX, zoomFactorY) + (_zoomRectangleMargin / _renderer.getRenderSize().height()) / 2.f); + setZoomFactor(std::max(zoomFactorX, zoomFactorY)); setZoomCenterWorld(_renderer.getWorldBounds().center()); } endChangeZoomRectangleWorld(); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index c7e357f35..75cfec858 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -73,7 +73,7 @@ class CORE_EXPORT Navigator2D : public QObject * Get the zoom rectangle margin * @return Zoom rectangle margin */ - float getZoomRectangleMargin() const; + float getZoomMarginScreen() const; /** * Get the zoom factor @@ -311,7 +311,8 @@ class CORE_EXPORT Navigator2D : public QObject bool _isZooming; /** Zooming flag */ float _zoomFactor; /** Zoom factor */ QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ - float _zoomRectangleMargin; /** Zoom rectangle margin */ + float _zoomMarginScreen; /** Zoom margin in screen coordinates */ + float _zoomMarginWorld; /** Zoom margin in world coordinates */ QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ bool _userHasNavigated; /** Boolean determining whether the user has navigated */ gui::NavigationAction _navigationAction; /** Navigation group action */ diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index 7da14ad71..af48a5990 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -259,8 +259,16 @@ namespace mv void PointRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); + } + + QRectF PointRenderer::computeWorldBounds() const + { + const auto marginX = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().height()) / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); + const auto marginY = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().width()) / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); + const auto margin = std::max(marginX, marginY); + const auto margins = QMarginsF(margin, margin, margin, margin); - setWorldBounds(dataBounds); + return getDataBounds().marginsAdded(margins); } void PointRenderer::setData(const std::vector& positions) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 3ff62840b..8082da470 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -141,6 +141,9 @@ namespace mv */ void setDataBounds(const QRectF& dataBounds) override; + /** Update the world bounds */ + QRectF computeWorldBounds() const override; + void setData(const std::vector& points); void setHighlights(const std::vector& highlights, const std::int32_t& numSelectedPoints); void setFocusHighlights(const std::vector& focusHighlights, const std::int32_t& numberOfFocusHighlights); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 5994c734c..3fe3c2682 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -22,6 +22,10 @@ Renderer2D::Renderer2D(QObject* parent) : void Renderer2D::resize(QSize renderSize) { _renderSize = renderSize; + + setWorldBounds(computeWorldBounds()); + + getNavigator().resetView(); } QSize Renderer2D::getRenderSize() const @@ -68,14 +72,16 @@ void Renderer2D::setDataBounds(const QRectF& dataBounds) qDebug() << __FUNCTION__ << dataBounds; #endif - const auto previousDataBounds = _dataBounds; + const auto previousDataBounds = _dataBounds; if (dataBounds == _dataBounds) return; _dataBounds = dataBounds; - + emit dataBoundsChanged(previousDataBounds, _dataBounds); + + setWorldBounds(computeWorldBounds()); } QRectF Renderer2D::getWorldBounds() const diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index c3f0c49c6..6fb77d268 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -164,6 +164,11 @@ Q_OBJECT /** Update the model-view-projection matrix */ void updateModelViewProjectionMatrix(); +protected: + + /** Compute the world bounds */ + virtual QRectF computeWorldBounds() const = 0; + signals: /** From e0e77f654952a4bd0156e28f39e5e4c300ca60fc Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 13:41:08 +0200 Subject: [PATCH 84/89] Change how the default size is overridden --- ManiVault/src/actions/WidgetActionWidget.cpp | 2 +- ManiVault/src/renderers/Navigator2D.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ManiVault/src/actions/WidgetActionWidget.cpp b/ManiVault/src/actions/WidgetActionWidget.cpp index 46efb24fe..a5732c123 100644 --- a/ManiVault/src/actions/WidgetActionWidget.cpp +++ b/ManiVault/src/actions/WidgetActionWidget.cpp @@ -30,7 +30,7 @@ QSize WidgetActionWidget::sizeHint() const return popupSizeHint; } - if (action->getOverrideSizeHint().width() > 0 || action->getOverrideSizeHint().height() > 0) + if (action->getOverrideSizeHint().width() > 0 && action->getOverrideSizeHint().height() > 0) return action->getOverrideSizeHint(); return QWidget::sizeHint(); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index ae200f867..df1da9744 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -71,8 +71,6 @@ void Navigator2D::initialize(QWidget* sourceWidget) connect(this, &Navigator2D::zoomCenterWorldChanged, this, [this](const QPointF& previousZoomCenterWorld, const QPointF& currentZoomCenterWorld) -> void { _navigationAction.getZoomCenterAction().set(currentZoomCenterWorld); - - qDebug() << currentZoomCenterWorld; }); connect(this, &Navigator2D::zoomFactorChanged, this, [this](float previousZoomFactor, float currentZoomFactor) -> void { @@ -87,10 +85,6 @@ void Navigator2D::initialize(QWidget* sourceWidget) endChangeZoomRectangleWorld(); }); - // connect(&_navigationAction.getZoomCenterYAction(), &DecimalAction::valueChanged, this, [this](float value) -> void { - // setZoomCenterWorld(QPointF(_zoomCenterWorld.x(), value)); - //}); - connect(&_navigationAction.getZoomInAction(), &TriggerAction::triggered, this, [this]() -> void { setZoomPercentage(getZoomPercentage() + 10.f); }); From 4f6126112e12414b7cf835b15a028a89be7cf6c6 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 3 Apr 2025 15:54:46 +0200 Subject: [PATCH 85/89] Work navigation freezing --- ManiVault/src/actions/NavigationAction.cpp | 23 +++ ManiVault/src/actions/NavigationAction.h | 3 + ManiVault/src/renderers/Navigator2D.cpp | 203 ++++++++++++++++----- ManiVault/src/renderers/Navigator2D.h | 72 ++++++-- ManiVault/src/util/StyledIconEngine.cpp | 7 +- 5 files changed, 246 insertions(+), 62 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index dea3ca25b..525b7862f 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -25,6 +25,7 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomExtentsAction(this, "Zoom All"), _zoomSelectionAction(this, "Zoom Selection"), _zoomRegionAction(this, "Zoom Region"), + _freezeNavigation(this, "Freeze Navigation"), _zoomRectangleAction(this, "Zoom Rectangle"), _zoomCenterAction(this, "Zoom Center"), _zoomFactorAction(this, "Zoom Factor") @@ -36,6 +37,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomInAction.setToolTip("Zoom in by 10%"); _zoomExtentsAction.setToolTip("Zoom to the boundaries of the scene (o)"); _zoomSelectionAction.setToolTip("Zoom to the boundaries of the current selection (b)"); + _zoomRegionAction.setToolTip("Zoom to a picked region"); + _freezeNavigation.setToolTip("Freeze the navigation"); _zoomPercentageAction.setOverrideSizeHint(QSize(300, 0)); @@ -46,6 +49,8 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : _zoomSelectionAction.setIcon(StyledIcon("compress").withModifier("mouse-pointer")); _zoomSelectionAction.setEnabled(false); + _zoomRegionAction.setIcon(StyledIcon("compress").withModifier("search")); + _zoomPercentageAction.setSuffix("%"); _zoomPercentageAction.setUpdateDuringDrag(false); @@ -67,6 +72,24 @@ NavigationAction::NavigationAction(QObject* parent, const QString& title) : addAction(&_zoomCenterAction); addAction(&_zoomExtentsAction, gui::TriggerAction::Icon); addAction(&_zoomSelectionAction, gui::TriggerAction::Icon); + addAction(&_zoomRegionAction, gui::TriggerAction::Icon); + addAction(&_freezeNavigation, gui::ToggleAction::WidgetFlag::CheckBox); + + const auto updateReadOnly = [this]() -> void { + const auto notFrozen = _freezeNavigation.isChecked(); + + _zoomOutAction.setEnabled(!notFrozen); + _zoomPercentageAction.setEnabled(!notFrozen); + _zoomInAction.setEnabled(!notFrozen); + _zoomExtentsAction.setEnabled(!notFrozen); + _zoomSelectionAction.setEnabled(!notFrozen); + _zoomRegionAction.setEnabled(!notFrozen); + _zoomCenterAction.setEnabled(!notFrozen); + }; + + updateReadOnly(); + + connect(&_freezeNavigation, &ToggleAction::toggled, this, updateReadOnly); } void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) diff --git a/ManiVault/src/actions/NavigationAction.h b/ManiVault/src/actions/NavigationAction.h index d7164d57c..6e399d7fd 100644 --- a/ManiVault/src/actions/NavigationAction.h +++ b/ManiVault/src/actions/NavigationAction.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction& getZoomExtentsAction() { return _zoomExtentsAction; } TriggerAction& getZoomSelectionAction() { return _zoomSelectionAction; } TriggerAction& getZoomRegionAction() { return _zoomRegionAction; } + ToggleAction& getFreezeNavigation() { return _freezeNavigation; } DecimalRectangleAction& getZoomRectangleAction() { return _zoomRectangleAction; } DecimalPointAction& getZoomCenterAction() { return _zoomCenterAction; } DecimalAction& getZoomFactorAction() { return _zoomFactorAction; } @@ -79,6 +81,7 @@ class CORE_EXPORT NavigationAction : public HorizontalGroupAction TriggerAction _zoomExtentsAction; /** Zoom extents action */ TriggerAction _zoomSelectionAction; /** Zoom to selection extents action */ TriggerAction _zoomRegionAction; /** Zoom to region action */ + ToggleAction _freezeNavigation; /** Freeze navigation action */ DecimalRectangleAction _zoomRectangleAction; /** Rectangle action for setting the current zoom bounds */ DecimalPointAction _zoomCenterAction; /** Zoom center action */ DecimalAction _zoomFactorAction; /** Zoom factor action */ diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index df1da9744..5ee831a7b 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -6,6 +6,8 @@ #include "Renderer2D.h" +#include + #ifdef _DEBUG //#define NAVIGATOR_2D_VERBOSE #endif @@ -17,6 +19,25 @@ using namespace mv::gui; namespace mv { +Navigator2D::ZoomOverlayWidget::ZoomOverlayWidget(Navigator2D& navigator, QWidget* targetWidget): + gui::OverlayWidget(targetWidget), + _navigator(navigator) +{ +} + +void Navigator2D::ZoomOverlayWidget::paintEvent(QPaintEvent* event) +{ + if (_navigator.getZoomRegionRectangle().isValid()) { + QPainter painter(this); + + painter.setBrush(QColor(0, 0, 0, 50)); + painter.setPen(QPen(QColor(0, 0, 0, 150), 2)); + painter.drawRect(_navigator.getZoomRegionRectangle()); + + event->accept(); + } +} + Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : QObject(parent), _renderer(renderer), @@ -28,6 +49,7 @@ Navigator2D::Navigator2D(Renderer2D& renderer, QObject* parent) : _zoomFactor(1.0f), _zoomMarginScreen(100.f), _zoomMarginWorld(.0f), + _zoomRegionInProgress(false), _userHasNavigated(), _navigationAction(this, "Navigation") { @@ -107,12 +129,18 @@ void Navigator2D::initialize(QWidget* sourceWidget) resetView(); }); + connect(&_navigationAction.getZoomRegionAction(), &TriggerAction::triggered, this, [this]() -> void { + beginZoomToRegion(); + }); + setZoomFactor(_navigationAction.getZoomFactorAction().getValue()); _sourceWidget->addAction(&_navigationAction.getZoomInAction()); _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); + _zoomOverlayWidget = new ZoomOverlayWidget(*this, _sourceWidget); + _initialized = true; } @@ -121,6 +149,9 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) if (!_initialized || !_enabled) return false; + if (getNavigationAction().getFreezeNavigation().isChecked()) + return QObject::eventFilter(watched, event); + if (event->type() == QEvent::KeyPress) { if (const auto* keyEvent = dynamic_cast(event)) { if (keyEvent->key() == Qt::Key_Alt) { @@ -142,51 +173,93 @@ bool Navigator2D::eventFilter(QObject* watched, QEvent* event) } if (isNavigating()) { + if (_zoomRegionInProgress) { + const auto updateZoomRegion = [this]() -> void { + int x1 = std::min(_zoomRegionPoints[0].x(), _zoomRegionPoints[1].x()); + int y1 = std::min(_zoomRegionPoints[0].y(), _zoomRegionPoints[1].y()); + int x2 = std::max(_zoomRegionPoints[0].x(), _zoomRegionPoints[1].x()); + int y2 = std::max(_zoomRegionPoints[0].y(), _zoomRegionPoints[1].y()); + + _zoomRegionRectangle = QRect(x1, y1, x2 - x1, y2 - y1); + + if (_zoomOverlayWidget) + _zoomOverlayWidget->update(); + }; + + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->buttons() == Qt::LeftButton) { + _zoomRegionPoints << mouseEvent->pos() << mouseEvent->pos(); + + updateZoomRegion(); + } + } + } + + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->buttons() == Qt::LeftButton) { + if (_zoomRegionPoints.size() == 2) + _zoomRegionPoints[1] = mouseEvent->pos(); - if (event->type() == QEvent::Wheel) { - if (auto* wheelEvent = dynamic_cast(event)) { - constexpr auto zoomSensitivity = .1f; + updateZoomRegion(); + } + } + } - if (wheelEvent->angleDelta().x() < 0) - zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); - else - zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + if (event->type() == QEvent::MouseButtonRelease) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->button() == Qt::LeftButton) { + endZoomToRegion(); + } + } + } + } else { + if (event->type() == QEvent::Wheel) { + if (auto* wheelEvent = dynamic_cast(event)) { + constexpr auto zoomSensitivity = .1f; + + if (wheelEvent->angleDelta().x() < 0) + zoomAround(wheelEvent->position().toPoint(), 1.0f - zoomSensitivity); + else + zoomAround(wheelEvent->position().toPoint(), 1.0f + zoomSensitivity); + } } - } - if (event->type() == QEvent::MouseButtonPress) { - if (const auto* mouseEvent = dynamic_cast(event)) { - if (mouseEvent->button() == Qt::MiddleButton) - resetView(); + if (event->type() == QEvent::MouseButtonPress) { + if (const auto* mouseEvent = dynamic_cast(event)) { + if (mouseEvent->button() == Qt::MiddleButton) + resetView(); - if (mouseEvent->buttons() == Qt::LeftButton) { - changeCursor(Qt::ClosedHandCursor); + if (mouseEvent->buttons() == Qt::LeftButton) { + changeCursor(Qt::ClosedHandCursor); - _mousePositions << mouseEvent->pos(); + _mousePositions << mouseEvent->pos(); - _sourceWidget->update(); + _sourceWidget->update(); + } } } - } - if (event->type() == QEvent::MouseButtonRelease) { - restoreCursor(); + if (event->type() == QEvent::MouseButtonRelease) { + restoreCursor(); - _mousePositions.clear(); + _mousePositions.clear(); - _sourceWidget->update(); - } + _sourceWidget->update(); + } - if (event->type() == QEvent::MouseMove) { - if (const auto* mouseEvent = dynamic_cast(event)) { - _mousePositions << mouseEvent->pos(); + if (event->type() == QEvent::MouseMove) { + if (const auto* mouseEvent = dynamic_cast(event)) { + _mousePositions << mouseEvent->pos(); - 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; + 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; - panBy(-panVector); + panBy(-panVector); + } } } } @@ -240,6 +313,9 @@ QRectF Navigator2D::getZoomRectangleWorld() const void Navigator2D::setZoomRectangleWorld(const QRectF& zoomRectangleWorld) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + #ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << zoomRectangleWorld; #endif @@ -271,6 +347,9 @@ float Navigator2D::getZoomFactor() const void Navigator2D::setZoomFactor(float zoomFactor) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + if (zoomFactor == _zoomFactor) return; @@ -300,11 +379,14 @@ float Navigator2D::getZoomPercentage() const void Navigator2D::setZoomPercentage(float zoomPercentage) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + beginZooming(); { beginChangeZoomRectangleWorld(); { - if (zoomPercentage < 0.05f) + if (zoomPercentage < 0.01f) return; const auto zoomPercentageNormalized = .01f * zoomPercentage; @@ -347,12 +429,11 @@ const gui::NavigationAction& Navigator2D::getNavigationAction() const void Navigator2D::zoomAround(const QPoint& center, float factor) { - if (!_initialized) + if (getNavigationAction().getFreezeNavigation().isChecked()) return; -#ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << center << factor; -#endif + if (!_initialized) + return; beginZooming(); { @@ -370,12 +451,11 @@ void Navigator2D::zoomAround(const QPoint& center, float factor) void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) { - if (!_initialized) + if (getNavigationAction().getFreezeNavigation().isChecked()) return; -#ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << zoomRectangle; -#endif + if (!_initialized) + return; beginZooming(); { @@ -390,12 +470,11 @@ void Navigator2D::zoomToRectangle(const QRectF& zoomRectangle) void Navigator2D::panBy(const QPointF& delta) { - if (!_initialized) + if (getNavigationAction().getFreezeNavigation().isChecked()) return; -#ifdef NAVIGATOR_2D_VERBOSE - qDebug() << __FUNCTION__ << delta; -#endif + if (!_initialized) + return; beginPanning(); { @@ -413,6 +492,9 @@ void Navigator2D::panBy(const QPointF& delta) void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) { + if (getNavigationAction().getFreezeNavigation().isChecked()) + return; + if (zoomCenterWorld == _zoomCenterWorld) return; @@ -434,6 +516,9 @@ void Navigator2D::resetView(bool force /*= false*/) if (!force && hasUserNavigated()) return; + if (_navigationAction.getFreezeNavigation().isChecked()) + return; + #ifdef NAVIGATOR_2D_VERBOSE qDebug() << __FUNCTION__ << force; #endif @@ -475,6 +560,11 @@ bool Navigator2D::hasUserNavigated() const return _userHasNavigated; } +QRect Navigator2D::getZoomRegionRectangle() const +{ + return _zoomRegionRectangle; +} + void Navigator2D::setIsPanning(bool isPanning) { if (!_initialized) @@ -618,6 +708,33 @@ void Navigator2D::endChangeZoomRectangleWorld() emit zoomRectangleWorldChanged(_previousZoomRectangleWorld, getZoomRectangleWorld()); } +void Navigator2D::beginZoomToRegion() +{ + setIsNavigating(true); + + _zoomRegionInProgress = true; + _zoomRegionRectangle = QRect(); + + if (_zoomOverlayWidget) + _zoomOverlayWidget->update(); +} + +void Navigator2D::endZoomToRegion() +{ + _zoomRegionPoints.clear(); + + const auto p1 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), _zoomRegionRectangle.topLeft()).toPointF(); + const auto p2 = _renderer.getScreenPointToWorldPosition(getViewMatrix(), _zoomRegionRectangle.bottomRight()).toPointF(); + + setZoomRectangleWorld(QRectF(p1, p2)); + + _zoomRegionInProgress = false; + _zoomRegionRectangle = QRect(); + + if (_zoomOverlayWidget) + _zoomOverlayWidget->update(); +} + void Navigator2D::changeCursor(const QCursor& cursor) { Q_ASSERT(_sourceWidget); diff --git a/ManiVault/src/renderers/Navigator2D.h b/ManiVault/src/renderers/Navigator2D.h index 75cfec858..1c5add717 100644 --- a/ManiVault/src/renderers/Navigator2D.h +++ b/ManiVault/src/renderers/Navigator2D.h @@ -26,6 +26,30 @@ class CORE_EXPORT Navigator2D : public QObject { Q_OBJECT +public: + + /** For drawing the zoom region */ + class ZoomOverlayWidget : public gui::OverlayWidget + { + public: + + /** + * Construct a new zoom overlay widget + * @param navigator Reference to the navigator + * @param targetWidget Pointer to the target widget + */ + ZoomOverlayWidget(Navigator2D& navigator, QWidget* targetWidget); + + /** + * Override the paint event to draw the zoom regio rectangle + * @param event + */ + void paintEvent(QPaintEvent* event) override; + + private: + Navigator2D& _navigator; /** Reference to the navigator */ + }; + public: /** @@ -180,6 +204,12 @@ class CORE_EXPORT Navigator2D : public QObject */ bool hasUserNavigated() const; + /** + * Get the zoom region rectangle in screen coordinates + * @return Zoom region rectangle in screen coordinates + */ + QRect getZoomRegionRectangle() const; + protected: // Navigation /** @@ -224,6 +254,12 @@ class CORE_EXPORT Navigator2D : public QObject /** End changing the zoom rectangle in world coordinates */ void endChangeZoomRectangleWorld(); + /** Begin zooming to a region */ + void beginZoomToRegion(); + + /** End zooming to a region */ + void endZoomToRegion(); + protected: // Cursor /** @@ -301,22 +337,26 @@ class CORE_EXPORT Navigator2D : public QObject void zoomFactorChanged(float previousZoomFactor, float currentZoomFactor); private: - QPointer _sourceWidget; /** Source widget for panning and zooming */ - Renderer2D& _renderer; /** Reference to parent renderer */ - bool _enabled; /** Enabled flag */ - bool _initialized; /** Initialized flag */ - QVector _mousePositions; /** Recorded mouse positions */ - bool _isNavigating; /** Navigating flag */ - bool _isPanning; /** Panning flag */ - bool _isZooming; /** Zooming flag */ - float _zoomFactor; /** Zoom factor */ - QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ - float _zoomMarginScreen; /** Zoom margin in screen coordinates */ - float _zoomMarginWorld; /** Zoom margin in world coordinates */ - QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ - bool _userHasNavigated; /** Boolean determining whether the user has navigated */ - gui::NavigationAction _navigationAction; /** Navigation group action */ - QCursor _cachedCursor; /** Cached cursor */ + QPointer _sourceWidget; /** Source widget for panning and zooming */ + Renderer2D& _renderer; /** Reference to parent renderer */ + bool _enabled; /** Enabled flag */ + bool _initialized; /** Initialized flag */ + QVector _mousePositions; /** Recorded mouse positions */ + bool _isNavigating; /** Navigating flag */ + bool _isPanning; /** Panning flag */ + bool _isZooming; /** Zooming flag */ + float _zoomFactor; /** Zoom factor */ + QPointF _zoomCenterWorld; /** Zoom rectangle top-left in world coordinates */ + float _zoomMarginScreen; /** Zoom margin in screen coordinates */ + float _zoomMarginWorld; /** Zoom margin in world coordinates */ + QVector _zoomRegionPoints; /** Zoom region points */ + QRect _zoomRegionRectangle; /** Zoom region rectangle */ + bool _zoomRegionInProgress; /** Zoom region in progress flag */ + QRectF _previousZoomRectangleWorld; /** Previous world zoom rectangle */ + bool _userHasNavigated; /** Boolean determining whether the user has navigated */ + gui::NavigationAction _navigationAction; /** Navigation group action */ + QCursor _cachedCursor; /** Cached cursor */ + QPointer _zoomOverlayWidget; /** Zoom overlay widget */ }; } diff --git a/ManiVault/src/util/StyledIconEngine.cpp b/ManiVault/src/util/StyledIconEngine.cpp index a0eb19748..4291fe666 100644 --- a/ManiVault/src/util/StyledIconEngine.cpp +++ b/ManiVault/src/util/StyledIconEngine.cpp @@ -55,15 +55,16 @@ QPixmap StyledIconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::Sta } if (!_iconSettings._modifierSha.isEmpty()) { - const auto recolorColor = qApp->palette().color(static_cast(mode), _iconSettings.getColorRoleForCurrentTheme()); - const auto modifierIconPixmap = recolorPixmap(StyledIcon::pixmaps[_iconSettings._modifierSha], size / 2, recolorColor); + const auto recolorColor = qApp->palette().color(static_cast(mode), _iconSettings.getColorRoleForCurrentTheme()); + const auto scaledModifierIconPixmap = StyledIcon::pixmaps[_iconSettings._modifierSha].scaled(size / 2, Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation); + const auto recoloredModifierIconPixmap = recolorPixmap(scaledModifierIconPixmap, size / 2, recolorColor); QPainter modifierIconPixmapPainter(&result); modifierIconPixmapPainter.setRenderHint(QPainter::Antialiasing); modifierIconPixmapPainter.setRenderHint(QPainter::SmoothPixmapTransform, true); modifierIconPixmapPainter.setRenderHint(QPainter::LosslessImageRendering, true); - modifierIconPixmapPainter.drawPixmap(QPoint(size.width() / 2, size.height() / 2), modifierIconPixmap); + modifierIconPixmapPainter.drawPixmap(QPointF(std::round(size.width() / 2.f), std::round(size.height() / 2.f)), recoloredModifierIconPixmap); } auto& badgeParameters = _iconSettings._badgeParameters; From 47ff612a28ae13fb5a99583524e9cbeae36d521b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 7 Apr 2025 13:55:19 +0200 Subject: [PATCH 86/89] Align density renderer with point renderer and fix shortcuts --- ManiVault/src/actions/NavigationAction.cpp | 3 +- ManiVault/src/renderers/DensityRenderer.cpp | 42 ++++++++++++--------- ManiVault/src/renderers/DensityRenderer.h | 6 +++ ManiVault/src/renderers/Navigator2D.cpp | 2 + ManiVault/src/renderers/Renderer2D.cpp | 20 +++++++++- ManiVault/src/renderers/Renderer2D.h | 27 ++++++++++--- 6 files changed, 75 insertions(+), 25 deletions(-) diff --git a/ManiVault/src/actions/NavigationAction.cpp b/ManiVault/src/actions/NavigationAction.cpp index 525b7862f..2d076efe4 100644 --- a/ManiVault/src/actions/NavigationAction.cpp +++ b/ManiVault/src/actions/NavigationAction.cpp @@ -97,7 +97,8 @@ void NavigationAction::setShortcutsEnabled(bool shortcutsEnabled) _zoomOutAction.setShortcut(shortcutsEnabled ? QKeySequence("-") : QKeySequence()); _zoomInAction.setShortcut(shortcutsEnabled ? QKeySequence("+") : QKeySequence()); _zoomExtentsAction.setShortcut(shortcutsEnabled ? QKeySequence("O") : QKeySequence()); - //_zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("d") : QKeySequence()); + _zoomSelectionAction.setShortcut(shortcutsEnabled ? QKeySequence("H") : QKeySequence()); + _zoomRegionAction.setShortcut(shortcutsEnabled ? QKeySequence("F") : QKeySequence()); } void NavigationAction::fromVariantMap(const QVariantMap& variantMap) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index 9e702914d..d6e3066e4 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -33,20 +33,24 @@ void DensityRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); - _densityComputation.setBounds(dataBounds.left(), dataBounds.right(), dataBounds.bottom(), dataBounds.top()); - updateQuad(); } -QRectF DensityRenderer::computeWorldBounds() const +QRectF DensityRenderer::computeWorldBounds() const { - const auto textureSize = static_cast(_densityComputation.getDensityTextureSize().height()); - const auto marginX = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); - const auto marginY = getNavigator().getZoomMarginScreen() * textureSize / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); - const auto margin = std::max(marginX, marginY); - const auto margins = QMarginsF(margin, margin, margin, margin); + const auto squareSize = std::max(getDataBounds().width(), getDataBounds().height()); + const auto squareDataBounds = QRectF(getDataBounds().center() - QPointF(squareSize / 2.f, squareSize / 2.f), QSizeF(squareSize, squareSize)); + const auto marginX = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().width()) / (static_cast(getRenderSize().height() - 2.f * getNavigator().getZoomMarginScreen())); + const auto marginY = getNavigator().getZoomMarginScreen() * static_cast(getDataBounds().height()) / (static_cast(getRenderSize().width() - 2.f * getNavigator().getZoomMarginScreen())); + const auto margin = std::max(marginX, marginY); + const auto margins = QMarginsF(margin, margin, margin, margin); + + return squareDataBounds.marginsAdded(margins); +} - return QRectF(QPointF(),_densityComputation.getDensityTextureSize()).marginsAdded(margins); +void DensityRenderer::setDensityComputationDataBounds(const QRectF& bounds) +{ + _densityComputation.setBounds(bounds.left(), bounds.right(), bounds.bottom(), bounds.top()); } void DensityRenderer::setRenderMode(RenderMode renderMode) @@ -148,15 +152,17 @@ void DensityRenderer::destroy() void DensityRenderer::updateQuad() { - const auto textureSize = _densityComputation.getDensityTextureSize().toSizeF(); - const auto width = static_cast(textureSize.width()); - const auto height = static_cast(textureSize.height()); - + const auto worldBounds = getDataBounds();//computeWorldBounds(); + const auto left = static_cast(worldBounds.left()); + const auto right = static_cast(worldBounds.right()); + const auto top = static_cast(worldBounds.top()); + const auto bottom = static_cast(worldBounds.bottom()); + float vertices[] = { - 0.f, 0.f, 0.0f, 0.0f, - width, 0.f, 1.0f, 0.0f, - width, height, 1.0f, 1.0f, - 0.f, height, 0.0f, 1.0f + left, bottom, 0.0f, 0.0f, + right, bottom, 1.0f, 0.0f, + right, top, 1.0f, 1.0f, + left, top, 0.0f, 1.0f }; unsigned int indices[] = { @@ -190,6 +196,8 @@ void DensityRenderer::updateQuad() void DensityRenderer::drawQuad() { + updateQuad(); + glBindVertexArray(_VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index b42b91a52..dc8c629b6 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -42,6 +42,12 @@ class CORE_EXPORT DensityRenderer : public Renderer2D /** Update the world bounds */ QRectF computeWorldBounds() const override; + /** + * Set density computation data boundss + * @param bounds Density computation data bounds + */ + void setDensityComputationDataBounds(const QRectF& bounds); + void setRenderMode(RenderMode renderMode); void setData(const std::vector* data); void setWeights(const std::vector* weights); diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index 5ee831a7b..fc65635e3 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -138,6 +138,8 @@ void Navigator2D::initialize(QWidget* sourceWidget) _sourceWidget->addAction(&_navigationAction.getZoomInAction()); _sourceWidget->addAction(&_navigationAction.getZoomExtentsAction()); _sourceWidget->addAction(&_navigationAction.getZoomOutAction()); + _sourceWidget->addAction(&_navigationAction.getZoomSelectionAction()); + _sourceWidget->addAction(&_navigationAction.getZoomRegionAction()); _zoomOverlayWidget = new ZoomOverlayWidget(*this, _sourceWidget); diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 3fe3c2682..2747585b3 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -35,14 +35,32 @@ QSize Renderer2D::getRenderSize() const Navigator2D& Renderer2D::getNavigator() { + if (_customNavigator) { + return *_customNavigator; + } + return _navigator; } const Navigator2D& Renderer2D::getNavigator() const { + if (_customNavigator) { + return *_customNavigator; + } + return _navigator; } +QPointer Renderer2D::getCustomNavigator() const +{ + return _customNavigator; +} + +void Renderer2D::setCustomNavigator(const QPointer& customNavigator) +{ + _customNavigator = customNavigator; +} + void Renderer2D::beginRender() { #ifdef RENDERER_2D_VERBOSE @@ -117,7 +135,7 @@ QVector3D Renderer2D::getScreenPointToWorldPosition(const QMatrix4x4& modelViewM QVector2D Renderer2D::getWorldPositionToNormalizedScreenPoint(const QVector3D& position) const { - const auto clipSpacePos = getProjectionMatrix() * (_navigator.getViewMatrix() * QVector4D(position, 1.0)); + const auto clipSpacePos = getProjectionMatrix() * (getNavigator().getViewMatrix() * QVector4D(position, 1.0)); return (clipSpacePos.toVector3D() / clipSpacePos.w()).toVector2D(); } diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 6fb77d268..3b62053a1 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -59,6 +59,20 @@ Q_OBJECT */ const Navigator2D& getNavigator() const; +public: + + /** + * Get custom navigator + * @return Pointer to custom navigator + */ + QPointer getCustomNavigator() const; + +/** + * Set custom navigator to \p customNavigator + * @param customNavigator Pointer to custom navigator + */ + void setCustomNavigator(const QPointer& customNavigator); + public: // Coordinate conversions /** @@ -186,12 +200,13 @@ Q_OBJECT void worldBoundsChanged(const QRectF& previousWorldBounds, const QRectF& currentWorldBounds); private: - QSize _renderSize; /** Size of the renderer canvas */ - Navigator2D _navigator; /** 2D navigator */ - QRectF _dataBounds; /** Bounds of the data */ - QRectF _worldBounds; /** Bounds of the world */ - QMatrix4x4 _modelMatrix; /** Model matrix */ - QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ + QSize _renderSize; /** Size of the renderer canvas */ + Navigator2D _navigator; /** 2D navigator */ + QPointer _customNavigator; /** Use this one in stead of Renderer2D#_navigator when set */ + QRectF _dataBounds; /** Bounds of the data */ + QRectF _worldBounds; /** Bounds of the world */ + QMatrix4x4 _modelMatrix; /** Model matrix */ + QMatrix4x4 _modelViewProjectionMatrix; /** Model-view-projection matrix */ friend class Navigator2D; }; From 41c7e5d421e7f4de6617671202cd65be57d01ef5 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 14 Apr 2025 13:56:43 +0200 Subject: [PATCH 87/89] Some explicit default inits --- ManiVault/src/renderers/PointRenderer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 8082da470..66f272c88 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -197,7 +197,7 @@ namespace mv private: /* Point properties */ - PointSettings _pointSettings; + PointSettings _pointSettings = {}; PointEffect _pointEffect = PointEffect::Size; /** Selection visualization */ @@ -212,9 +212,9 @@ namespace mv bool _randomizedDepthEnabled = true; /* Rendering variables */ - ShaderProgram _shader; - PointArrayObject _gpuPoints; - Texture2D _colormap; /** 2D colormap, sets point color based on point position */ + ShaderProgram _shader = {}; + PointArrayObject _gpuPoints = {}; + Texture2D _colormap = {}; /** 2D colormap, sets point color based on point position */ std::int32_t _numSelectedPoints = 0; /** Number of selected (highlighted points) */ std::int32_t _numberOfFocusHighlights = 0; /** Number of focus highlights */ }; From f53cc7d761733f579a3bbd119eddc77aa0d33d94 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 14 Apr 2025 13:57:01 +0200 Subject: [PATCH 88/89] Add several helper functions --- ManiVault/src/renderers/DensityRenderer.cpp | 6 ++++-- ManiVault/src/renderers/DensityRenderer.h | 2 +- ManiVault/src/renderers/PointRenderer.cpp | 12 ++++++++++++ ManiVault/src/renderers/PointRenderer.h | 5 +++++ ManiVault/src/renderers/Renderer2D.cpp | 5 +++++ ManiVault/src/renderers/Renderer2D.h | 6 ++++++ 6 files changed, 33 insertions(+), 3 deletions(-) diff --git a/ManiVault/src/renderers/DensityRenderer.cpp b/ManiVault/src/renderers/DensityRenderer.cpp index d6e3066e4..c0189817d 100644 --- a/ManiVault/src/renderers/DensityRenderer.cpp +++ b/ManiVault/src/renderers/DensityRenderer.cpp @@ -10,10 +10,12 @@ namespace mv::gui { - -DensityRenderer::DensityRenderer(RenderMode renderMode) : +DensityRenderer::DensityRenderer(RenderMode renderMode, QWidget* sourceWidget, QObject* parent) : + Renderer2D(parent), _renderMode(renderMode) { + if (sourceWidget) + setSourceWidget(sourceWidget); } DensityRenderer::~DensityRenderer() diff --git a/ManiVault/src/renderers/DensityRenderer.h b/ManiVault/src/renderers/DensityRenderer.h index dc8c629b6..47afef1b5 100644 --- a/ManiVault/src/renderers/DensityRenderer.h +++ b/ManiVault/src/renderers/DensityRenderer.h @@ -24,7 +24,7 @@ class CORE_EXPORT DensityRenderer : public Renderer2D DENSITY, LANDSCAPE }; - DensityRenderer(RenderMode renderMode); + DensityRenderer(RenderMode renderMode, QWidget* sourceWidget = nullptr, QObject* parent = nullptr); ~DensityRenderer() override; /** diff --git a/ManiVault/src/renderers/PointRenderer.cpp b/ManiVault/src/renderers/PointRenderer.cpp index af48a5990..764d8e097 100644 --- a/ManiVault/src/renderers/PointRenderer.cpp +++ b/ManiVault/src/renderers/PointRenderer.cpp @@ -256,6 +256,13 @@ namespace mv _positionBuffer.destroy(); } + PointRenderer::PointRenderer(QWidget* sourceWidget, QObject* parent) : + Renderer2D(parent) + { + if(sourceWidget) + setSourceWidget(sourceWidget); + } + void PointRenderer::setDataBounds(const QRectF& dataBounds) { Renderer2D::setDataBounds(dataBounds); @@ -427,6 +434,11 @@ namespace mv _randomizedDepthEnabled = randomizedDepth; } + void PointRenderer::initView() + { + getNavigator().resetView(true); + } + void PointRenderer::init() { initializeOpenGLFunctions(); diff --git a/ManiVault/src/renderers/PointRenderer.h b/ManiVault/src/renderers/PointRenderer.h index 66f272c88..223ce5cde 100644 --- a/ManiVault/src/renderers/PointRenderer.h +++ b/ManiVault/src/renderers/PointRenderer.h @@ -135,6 +135,9 @@ namespace mv public: using Renderer2D::Renderer2D; + PointRenderer() = default; + PointRenderer(QWidget* sourceWidget, QObject* parent = nullptr); + /** * Set data bounds to \p dataBounds * @param dataBounds Data bounds @@ -165,6 +168,8 @@ namespace mv void setAlpha(const float alpha); void setPointScaling(PointScaling scalingMode); + void initView(); + public: // Selection visualization PointSelectionDisplayMode getSelectionDisplayMode() const; diff --git a/ManiVault/src/renderers/Renderer2D.cpp b/ManiVault/src/renderers/Renderer2D.cpp index 2747585b3..5b765219d 100644 --- a/ManiVault/src/renderers/Renderer2D.cpp +++ b/ManiVault/src/renderers/Renderer2D.cpp @@ -51,6 +51,11 @@ const Navigator2D& Renderer2D::getNavigator() const return _navigator; } +void Renderer2D::setSourceWidget(QWidget* sourceWidget) +{ + getNavigator().initialize(sourceWidget); +} + QPointer Renderer2D::getCustomNavigator() const { return _customNavigator; diff --git a/ManiVault/src/renderers/Renderer2D.h b/ManiVault/src/renderers/Renderer2D.h index 3b62053a1..9d2991848 100644 --- a/ManiVault/src/renderers/Renderer2D.h +++ b/ManiVault/src/renderers/Renderer2D.h @@ -59,6 +59,12 @@ Q_OBJECT */ const Navigator2D& getNavigator() const; + /** + * Initializes the source widget used for setting the renderer view + * @param sourceWidget Pointer to the renderer widget + */ + void setSourceWidget(QWidget* sourceWidget); + public: /** From 78602d588bf8a423aa2932c4cbf07bcf9a6b7a05 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 28 Apr 2025 10:48:42 +0200 Subject: [PATCH 89/89] Skip opening project requirement if view reset is forced --- ManiVault/src/renderers/Navigator2D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ManiVault/src/renderers/Navigator2D.cpp b/ManiVault/src/renderers/Navigator2D.cpp index fc65635e3..badc657c4 100644 --- a/ManiVault/src/renderers/Navigator2D.cpp +++ b/ManiVault/src/renderers/Navigator2D.cpp @@ -509,7 +509,7 @@ void Navigator2D::setZoomCenterWorld(const QPointF& zoomCenterWorld) void Navigator2D::resetView(bool force /*= false*/) { - if (mv::projects().isOpeningProject() || mv::projects().isImportingProject()) + if (!force && (mv::projects().isOpeningProject() || mv::projects().isImportingProject())) return; if (!_initialized)