From 92e13931ba09cff7bc9ae189cf2537c2eeb64677 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Fri, 15 Sep 2023 13:35:04 +0200 Subject: [PATCH] Add multi axis zoom class Add class used to zoom all axis in a plot based on a screen rectangle. --- .../UserInterface/CMakeLists_files.cmake | 3 + .../UserInterface/RiuGridCrossQwtPlot.cpp | 20 +--- .../UserInterface/RiuGridCrossQwtPlot.h | 3 +- .../UserInterface/RiuQwtPlotZoomer.h | 9 +- .../RiuQwtPlotZoomerMultiAxes.cpp | 110 ++++++++++++++++++ .../UserInterface/RiuQwtPlotZoomerMultiAxes.h | 40 +++++++ .../UserInterface/RiuSummaryQwtPlot.cpp | 20 +--- .../UserInterface/RiuSummaryQwtPlot.h | 3 +- 8 files changed, 171 insertions(+), 37 deletions(-) create mode 100644 ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.cpp create mode 100644 ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.h diff --git a/ApplicationLibCode/UserInterface/CMakeLists_files.cmake b/ApplicationLibCode/UserInterface/CMakeLists_files.cmake index 6a3bba7a94..e8361f003b 100644 --- a/ApplicationLibCode/UserInterface/CMakeLists_files.cmake +++ b/ApplicationLibCode/UserInterface/CMakeLists_files.cmake @@ -109,6 +109,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RiuMatrixPlotWidget.h ${CMAKE_CURRENT_LIST_DIR}/RiuMenuBarBuildTools.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotRectAnnotation.h + ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotZoomerMultiAxes.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -219,6 +220,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RiuMatrixPlotWidget.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuMenuBarBuildTools.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotRectAnnotation.cpp + ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotZoomerMultiAxes.cpp ) if(RESINSIGHT_USE_QT_CHARTS) @@ -311,6 +313,7 @@ list( ${CMAKE_CURRENT_LIST_DIR}/RiuTextContentFrame.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtLegendOverlayContentFrame.h ${CMAKE_CURRENT_LIST_DIR}/RiuMatrixPlotWidget.h + ${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotZoomerMultiAxes.h ) list(APPEND QT_UI_FILES) diff --git a/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.cpp b/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.cpp index ced8ef81ef..f981e06f49 100644 --- a/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.cpp +++ b/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.cpp @@ -65,15 +65,9 @@ RiuGridCrossQwtPlot::RiuGridCrossQwtPlot( RimGridCrossPlot* plot, QWidget* paren : RiuQwtPlotWidget( plot, parent ) { // LeftButton for the zooming - m_zoomerLeft = new RiuQwtPlotZoomer( qwtPlot()->canvas() ); - m_zoomerLeft->setTrackerMode( QwtPicker::AlwaysOff ); - m_zoomerLeft->initMousePattern( 1 ); - - // Attach a zoomer for the right axis - m_zoomerRight = new RiuQwtPlotZoomer( qwtPlot()->canvas() ); - m_zoomerRight->setAxes( QwtAxis::XTop, QwtAxis::YRight ); - m_zoomerRight->setTrackerMode( QwtPicker::AlwaysOff ); - m_zoomerRight->initMousePattern( 1 ); + m_plotZoomer = new RiuQwtPlotZoomer( qwtPlot()->canvas() ); + m_plotZoomer->setTrackerMode( QwtPicker::AlwaysOff ); + m_plotZoomer->initMousePattern( 1 ); // MidButton for the panning QwtPlotPanner* panner = new QwtPlotPanner( qwtPlot()->canvas() ); @@ -82,8 +76,7 @@ RiuGridCrossQwtPlot::RiuGridCrossQwtPlot( RimGridCrossPlot* plot, QWidget* paren auto wheelZoomer = new RiuQwtPlotWheelZoomer( qwtPlot() ); connect( wheelZoomer, SIGNAL( zoomUpdated() ), SLOT( onZoomedSlot() ) ); - connect( m_zoomerLeft, SIGNAL( zoomed( const QRectF& ) ), SLOT( onZoomedSlot() ) ); - connect( m_zoomerRight, SIGNAL( zoomed( const QRectF& ) ), SLOT( onZoomedSlot() ) ); + connect( m_plotZoomer, SIGNAL( zoomed() ), SLOT( onZoomedSlot() ) ); connect( panner, SIGNAL( panned( int, int ) ), SLOT( onZoomedSlot() ) ); connect( this, SIGNAL( plotItemSelected( std::shared_ptr, bool, int ) ), @@ -254,7 +247,7 @@ bool RiuGridCrossQwtPlot::curveText( const QwtPlotCurve* curve, QString* curveTi //-------------------------------------------------------------------------------------------------- bool RiuGridCrossQwtPlot::isZoomerActive() const { - return m_zoomerLeft->isActiveAndValid() || m_zoomerRight->isActiveAndValid(); + return m_plotZoomer->isActiveAndValid(); } //-------------------------------------------------------------------------------------------------- @@ -262,8 +255,7 @@ bool RiuGridCrossQwtPlot::isZoomerActive() const //-------------------------------------------------------------------------------------------------- void RiuGridCrossQwtPlot::endZoomOperations() { - m_zoomerLeft->endZoomOperation(); - m_zoomerRight->endZoomOperation(); + m_plotZoomer->endZoomOperation(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.h b/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.h index 597a5304e7..47cba2fe4b 100644 --- a/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.h +++ b/ApplicationLibCode/UserInterface/RiuGridCrossQwtPlot.h @@ -73,6 +73,5 @@ private slots: std::unique_ptr m_annotationTool; QwtPlotMarker* m_selectedPointMarker; - QPointer m_zoomerLeft; - QPointer m_zoomerRight; + QPointer m_plotZoomer; }; diff --git a/ApplicationLibCode/UserInterface/RiuQwtPlotZoomer.h b/ApplicationLibCode/UserInterface/RiuQwtPlotZoomer.h index fdc02a9544..4bbdd414ca 100644 --- a/ApplicationLibCode/UserInterface/RiuQwtPlotZoomer.h +++ b/ApplicationLibCode/UserInterface/RiuQwtPlotZoomer.h @@ -18,16 +18,17 @@ #pragma once #include "RiuGuiTheme.h" +#include "RiuQwtPlotZoomerMultiAxes.h" #include "qwt_plot_zoomer.h" #include -class RiuQwtPlotZoomer : public QwtPlotZoomer +class RiuQwtPlotZoomer : public RiuQwtPlotZoomerMultiAxes { public: RiuQwtPlotZoomer( QWidget* canvas, bool doReplot = true ) - : QwtPlotZoomer( canvas, doReplot ) + : RiuQwtPlotZoomerMultiAxes( canvas, doReplot ) { auto color = RiuGuiTheme::getColorByVariableName( "markerColor" ); @@ -46,8 +47,6 @@ class RiuQwtPlotZoomer : public QwtPlotZoomer void endZoomOperation() { reset(); } protected: - QSizeF minZoomSize() const override { return QwtPlotZoomer::minZoomSize() / 10.0e6; } - bool accept( QPolygon& pa ) const override { if ( pa.count() < 2 ) return false; @@ -59,6 +58,6 @@ class RiuQwtPlotZoomer : public QwtPlotZoomer const int minSize = 10; if ( rect.width() < minSize && rect.height() < minSize ) return false; - return QwtPlotZoomer::accept( pa ); + return RiuQwtPlotZoomerMultiAxes::accept( pa ); } }; diff --git a/ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.cpp b/ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.cpp new file mode 100644 index 0000000000..33b34d9025 --- /dev/null +++ b/ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.cpp @@ -0,0 +1,110 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RiuQwtPlotZoomerMultiAxes.h" + +#include "qwt_picker_machine.h" +#include "qwt_plot.h" +#include "qwt_scale_div.h" +#include "qwt_scale_map.h" + +//-------------------------------------------------------------------------------------------------- +/// Create a zoomer for a plot canvas. All axes will be scaled based on the rectangle in screen coordinates. +//-------------------------------------------------------------------------------------------------- +RiuQwtPlotZoomerMultiAxes::RiuQwtPlotZoomerMultiAxes( QWidget* canvas, bool doReplot ) + : QwtPlotPicker( canvas ) +{ + if ( canvas ) + { + setTrackerMode( ActiveOnly ); + setRubberBand( RectRubberBand ); + setStateMachine( new QwtPickerDragRectMachine() ); + + if ( doReplot && plot() ) plot()->replot(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RiuQwtPlotZoomerMultiAxes::end( bool ok ) +{ + ok = QwtPlotPicker::end( ok ); + if ( !ok ) return false; + + QwtPlot* plot = RiuQwtPlotZoomerMultiAxes::plot(); + if ( !plot ) return false; + + const QPolygon& pa = selection(); + if ( pa.count() < 2 ) return false; + + QRect rect = QRect( pa.first(), pa.last() ); + rect = rect.normalized(); + + zoomFromScreenCoords( rect ); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuQwtPlotZoomerMultiAxes::zoomFromScreenCoords( const QRectF& screenCoords ) +{ + QwtPlot* plot = RiuQwtPlotZoomerMultiAxes::plot(); + if ( !plot ) return; + + const bool doReplot = plot->autoReplot(); + plot->setAutoReplot( false ); + + for ( int axisPos = 0; axisPos < QwtAxis::AxisPositions; axisPos++ ) + { + const int axesCount = plot->axesCount( axisPos ); + for ( int i = 0; i < axesCount; i++ ) + { + const QwtAxisId axisId( axisPos, i ); + + // Get the scale map for the axis used to convert between screen and domain coordinates + const QwtScaleMap map = plot->canvasMap( axisId ); + + if ( axisId.isXAxis() ) + { + const double screenX1 = screenCoords.left(); + const double screenX2 = screenCoords.right(); + const double domainX1 = map.invTransform( screenX1 ); + const double domainX2 = map.invTransform( screenX2 ); + + plot->setAxisScale( axisId, domainX1, domainX2 ); + } + else + { + const double screenY1 = screenCoords.bottom(); + const double screenY2 = screenCoords.top(); + const double domainY1 = map.invTransform( screenY1 ); + const double domainY2 = map.invTransform( screenY2 ); + + plot->setAxisScale( axisId, domainY1, domainY2 ); + } + } + } + + plot->setAutoReplot( doReplot ); + plot->replot(); + + emit zoomed(); +} diff --git a/ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.h b/ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.h new file mode 100644 index 0000000000..e706bf4731 --- /dev/null +++ b/ApplicationLibCode/UserInterface/RiuQwtPlotZoomerMultiAxes.h @@ -0,0 +1,40 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "qwt_plot_picker.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +class RiuQwtPlotZoomerMultiAxes : public QwtPlotPicker +{ + Q_OBJECT +public: + explicit RiuQwtPlotZoomerMultiAxes( QWidget*, bool doReplot = true ); + +Q_SIGNALS: + void zoomed(); + +protected: + bool end( bool ok = true ) override; + +private: + void zoomFromScreenCoords( const QRectF& screenCoords ); +}; diff --git a/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.cpp b/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.cpp index ccfa9edb2b..282c3ec29b 100644 --- a/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.cpp +++ b/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.cpp @@ -81,15 +81,9 @@ RiuSummaryQwtPlot::RiuSummaryQwtPlot( RimSummaryPlot* plot, QWidget* parent /*= connect( m_plotWidget, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( showContextMenu( QPoint ) ) ); // LeftButton for the zooming - m_zoomerLeft = new RiuQwtPlotZoomer( m_plotWidget->qwtPlot()->canvas() ); - m_zoomerLeft->setTrackerMode( QwtPicker::AlwaysOff ); - m_zoomerLeft->initMousePattern( 1 ); - - // Attach a zoomer for the right axis - m_zoomerRight = new RiuQwtPlotZoomer( m_plotWidget->qwtPlot()->canvas() ); - m_zoomerRight->setAxes( QwtAxis::XTop, QwtAxis::YRight ); - m_zoomerRight->setTrackerMode( QwtPicker::AlwaysOff ); - m_zoomerRight->initMousePattern( 1 ); + m_plotZoomer = new RiuQwtPlotZoomer( m_plotWidget->qwtPlot()->canvas() ); + m_plotZoomer->setTrackerMode( QwtPicker::AlwaysOff ); + m_plotZoomer->initMousePattern( 1 ); // MidButton for the panning QwtPlotPanner* panner = new QwtPlotPanner( m_plotWidget->qwtPlot()->canvas() ); @@ -98,8 +92,7 @@ RiuSummaryQwtPlot::RiuSummaryQwtPlot( RimSummaryPlot* plot, QWidget* parent /*= m_wheelZoomer = new RiuQwtPlotWheelZoomer( m_plotWidget->qwtPlot() ); connect( m_wheelZoomer, SIGNAL( zoomUpdated() ), SLOT( onZoomedSlot() ) ); - connect( m_zoomerLeft, SIGNAL( zoomed( const QRectF& ) ), SLOT( onZoomedSlot() ) ); - connect( m_zoomerRight, SIGNAL( zoomed( const QRectF& ) ), SLOT( onZoomedSlot() ) ); + connect( m_plotZoomer, SIGNAL( zoomed() ), SLOT( onZoomedSlot() ) ); connect( panner, SIGNAL( panned( int, int ) ), SLOT( onZoomedSlot() ) ); setDefaults(); @@ -194,7 +187,7 @@ void RiuSummaryQwtPlot::setDefaults() //-------------------------------------------------------------------------------------------------- bool RiuSummaryQwtPlot::isZoomerActive() const { - return m_zoomerLeft->isActiveAndValid() || m_zoomerRight->isActiveAndValid(); + return m_plotZoomer->isActiveAndValid(); } //-------------------------------------------------------------------------------------------------- @@ -202,8 +195,7 @@ bool RiuSummaryQwtPlot::isZoomerActive() const //-------------------------------------------------------------------------------------------------- void RiuSummaryQwtPlot::endZoomOperations() { - m_zoomerLeft->endZoomOperation(); - m_zoomerRight->endZoomOperation(); + m_plotZoomer->endZoomOperation(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.h b/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.h index 156eeaad4f..c6afbcd410 100644 --- a/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.h +++ b/ApplicationLibCode/UserInterface/RiuSummaryQwtPlot.h @@ -71,7 +71,6 @@ private slots: std::unique_ptr m_annotationTool; QPointer m_plotWidget; - QPointer m_zoomerLeft; - QPointer m_zoomerRight; + QPointer m_plotZoomer; QPointer m_wheelZoomer; };