diff --git a/ApplicationLibCode/UserInterface/CMakeLists_files.cmake b/ApplicationLibCode/UserInterface/CMakeLists_files.cmake index 4feb9d3b05..b037d583f0 100644 --- a/ApplicationLibCode/UserInterface/CMakeLists_files.cmake +++ b/ApplicationLibCode/UserInterface/CMakeLists_files.cmake @@ -23,6 +23,8 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RiuPropertyViewTabWidget.h ${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotPanel.h ${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotUpdater.h + ${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotWidget.h + ${CMAKE_CURRENT_LIST_DIR}/RiuPlotUpdater.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtLinearScaleEngine.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtScalePicker.h ${CMAKE_CURRENT_LIST_DIR}/RiuQwtCurvePointTracker.h @@ -137,6 +139,8 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RiuPropertyViewTabWidget.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotPanel.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotUpdater.cpp + ${CMAKE_CURRENT_LIST_DIR}/RiuPvtPlotWidget.cpp + ${CMAKE_CURRENT_LIST_DIR}/RiuPlotUpdater.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuQwtLinearScaleEngine.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuQwtScalePicker.cpp ${CMAKE_CURRENT_LIST_DIR}/RiuQwtCurvePointTracker.cpp diff --git a/ApplicationLibCode/UserInterface/Riu3dSelectionManager.h b/ApplicationLibCode/UserInterface/Riu3dSelectionManager.h index 54bdb5b752..71b9a90dd1 100644 --- a/ApplicationLibCode/UserInterface/Riu3dSelectionManager.h +++ b/ApplicationLibCode/UserInterface/Riu3dSelectionManager.h @@ -28,8 +28,6 @@ #include #include #include -// #include "RivWellPathSourceInfo.h" -// #include "RivWellPipeSourceInfo.h" class RimGridView; class RimEclipseView; diff --git a/ApplicationLibCode/UserInterface/RiuPlotUpdater.cpp b/ApplicationLibCode/UserInterface/RiuPlotUpdater.cpp new file mode 100644 index 0000000000..2cf315d0f5 --- /dev/null +++ b/ApplicationLibCode/UserInterface/RiuPlotUpdater.cpp @@ -0,0 +1,205 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 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 "RiuPlotUpdater.h" + +#include "Rim2dIntersectionView.h" +#include "Rim3dView.h" +#include "RimEclipseCellColors.h" +#include "RimEclipseResultDefinition.h" +#include "RimEclipseView.h" + +#include "Riu3dSelectionManager.h" + +#include + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiuPlotUpdater::RiuPlotUpdater() + : m_viewToFollowAnimationFrom( nullptr ) + , m_eclipseResultDef( nullptr ) + , m_gridIndex( 0 ) + , m_gridLocalCellIndex( 0 ) + , m_timeStepIndex( 0 ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiuEclipseSelectionItem* RiuPlotUpdater::extractEclipseSelectionItem( const RiuSelectionItem* selectionItem, Rim3dView*& newFollowAnimView ) +{ + newFollowAnimView = nullptr; + RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; + + eclipseSelectionItem = dynamic_cast( const_cast( selectionItem ) ); + + if ( eclipseSelectionItem ) + { + // If we clicked in an eclipse view, and hit something using the standard result definition there, + // set this up to follow the animation there. + + RimEclipseView* clickedInEclView = dynamic_cast( eclipseSelectionItem->m_view.p() ); + + if ( clickedInEclView && clickedInEclView->cellResult() == eclipseSelectionItem->m_resultDefinition.p() ) + { + newFollowAnimView = eclipseSelectionItem->m_view; + } + } + else + { + auto intersectionSelItem = dynamic_cast( selectionItem ); + + if ( intersectionSelItem && intersectionSelItem->eclipseSelectionItem() ) + { + eclipseSelectionItem = intersectionSelItem->eclipseSelectionItem(); + newFollowAnimView = intersectionSelItem->view(); + } + } + + return eclipseSelectionItem; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPlotUpdater::doDelayedUpdate() +{ + if ( m_eclipseResultDef != nullptr ) + { + if ( !queryDataAndUpdatePlot( m_eclipseResultDef, m_timeStepIndex, m_gridIndex, m_gridLocalCellIndex ) ) + { + clearPlot(); + } + clearDelayedInformation(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPlotUpdater::storeDelayedInformation( const RiuEclipseSelectionItem* selItem ) +{ + if ( selItem != nullptr ) + { + m_eclipseResultDef = selItem->m_resultDefinition; + m_timeStepIndex = selItem->m_timestepIdx; + m_gridIndex = selItem->m_gridIndex; + m_gridLocalCellIndex = selItem->m_gridLocalCellIndex; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPlotUpdater::clearDelayedInformation() +{ + m_eclipseResultDef = nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPlotUpdater::updateOnSelectionChanged( const RiuSelectionItem* selectionItem ) +{ + if ( !plotPanel() ) + { + return; + } + + Rim3dView* newFollowAnimView = nullptr; + RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; + + eclipseSelectionItem = extractEclipseSelectionItem( selectionItem, newFollowAnimView ); + + bool mustClearPlot = true; + m_viewToFollowAnimationFrom = nullptr; + + if ( eclipseSelectionItem && eclipseSelectionItem->m_resultDefinition ) + { + if ( plotPanel()->isVisible() ) + { + if ( queryDataAndUpdatePlot( eclipseSelectionItem->m_resultDefinition, + eclipseSelectionItem->m_timestepIdx, + eclipseSelectionItem->m_gridIndex, + eclipseSelectionItem->m_gridLocalCellIndex ) ) + { + mustClearPlot = false; + } + } + else + { + storeDelayedInformation( eclipseSelectionItem ); + mustClearPlot = false; + } + m_viewToFollowAnimationFrom = newFollowAnimView; + } + + if ( mustClearPlot ) + { + clearDelayedInformation(); + clearPlot(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPlotUpdater::updateOnTimeStepChanged( Rim3dView* changedView ) +{ + if ( !plotPanel() ) + { + return; + } + + // Don't update the plot if the view that changed time step is different + // from the view that was the source of the current plot + + if ( changedView != m_viewToFollowAnimationFrom ) + { + return; + } + + // Fetch the current global selection and only continue if the selection's view matches the view with time step change + + const RiuSelectionItem* selectionItem = Riu3dSelectionManager::instance()->selectedItem(); + Rim3dView* newFollowAnimView = nullptr; + RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; + + eclipseSelectionItem = RiuPlotUpdater::extractEclipseSelectionItem( selectionItem, newFollowAnimView ); + + if ( eclipseSelectionItem && newFollowAnimView == changedView ) + { + if ( plotPanel()->isVisible() ) + { + if ( !queryDataAndUpdatePlot( eclipseSelectionItem->m_resultDefinition, + newFollowAnimView->currentTimeStep(), + eclipseSelectionItem->m_gridIndex, + eclipseSelectionItem->m_gridLocalCellIndex ) ) + { + clearPlot(); + } + } + else + { + storeDelayedInformation( eclipseSelectionItem ); + m_timeStepIndex = newFollowAnimView->currentTimeStep(); + } + } +} diff --git a/ApplicationLibCode/UserInterface/RiuPlotUpdater.h b/ApplicationLibCode/UserInterface/RiuPlotUpdater.h new file mode 100644 index 0000000000..20c47b549c --- /dev/null +++ b/ApplicationLibCode/UserInterface/RiuPlotUpdater.h @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2024 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 +#include + +class RiuEclipseSelectionItem; +class RiuSelectionItem; +class Rim3dView; +class RimEclipseView; +class RigEclipseCaseData; +class RimEclipseResultDefinition; +class QWidget; + +//================================================================================================== +// +// +// +//================================================================================================== +class RiuPlotUpdater +{ +public: + RiuPlotUpdater(); + + void updateOnSelectionChanged( const RiuSelectionItem* selectionItem ); + void updateOnTimeStepChanged( Rim3dView* changedView ); + + void doDelayedUpdate(); + +protected: + virtual void clearPlot() = 0; + virtual QWidget* plotPanel() = 0; + + virtual bool queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResDef, + size_t timeStepIndex, + size_t gridIndex, + size_t gridLocalCellIndex ) = 0; + + void storeDelayedInformation( const RiuEclipseSelectionItem* selItem ); + void clearDelayedInformation(); + + static RiuEclipseSelectionItem* extractEclipseSelectionItem( const RiuSelectionItem* selectionItem, Rim3dView*& newFollowAnimView ); + +protected: + const Rim3dView* m_viewToFollowAnimationFrom; + + // cached values for delayed plot updates + const RimEclipseResultDefinition* m_eclipseResultDef; + size_t m_timeStepIndex; + size_t m_gridIndex; + size_t m_gridLocalCellIndex; +}; diff --git a/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.cpp b/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.cpp index 6cc42753f4..7b3eb9cbbf 100644 --- a/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.cpp +++ b/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.cpp @@ -18,485 +18,18 @@ #include "RiuPvtPlotPanel.h" -#include "RiuDockedQwtPlot.h" -#include "RiuGuiTheme.h" #include "RiuPvtPlotUpdater.h" - -#include "RiaEclipseUnitTools.h" +#include "RiuPvtPlotWidget.h" #include "RigFlowDiagSolverInterface.h" #include "cvfAssert.h" #include "cvfMath.h" -#include "qwt_legend.h" -#include "qwt_picker_machine.h" -#include "qwt_plot.h" -#include "qwt_plot_curve.h" -#include "qwt_plot_grid.h" -#include "qwt_plot_layout.h" -#include "qwt_plot_marker.h" -#include "qwt_plot_picker.h" -#include "qwt_symbol.h" -#include "qwt_text.h" - #include #include #include #include -#include - -//================================================================================================== -// -// -// -//================================================================================================== -class PvtQwtPlot : public RiuDockedQwtPlot -{ -public: - PvtQwtPlot( QWidget* parent ) - : RiuDockedQwtPlot( parent ) - { - } - QSize sizeHint() const override { return QSize( 100, 100 ); } - QSize minimumSizeHint() const override { return QSize( 0, 0 ); } -}; - -//================================================================================================== -// -// -// -//================================================================================================== -class RiuPvtQwtPicker : public QwtPicker -{ -public: - RiuPvtQwtPicker( QwtPlot* plot, RiuPvtTrackerTextProvider* trackerTextProvider ) - : QwtPicker( QwtPicker::NoRubberBand, QwtPicker::AlwaysOn, plot->canvas() ) - , m_trackerTextProvider( trackerTextProvider ) - { - setStateMachine( new QwtPickerTrackerMachine ); - } - - QwtText trackerText( const QPoint& ) const override - { - QwtText text( m_trackerTextProvider->trackerText() ); - text.setRenderFlags( Qt::AlignLeft ); - return text; - } - -private: - const RiuPvtTrackerTextProvider* m_trackerTextProvider; -}; - -//================================================================================================== -/// -/// \class RiuPvtPlotWidget -/// -/// -/// -//================================================================================================== - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RiuPvtPlotWidget::RiuPvtPlotWidget( RiuPvtPlotPanel* parent ) - : QWidget( parent ) - , m_trackerPlotMarker( nullptr ) -{ - m_qwtPlot = new PvtQwtPlot( this ); - m_qwtPlot->setProperty( "qss-class", "PvtPlot" ); - setPlotDefaults( m_qwtPlot ); - applyFontSizes( false ); - - QHBoxLayout* layout = new QHBoxLayout(); - layout->addWidget( m_qwtPlot ); - layout->setSpacing( 0 ); - layout->setContentsMargins( 0, 0, 0, 0 ); - - setLayout( layout ); - - m_qwtPicker = new RiuPvtQwtPicker( m_qwtPlot, this ); - RiuGuiTheme::styleQwtItem( m_qwtPicker ); - connect( m_qwtPicker, SIGNAL( activated( bool ) ), this, SLOT( slotPickerActivated( bool ) ) ); - connect( m_qwtPicker, SIGNAL( moved( const QPoint& ) ), this, SLOT( slotPickerPointChanged( const QPoint& ) ) ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RiuPvtPlotWidget::setPlotDefaults( QwtPlot* plot ) -{ - plot->setAutoFillBackground( true ); - - QFrame* canvasFrame = dynamic_cast( plot->canvas() ); - if ( canvasFrame ) - { - canvasFrame->setFrameShape( QFrame::NoFrame ); - } - - // Grid - { - QwtPlotGrid* grid = new QwtPlotGrid; - grid->attach( plot ); - RiuGuiTheme::styleQwtItem( grid ); - } - - // Axis number font - { - QFont axisFont = plot->axisFont( QwtAxis::XBottom ); - axisFont.setPointSize( 8 ); - plot->setAxisFont( QwtAxis::XBottom, axisFont ); - plot->setAxisFont( QwtAxis::YLeft, axisFont ); - } - - // Axis title font - { - QwtText axisTitle = plot->axisTitle( QwtAxis::XBottom ); - QFont axisTitleFont = axisTitle.font(); - axisTitleFont.setPointSize( 8 ); - axisTitleFont.setBold( false ); - axisTitle.setFont( axisTitleFont ); - axisTitle.setRenderFlags( Qt::AlignRight ); - plot->setAxisTitle( QwtAxis::XBottom, axisTitle ); - plot->setAxisTitle( QwtAxis::YLeft, axisTitle ); - } - - // Title font - { - QwtText plotTitle = plot->title(); - QFont titleFont = plotTitle.font(); - titleFont.setPointSize( 12 ); - plotTitle.setFont( titleFont ); - plot->setTitle( plotTitle ); - } - - plot->setAxisMaxMinor( QwtAxis::XBottom, 2 ); - plot->setAxisMaxMinor( QwtAxis::YLeft, 3 ); - - plot->plotLayout()->setAlignCanvasToScales( true ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RiuPvtPlotWidget::plotCurves( RiaDefines::EclipseUnitSystem unitSystem, - const std::vector& curveArr, - double pressure, - double pointMarkerYValue, - const QString& pointMarkerLabel, - const QString& plotTitle, - const QString& yAxisTitle ) -{ - m_qwtPlot->detachItems( QwtPlotItem::Rtti_PlotCurve ); - m_qwtPlot->detachItems( QwtPlotItem::Rtti_PlotMarker ); - m_qwtCurveArr.clear(); - m_pvtCurveArr.clear(); - m_trackerPlotMarker = nullptr; - - // Construct an auxiliary curve that connects the first point in all the input curves as a visual aid - // This should only be shown when the phase being plotted is oil - // Will not be added to our array of qwt curves since we do not expect the user to interact with it - { - std::vector xVals; - std::vector yVals; - for ( size_t i = 0; i < curveArr.size(); i++ ) - { - const RigFlowDiagSolverInterface::PvtCurve& curve = curveArr[i]; - if ( curve.phase == RigFlowDiagSolverInterface::PvtCurve::OIL && !curve.pressureVals.empty() && !curve.yVals.empty() ) - { - xVals.push_back( curve.pressureVals[0] ); - yVals.push_back( curve.yVals[0] ); - } - } - - if ( xVals.size() > 1 ) - { - QwtPlotCurve* qwtCurve = new QwtPlotCurve( "Auxiliary" ); - qwtCurve->setSamples( xVals.data(), yVals.data(), static_cast( xVals.size() ) ); - - qwtCurve->setStyle( QwtPlotCurve::Lines ); - qwtCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); - - qwtCurve->attach( m_qwtPlot ); - RiuGuiTheme::styleQwtItem( qwtCurve ); - } - } - - // Add the primary curves - for ( size_t i = 0; i < curveArr.size(); i++ ) - { - const RigFlowDiagSolverInterface::PvtCurve& curve = curveArr[i]; - QwtPlotCurve* qwtCurve = new QwtPlotCurve(); - QwtSymbol* curveSymbol = new QwtSymbol( QwtSymbol::Ellipse ); - - CVF_ASSERT( curve.pressureVals.size() == curve.yVals.size() ); - qwtCurve->setSamples( curve.pressureVals.data(), curve.yVals.data(), static_cast( curve.pressureVals.size() ) ); - - qwtCurve->setStyle( QwtPlotCurve::Lines ); - - if ( curve.phase == RigFlowDiagSolverInterface::PvtCurve::GAS ) - { - qwtCurve->setTitle( "Gas" ); - } - else if ( curve.phase == RigFlowDiagSolverInterface::PvtCurve::OIL ) - { - qwtCurve->setTitle( "Oil" ); - } - else - { - qwtCurve->setTitle( "Undefined" ); - } - - qwtCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); - - curveSymbol->setSize( 6, 6 ); - curveSymbol->setBrush( Qt::NoBrush ); - qwtCurve->setSymbol( curveSymbol ); - - qwtCurve->attach( m_qwtPlot ); - RiuGuiTheme::styleQwtItem( qwtCurve ); - - m_qwtCurveArr.push_back( qwtCurve ); - } - - m_pvtCurveArr = curveArr; - CVF_ASSERT( m_pvtCurveArr.size() == m_qwtCurveArr.size() ); - - // Add vertical marker line to indicate cell pressure - if ( pressure != HUGE_VAL ) - { - QwtPlotMarker* lineMarker = new QwtPlotMarker; - - QPen pen; - pen.setStyle( Qt::DashLine ); - lineMarker->setLinePen( pen ); - - lineMarker->setXValue( pressure ); - lineMarker->setLineStyle( QwtPlotMarker::VLine ); - lineMarker->setLabel( QString( "PRESSURE" ) ); - lineMarker->setLabelAlignment( Qt::AlignTop | Qt::AlignRight ); - lineMarker->setLabelOrientation( Qt::Vertical ); - lineMarker->attach( m_qwtPlot ); - RiuGuiTheme::styleQwtItem( lineMarker ); - } - - // Then point marker - if ( pressure != HUGE_VAL && pointMarkerYValue != HUGE_VAL ) - { - QwtPlotMarker* pointMarker = new QwtPlotMarker; - pointMarker->setValue( pressure, pointMarkerYValue ); - - QwtSymbol* symbol = new QwtSymbol( QwtSymbol::Ellipse ); - symbol->setSize( 13, 13 ); - QPen pen; - pen.setWidth( 2 ); - symbol->setPen( pen ); - symbol->setBrush( Qt::NoBrush ); - pointMarker->setSymbol( symbol ); - - if ( !pointMarkerLabel.isEmpty() ) - { - QwtText text( pointMarkerLabel ); - text.setRenderFlags( Qt::AlignLeft ); - text.setColor( RiuGuiTheme::getColorByVariableName( "textColor" ) ); - pointMarker->setLabel( text ); - pointMarker->setLabelAlignment( Qt::AlignTop | Qt::AlignRight ); - } - - pointMarker->attach( m_qwtPlot ); - RiuGuiTheme::styleQwtItem( pointMarker ); - } - - m_qwtPlot->setTitle( plotTitle ); - - m_qwtPlot->setAxisTitle( QwtAxis::XBottom, QString( "Pressure [%1]" ).arg( RiaEclipseUnitTools::unitStringPressure( unitSystem ) ) ); - m_qwtPlot->setAxisTitle( QwtAxis::YLeft, yAxisTitle ); - - updateTrackerPlotMarkerAndLabelFromPicker(); - - m_qwtPlot->replot(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RiuPvtPlotWidget::applyFontSizes( bool replot ) -{ - m_qwtPlot->applyFontSizes( replot ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RiuPvtPlotWidget::updateTrackerPlotMarkerAndLabelFromPicker() -{ - bool hasValidSamplePoint = false; - QPointF samplePoint; - QString mixRatioText = ""; - double mixRat = HUGE_VAL; - - if ( m_qwtPicker && m_qwtPicker->isActive() ) - { - const QPoint trackerPos = m_qwtPicker->trackerPosition(); - - int pointSampleIdx = -1; - const QwtPlotCurve* closestQwtCurve = closestCurveSample( trackerPos, &pointSampleIdx ); - if ( closestQwtCurve && pointSampleIdx >= 0 ) - { - samplePoint = closestQwtCurve->sample( pointSampleIdx ); - hasValidSamplePoint = true; - - size_t curveIdx = indexOfQwtCurve( closestQwtCurve ); - if ( curveIdx < m_pvtCurveArr.size() ) - { - const RigFlowDiagSolverInterface::PvtCurve& pvtCurve = m_pvtCurveArr[curveIdx]; - if ( static_cast( pointSampleIdx ) < pvtCurve.mixRatVals.size() ) - { - mixRat = pvtCurve.mixRatVals[pointSampleIdx]; - - // The text is Rs or Rv depending on phase - mixRatioText = ( pvtCurve.phase == RigFlowDiagSolverInterface::PvtCurve::GAS ) ? "Rv" : "Rs"; - } - } - } - } - - m_trackerLabel = ""; - - bool needsReplot = false; - - if ( hasValidSamplePoint ) - { - if ( !m_trackerPlotMarker ) - { - m_trackerPlotMarker = new QwtPlotMarker; - m_trackerPlotMarker->setTitle( QString( "TrackedPoint" ) ); - - QwtSymbol* symbol = new QwtSymbol( QwtSymbol::Ellipse ); - symbol->setSize( 13, 13 ); - symbol->setBrush( Qt::NoBrush ); - m_trackerPlotMarker->setSymbol( symbol ); - m_trackerPlotMarker->attach( m_qwtPlot ); - RiuGuiTheme::styleQwtItem( m_trackerPlotMarker ); - - needsReplot = true; - } - - if ( m_trackerPlotMarker->value() != samplePoint ) - { - m_trackerPlotMarker->setValue( samplePoint ); - needsReplot = true; - } - - m_trackerLabel = QString( "%1 (%2)" ).arg( samplePoint.y() ).arg( samplePoint.x() ); - if ( mixRat != HUGE_VAL ) - { - m_trackerLabel += QString( "\n%1 = %2" ).arg( mixRatioText ).arg( mixRat ); - } - } - else - { - if ( m_trackerPlotMarker ) - { - m_trackerPlotMarker->detach(); - delete m_trackerPlotMarker; - m_trackerPlotMarker = nullptr; - - needsReplot = true; - } - } - - if ( needsReplot ) - { - m_qwtPlot->replot(); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -const QwtPlotCurve* RiuPvtPlotWidget::closestCurveSample( const QPoint& cursorPosition, int* closestSampleIndex ) const -{ - // Construct a set containing the relevant qwt curves to consider - // These are the curves that have a corresponding Pvt source curve - std::set relevantQwtCurvesSet( m_qwtCurveArr.begin(), m_qwtCurveArr.end() ); - - if ( closestSampleIndex ) *closestSampleIndex = -1; - - const QwtPlotCurve* closestCurve = nullptr; - double distMin = HUGE_VAL; - int closestPointSampleIndex = -1; - - const QwtPlotItemList& itemList = m_qwtPlot->itemList(); - for ( QwtPlotItemIterator it = itemList.begin(); it != itemList.end(); it++ ) - { - if ( ( *it )->rtti() == QwtPlotItem::Rtti_PlotCurve ) - { - const QwtPlotCurve* curve = static_cast( *it ); - if ( relevantQwtCurvesSet.find( curve ) != relevantQwtCurvesSet.end() ) - { - double dist = HUGE_VAL; - int candidateSampleIndex = curve->closestPoint( cursorPosition, &dist ); - if ( dist < distMin ) - { - closestCurve = curve; - closestPointSampleIndex = candidateSampleIndex; - distMin = dist; - } - } - } - } - - if ( closestCurve && closestPointSampleIndex >= 0 && distMin < 50 ) - { - if ( closestSampleIndex ) *closestSampleIndex = closestPointSampleIndex; - return closestCurve; - } - else - { - return nullptr; - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -size_t RiuPvtPlotWidget::indexOfQwtCurve( const QwtPlotCurve* qwtCurve ) const -{ - for ( size_t i = 0; i < m_qwtCurveArr.size(); i++ ) - { - if ( m_qwtCurveArr[i] == qwtCurve ) - { - return i; - } - } - - return cvf::UNDEFINED_SIZE_T; -} - -//-------------------------------------------------------------------------------------------------- -/// Implements the RiuPvtTrackerTextProvider interface -//-------------------------------------------------------------------------------------------------- -QString RiuPvtPlotWidget::trackerText() const -{ - return m_trackerLabel; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RiuPvtPlotWidget::slotPickerPointChanged( const QPoint& pt ) -{ - updateTrackerPlotMarkerAndLabelFromPicker(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RiuPvtPlotWidget::slotPickerActivated( bool on ) -{ - updateTrackerPlotMarkerAndLabelFromPicker(); -} //================================================================================================== /// @@ -803,3 +336,12 @@ void RiuPvtPlotPanel::slotPhaseComboCurrentIndexChanged( int ) { plotUiSelectedCurves(); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPvtPlotPanel::showEvent( QShowEvent* event ) +{ + if ( m_plotUpdater != nullptr ) m_plotUpdater->doDelayedUpdate(); + QWidget::showEvent( event ); +} diff --git a/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.h b/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.h index 0f97890826..362d95a353 100644 --- a/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.h +++ b/ApplicationLibCode/UserInterface/RiuPvtPlotPanel.h @@ -21,75 +21,17 @@ #include "RiaDefines.h" #include "RigFlowDiagSolverInterface.h" -#include #include #include #include -class RiuDockedQwtPlot; class RiuPvtPlotUpdater; -class RiuPvtQwtPicker; -class RiuPvtPlotPanel; +class RiuPvtPlotWidget; class QComboBox; class QLabel; -class QwtPlot; -class QwtPlotMarker; -class QwtPlotCurve; - -// Interface for providing our custom picker with a tracker text -class RiuPvtTrackerTextProvider -{ -public: - virtual QString trackerText() const = 0; -}; - -//================================================================================================== -// -// -// -//================================================================================================== -class RiuPvtPlotWidget : public QWidget, public RiuPvtTrackerTextProvider -{ - Q_OBJECT - -public: - RiuPvtPlotWidget( RiuPvtPlotPanel* parent ); - - void plotCurves( RiaDefines::EclipseUnitSystem unitSystem, - const std::vector& curveArr, - double pressure, - double pointMarkerYValue, - const QString& pointMarkerLabel, - const QString& plotTitle, - const QString& yAxisTitle ); - void applyFontSizes( bool replot ); - -private: - static void setPlotDefaults( QwtPlot* plot ); - const QwtPlotCurve* closestCurveSample( const QPoint& cursorPosition, int* closestSampleIndex ) const; - size_t indexOfQwtCurve( const QwtPlotCurve* qwtCurve ) const; - void updateTrackerPlotMarkerAndLabelFromPicker(); - QString trackerText() const override; - -private slots: - void slotPickerActivated( bool ); - void slotPickerPointChanged( const QPoint& pt ); - -private: - QPointer m_qwtPlot; - - std::vector m_pvtCurveArr; // Array of source Pvt curves currently being plotted - std::vector m_qwtCurveArr; // Array of corresponding qwt curves used for mapping to Pvt curve - // when doing tracking - - QPointer m_qwtPicker; - QString m_trackerLabel; - QwtPlotMarker* m_trackerPlotMarker; -}; - //================================================================================================== // // @@ -140,6 +82,7 @@ private: private slots: void slotPhaseComboCurrentIndexChanged( int ); + void showEvent( QShowEvent* event ) override; private: RiaDefines::EclipseUnitSystem m_unitSystem; diff --git a/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.cpp b/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.cpp index e3b4f8ed27..56e25f0d36 100644 --- a/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.cpp +++ b/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.cpp @@ -47,85 +47,23 @@ //-------------------------------------------------------------------------------------------------- RiuPvtPlotUpdater::RiuPvtPlotUpdater( RiuPvtPlotPanel* targetPlotPanel ) : m_targetPlotPanel( targetPlotPanel ) - , m_viewToFollowAnimationFrom( nullptr ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RiuPvtPlotUpdater::updateOnSelectionChanged( const RiuSelectionItem* selectionItem ) +void RiuPvtPlotUpdater::clearPlot() { - if ( !m_targetPlotPanel ) - { - return; - } - - Rim3dView* newFollowAnimView = nullptr; - RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; - - eclipseSelectionItem = RiuRelativePermeabilityPlotUpdater::extractEclipseSelectionItem( selectionItem, newFollowAnimView ); - - bool mustClearPlot = true; - m_viewToFollowAnimationFrom = nullptr; - - if ( m_targetPlotPanel->isVisible() && eclipseSelectionItem && eclipseSelectionItem->m_resultDefinition ) - { - if ( queryDataAndUpdatePlot( eclipseSelectionItem->m_resultDefinition, - eclipseSelectionItem->m_timestepIdx, - eclipseSelectionItem->m_gridIndex, - eclipseSelectionItem->m_gridLocalCellIndex, - m_targetPlotPanel ) ) - { - mustClearPlot = false; - - m_viewToFollowAnimationFrom = newFollowAnimView; - } - } - - if ( mustClearPlot ) - { - m_targetPlotPanel->clearPlot(); - } + if ( m_targetPlotPanel ) m_targetPlotPanel->clearPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RiuPvtPlotUpdater::updateOnTimeStepChanged( Rim3dView* changedView ) +QWidget* RiuPvtPlotUpdater::plotPanel() { - if ( !m_targetPlotPanel || !m_targetPlotPanel->isVisible() ) - { - return; - } - - // Don't update the plot if the view that changed time step is different - // from the view that was the source of the current plot - - if ( changedView != m_viewToFollowAnimationFrom ) - { - return; - } - - // Fetch the current global selection and only continue if the selection's view matches the view with time step change - - const RiuSelectionItem* selectionItem = Riu3dSelectionManager::instance()->selectedItem(); - Rim3dView* newFollowAnimView = nullptr; - RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; - - eclipseSelectionItem = RiuRelativePermeabilityPlotUpdater::extractEclipseSelectionItem( selectionItem, newFollowAnimView ); - - if ( eclipseSelectionItem && newFollowAnimView == changedView ) - { - if ( !queryDataAndUpdatePlot( eclipseSelectionItem->m_resultDefinition, - newFollowAnimView->currentTimeStep(), - eclipseSelectionItem->m_gridIndex, - eclipseSelectionItem->m_gridLocalCellIndex, - m_targetPlotPanel ) ) - { - m_targetPlotPanel->clearPlot(); - } - } + return m_targetPlotPanel; } //-------------------------------------------------------------------------------------------------- @@ -134,11 +72,9 @@ void RiuPvtPlotUpdater::updateOnTimeStepChanged( Rim3dView* changedView ) bool RiuPvtPlotUpdater::queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResDef, size_t timeStepIndex, size_t gridIndex, - size_t gridLocalCellIndex, - RiuPvtPlotPanel* plotPanel ) + size_t gridLocalCellIndex ) { - CVF_ASSERT( plotPanel ); - + if ( m_targetPlotPanel == nullptr ) return false; if ( !eclipseResDef ) return false; RimEclipseResultCase* eclipseResultCase = dynamic_cast( eclipseResDef->eclipseCase() ); @@ -218,7 +154,13 @@ bool RiuPvtPlotUpdater::queryDataAndUpdatePlot( const RimEclipseResultDefinition "PVTNUM", cellPvtNumDouble ); - plotPanel->setPlotData( eclipseCaseData->unitsType(), fvfCurveArr, viscosityCurveArr, fvfDynProps, viscosityDynProps, cellValues, cellRefText ); + m_targetPlotPanel->setPlotData( eclipseCaseData->unitsType(), + fvfCurveArr, + viscosityCurveArr, + fvfDynProps, + viscosityDynProps, + cellValues, + cellRefText ); return true; } diff --git a/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.h b/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.h index df25eb304f..16fd7687fb 100644 --- a/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.h +++ b/ApplicationLibCode/UserInterface/RiuPvtPlotUpdater.h @@ -18,14 +18,9 @@ #pragma once -#include -#include +#include "RiuPlotUpdater.h" -class RiuSelectionItem; class RiuPvtPlotPanel; -class Rim3dView; -class RimEclipseView; -class RigEclipseCaseData; class RimEclipseResultDefinition; //================================================================================================== @@ -33,22 +28,20 @@ class RimEclipseResultDefinition; // // //================================================================================================== -class RiuPvtPlotUpdater +class RiuPvtPlotUpdater : public RiuPlotUpdater { public: RiuPvtPlotUpdater( RiuPvtPlotPanel* targetPlotPanel ); - void updateOnSelectionChanged( const RiuSelectionItem* selectionItem ); - void updateOnTimeStepChanged( Rim3dView* changedView ); +protected: + void clearPlot() override; + QWidget* plotPanel() override; -private: - static bool queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResultDef, - size_t timeStepIndex, - size_t gridIndex, - size_t gridLocalCellIndex, - RiuPvtPlotPanel* plotPanel ); + bool queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResultDef, + size_t timeStepIndex, + size_t gridIndex, + size_t gridLocalCellIndex ) override; private: QPointer m_targetPlotPanel; - const Rim3dView* m_viewToFollowAnimationFrom; }; diff --git a/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp b/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp new file mode 100644 index 0000000000..5a6a6e3bf2 --- /dev/null +++ b/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.cpp @@ -0,0 +1,496 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017 Statoil 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 "RiuPvtPlotWidget.h" + +#include "RiuDockedQwtPlot.h" +#include "RiuGuiTheme.h" +#include "RiuPvtPlotPanel.h" +#include "RiuPvtPlotUpdater.h" + +#include "RiaEclipseUnitTools.h" + +#include "RigFlowDiagSolverInterface.h" + +#include "cvfAssert.h" +#include "cvfMath.h" + +#include "qwt_legend.h" +#include "qwt_picker_machine.h" +#include "qwt_plot.h" +#include "qwt_plot_curve.h" +#include "qwt_plot_grid.h" +#include "qwt_plot_layout.h" +#include "qwt_plot_marker.h" +#include "qwt_plot_picker.h" +#include "qwt_symbol.h" +#include "qwt_text.h" + +#include + +//================================================================================================== +// +// +// +//================================================================================================== +class PvtQwtPlot : public RiuDockedQwtPlot +{ +public: + PvtQwtPlot( QWidget* parent ) + : RiuDockedQwtPlot( parent ) + { + } + QSize sizeHint() const override { return QSize( 100, 100 ); } + QSize minimumSizeHint() const override { return QSize( 0, 0 ); } +}; + +//================================================================================================== +// +// +// +//================================================================================================== +class RiuPvtQwtPicker : public QwtPicker +{ +public: + RiuPvtQwtPicker( QwtPlot* plot, RiuPvtTrackerTextProvider* trackerTextProvider ) + : QwtPicker( QwtPicker::NoRubberBand, QwtPicker::AlwaysOn, plot->canvas() ) + , m_trackerTextProvider( trackerTextProvider ) + { + setStateMachine( new QwtPickerTrackerMachine ); + } + + QwtText trackerText( const QPoint& ) const override + { + QwtText text( m_trackerTextProvider->trackerText() ); + text.setRenderFlags( Qt::AlignLeft ); + return text; + } + +private: + const RiuPvtTrackerTextProvider* m_trackerTextProvider; +}; + +//================================================================================================== +/// +/// \class RiuPvtPlotWidget +/// +/// +/// +//================================================================================================== + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RiuPvtPlotWidget::RiuPvtPlotWidget( RiuPvtPlotPanel* parent ) + : QWidget( parent ) + , m_trackerPlotMarker( nullptr ) +{ + m_qwtPlot = new PvtQwtPlot( this ); + m_qwtPlot->setProperty( "qss-class", "PvtPlot" ); + setPlotDefaults( m_qwtPlot ); + applyFontSizes( false ); + + QHBoxLayout* layout = new QHBoxLayout(); + layout->addWidget( m_qwtPlot ); + layout->setSpacing( 0 ); + layout->setContentsMargins( 0, 0, 0, 0 ); + + setLayout( layout ); + + m_qwtPicker = new RiuPvtQwtPicker( m_qwtPlot, this ); + RiuGuiTheme::styleQwtItem( m_qwtPicker ); + connect( m_qwtPicker, SIGNAL( activated( bool ) ), this, SLOT( slotPickerActivated( bool ) ) ); + connect( m_qwtPicker, SIGNAL( moved( const QPoint& ) ), this, SLOT( slotPickerPointChanged( const QPoint& ) ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPvtPlotWidget::setPlotDefaults( QwtPlot* plot ) +{ + plot->setAutoFillBackground( true ); + + QFrame* canvasFrame = dynamic_cast( plot->canvas() ); + if ( canvasFrame ) + { + canvasFrame->setFrameShape( QFrame::NoFrame ); + } + + // Grid + { + QwtPlotGrid* grid = new QwtPlotGrid; + grid->attach( plot ); + RiuGuiTheme::styleQwtItem( grid ); + } + + // Axis number font + { + QFont axisFont = plot->axisFont( QwtAxis::XBottom ); + axisFont.setPointSize( 8 ); + plot->setAxisFont( QwtAxis::XBottom, axisFont ); + plot->setAxisFont( QwtAxis::YLeft, axisFont ); + } + + // Axis title font + { + QwtText axisTitle = plot->axisTitle( QwtAxis::XBottom ); + QFont axisTitleFont = axisTitle.font(); + axisTitleFont.setPointSize( 8 ); + axisTitleFont.setBold( false ); + axisTitle.setFont( axisTitleFont ); + axisTitle.setRenderFlags( Qt::AlignRight ); + plot->setAxisTitle( QwtAxis::XBottom, axisTitle ); + plot->setAxisTitle( QwtAxis::YLeft, axisTitle ); + } + + // Title font + { + QwtText plotTitle = plot->title(); + QFont titleFont = plotTitle.font(); + titleFont.setPointSize( 12 ); + plotTitle.setFont( titleFont ); + plot->setTitle( plotTitle ); + } + + plot->setAxisMaxMinor( QwtAxis::XBottom, 2 ); + plot->setAxisMaxMinor( QwtAxis::YLeft, 3 ); + + plot->plotLayout()->setAlignCanvasToScales( true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPvtPlotWidget::plotCurves( RiaDefines::EclipseUnitSystem unitSystem, + const std::vector& curveArr, + double pressure, + double pointMarkerYValue, + const QString& pointMarkerLabel, + const QString& plotTitle, + const QString& yAxisTitle ) +{ + m_qwtPlot->detachItems( QwtPlotItem::Rtti_PlotCurve ); + m_qwtPlot->detachItems( QwtPlotItem::Rtti_PlotMarker ); + m_qwtCurveArr.clear(); + m_pvtCurveArr.clear(); + m_trackerPlotMarker = nullptr; + + // Construct an auxiliary curve that connects the first point in all the input curves as a visual aid + // This should only be shown when the phase being plotted is oil + // Will not be added to our array of qwt curves since we do not expect the user to interact with it + { + std::vector xVals; + std::vector yVals; + for ( size_t i = 0; i < curveArr.size(); i++ ) + { + const RigFlowDiagSolverInterface::PvtCurve& curve = curveArr[i]; + if ( curve.phase == RigFlowDiagSolverInterface::PvtCurve::OIL && !curve.pressureVals.empty() && !curve.yVals.empty() ) + { + xVals.push_back( curve.pressureVals[0] ); + yVals.push_back( curve.yVals[0] ); + } + } + + if ( xVals.size() > 1 ) + { + QwtPlotCurve* qwtCurve = new QwtPlotCurve( "Auxiliary" ); + qwtCurve->setSamples( xVals.data(), yVals.data(), static_cast( xVals.size() ) ); + + qwtCurve->setStyle( QwtPlotCurve::Lines ); + qwtCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); + + qwtCurve->attach( m_qwtPlot ); + RiuGuiTheme::styleQwtItem( qwtCurve ); + } + } + + // Add the primary curves + for ( size_t i = 0; i < curveArr.size(); i++ ) + { + const RigFlowDiagSolverInterface::PvtCurve& curve = curveArr[i]; + QwtPlotCurve* qwtCurve = new QwtPlotCurve(); + QwtSymbol* curveSymbol = new QwtSymbol( QwtSymbol::Ellipse ); + + CVF_ASSERT( curve.pressureVals.size() == curve.yVals.size() ); + qwtCurve->setSamples( curve.pressureVals.data(), curve.yVals.data(), static_cast( curve.pressureVals.size() ) ); + + qwtCurve->setStyle( QwtPlotCurve::Lines ); + + if ( curve.phase == RigFlowDiagSolverInterface::PvtCurve::GAS ) + { + qwtCurve->setTitle( "Gas" ); + } + else if ( curve.phase == RigFlowDiagSolverInterface::PvtCurve::OIL ) + { + qwtCurve->setTitle( "Oil" ); + } + else + { + qwtCurve->setTitle( "Undefined" ); + } + + qwtCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); + + curveSymbol->setSize( 6, 6 ); + curveSymbol->setBrush( Qt::NoBrush ); + qwtCurve->setSymbol( curveSymbol ); + + qwtCurve->attach( m_qwtPlot ); + RiuGuiTheme::styleQwtItem( qwtCurve ); + + m_qwtCurveArr.push_back( qwtCurve ); + } + + m_pvtCurveArr = curveArr; + CVF_ASSERT( m_pvtCurveArr.size() == m_qwtCurveArr.size() ); + + // Add vertical marker line to indicate cell pressure + if ( pressure != HUGE_VAL ) + { + QwtPlotMarker* lineMarker = new QwtPlotMarker; + + QPen pen; + pen.setStyle( Qt::DashLine ); + lineMarker->setLinePen( pen ); + + lineMarker->setXValue( pressure ); + lineMarker->setLineStyle( QwtPlotMarker::VLine ); + lineMarker->setLabel( QString( "PRESSURE" ) ); + lineMarker->setLabelAlignment( Qt::AlignTop | Qt::AlignRight ); + lineMarker->setLabelOrientation( Qt::Vertical ); + lineMarker->attach( m_qwtPlot ); + RiuGuiTheme::styleQwtItem( lineMarker ); + } + + // Then point marker + if ( pressure != HUGE_VAL && pointMarkerYValue != HUGE_VAL ) + { + QwtPlotMarker* pointMarker = new QwtPlotMarker; + pointMarker->setValue( pressure, pointMarkerYValue ); + + QwtSymbol* symbol = new QwtSymbol( QwtSymbol::Ellipse ); + symbol->setSize( 13, 13 ); + QPen pen; + pen.setWidth( 2 ); + symbol->setPen( pen ); + symbol->setBrush( Qt::NoBrush ); + pointMarker->setSymbol( symbol ); + + if ( !pointMarkerLabel.isEmpty() ) + { + QwtText text( pointMarkerLabel ); + text.setRenderFlags( Qt::AlignLeft ); + text.setColor( RiuGuiTheme::getColorByVariableName( "textColor" ) ); + pointMarker->setLabel( text ); + pointMarker->setLabelAlignment( Qt::AlignTop | Qt::AlignRight ); + } + + pointMarker->attach( m_qwtPlot ); + RiuGuiTheme::styleQwtItem( pointMarker ); + } + + m_qwtPlot->setTitle( plotTitle ); + + m_qwtPlot->setAxisTitle( QwtAxis::XBottom, QString( "Pressure [%1]" ).arg( RiaEclipseUnitTools::unitStringPressure( unitSystem ) ) ); + m_qwtPlot->setAxisTitle( QwtAxis::YLeft, yAxisTitle ); + + updateTrackerPlotMarkerAndLabelFromPicker(); + + m_qwtPlot->replot(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPvtPlotWidget::applyFontSizes( bool replot ) +{ + m_qwtPlot->applyFontSizes( replot ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPvtPlotWidget::updateTrackerPlotMarkerAndLabelFromPicker() +{ + bool hasValidSamplePoint = false; + QPointF samplePoint; + QString mixRatioText = ""; + double mixRat = HUGE_VAL; + + if ( m_qwtPicker && m_qwtPicker->isActive() ) + { + const QPoint trackerPos = m_qwtPicker->trackerPosition(); + + int pointSampleIdx = -1; + const QwtPlotCurve* closestQwtCurve = closestCurveSample( trackerPos, &pointSampleIdx ); + if ( closestQwtCurve && pointSampleIdx >= 0 ) + { + samplePoint = closestQwtCurve->sample( pointSampleIdx ); + hasValidSamplePoint = true; + + size_t curveIdx = indexOfQwtCurve( closestQwtCurve ); + if ( curveIdx < m_pvtCurveArr.size() ) + { + const RigFlowDiagSolverInterface::PvtCurve& pvtCurve = m_pvtCurveArr[curveIdx]; + if ( static_cast( pointSampleIdx ) < pvtCurve.mixRatVals.size() ) + { + mixRat = pvtCurve.mixRatVals[pointSampleIdx]; + + // The text is Rs or Rv depending on phase + mixRatioText = ( pvtCurve.phase == RigFlowDiagSolverInterface::PvtCurve::GAS ) ? "Rv" : "Rs"; + } + } + } + } + + m_trackerLabel = ""; + + bool needsReplot = false; + + if ( hasValidSamplePoint ) + { + if ( !m_trackerPlotMarker ) + { + m_trackerPlotMarker = new QwtPlotMarker; + m_trackerPlotMarker->setTitle( QString( "TrackedPoint" ) ); + + QwtSymbol* symbol = new QwtSymbol( QwtSymbol::Ellipse ); + symbol->setSize( 13, 13 ); + symbol->setBrush( Qt::NoBrush ); + m_trackerPlotMarker->setSymbol( symbol ); + m_trackerPlotMarker->attach( m_qwtPlot ); + RiuGuiTheme::styleQwtItem( m_trackerPlotMarker ); + + needsReplot = true; + } + + if ( m_trackerPlotMarker->value() != samplePoint ) + { + m_trackerPlotMarker->setValue( samplePoint ); + needsReplot = true; + } + + m_trackerLabel = QString( "%1 (%2)" ).arg( samplePoint.y() ).arg( samplePoint.x() ); + if ( mixRat != HUGE_VAL ) + { + m_trackerLabel += QString( "\n%1 = %2" ).arg( mixRatioText ).arg( mixRat ); + } + } + else + { + if ( m_trackerPlotMarker ) + { + m_trackerPlotMarker->detach(); + delete m_trackerPlotMarker; + m_trackerPlotMarker = nullptr; + + needsReplot = true; + } + } + + if ( needsReplot ) + { + m_qwtPlot->replot(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const QwtPlotCurve* RiuPvtPlotWidget::closestCurveSample( const QPoint& cursorPosition, int* closestSampleIndex ) const +{ + // Construct a set containing the relevant qwt curves to consider + // These are the curves that have a corresponding Pvt source curve + std::set relevantQwtCurvesSet( m_qwtCurveArr.begin(), m_qwtCurveArr.end() ); + + if ( closestSampleIndex ) *closestSampleIndex = -1; + + const QwtPlotCurve* closestCurve = nullptr; + double distMin = HUGE_VAL; + int closestPointSampleIndex = -1; + + const QwtPlotItemList& itemList = m_qwtPlot->itemList(); + for ( QwtPlotItemIterator it = itemList.begin(); it != itemList.end(); it++ ) + { + if ( ( *it )->rtti() == QwtPlotItem::Rtti_PlotCurve ) + { + const QwtPlotCurve* curve = static_cast( *it ); + if ( relevantQwtCurvesSet.find( curve ) != relevantQwtCurvesSet.end() ) + { + double dist = HUGE_VAL; + int candidateSampleIndex = curve->closestPoint( cursorPosition, &dist ); + if ( dist < distMin ) + { + closestCurve = curve; + closestPointSampleIndex = candidateSampleIndex; + distMin = dist; + } + } + } + } + + if ( closestCurve && closestPointSampleIndex >= 0 && distMin < 50 ) + { + if ( closestSampleIndex ) *closestSampleIndex = closestPointSampleIndex; + return closestCurve; + } + else + { + return nullptr; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RiuPvtPlotWidget::indexOfQwtCurve( const QwtPlotCurve* qwtCurve ) const +{ + for ( size_t i = 0; i < m_qwtCurveArr.size(); i++ ) + { + if ( m_qwtCurveArr[i] == qwtCurve ) + { + return i; + } + } + + return cvf::UNDEFINED_SIZE_T; +} + +//-------------------------------------------------------------------------------------------------- +/// Implements the RiuPvtTrackerTextProvider interface +//-------------------------------------------------------------------------------------------------- +QString RiuPvtPlotWidget::trackerText() const +{ + return m_trackerLabel; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPvtPlotWidget::slotPickerPointChanged( const QPoint& pt ) +{ + updateTrackerPlotMarkerAndLabelFromPicker(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuPvtPlotWidget::slotPickerActivated( bool on ) +{ + updateTrackerPlotMarkerAndLabelFromPicker(); +} diff --git a/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.h b/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.h new file mode 100644 index 0000000000..4d8d4ceb40 --- /dev/null +++ b/ApplicationLibCode/UserInterface/RiuPvtPlotWidget.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017 Statoil 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 "RiaDefines.h" +#include "RigFlowDiagSolverInterface.h" + +#include +#include + +#include +#include + +class RiuDockedQwtPlot; +class RiuPvtPlotUpdater; +class RiuPvtQwtPicker; +class RiuPvtPlotPanel; + +class QComboBox; + +class QwtPlot; +class QwtPlotMarker; +class QwtPlotCurve; + +// Interface for providing our custom picker with a tracker text +class RiuPvtTrackerTextProvider +{ +public: + virtual QString trackerText() const = 0; +}; + +//================================================================================================== +// +// +// +//================================================================================================== +class RiuPvtPlotWidget : public QWidget, public RiuPvtTrackerTextProvider +{ + Q_OBJECT + +public: + RiuPvtPlotWidget( RiuPvtPlotPanel* parent ); + + void plotCurves( RiaDefines::EclipseUnitSystem unitSystem, + const std::vector& curveArr, + double pressure, + double pointMarkerYValue, + const QString& pointMarkerLabel, + const QString& plotTitle, + const QString& yAxisTitle ); + void applyFontSizes( bool replot ); + +private: + static void setPlotDefaults( QwtPlot* plot ); + const QwtPlotCurve* closestCurveSample( const QPoint& cursorPosition, int* closestSampleIndex ) const; + size_t indexOfQwtCurve( const QwtPlotCurve* qwtCurve ) const; + void updateTrackerPlotMarkerAndLabelFromPicker(); + QString trackerText() const override; + +private slots: + void slotPickerActivated( bool ); + void slotPickerPointChanged( const QPoint& pt ); + +private: + QPointer m_qwtPlot; + + std::vector m_pvtCurveArr; // Array of source Pvt curves currently being plotted + std::vector m_qwtCurveArr; // Array of corresponding qwt curves used for mapping to Pvt curve + // when doing tracking + + QPointer m_qwtPicker; + QString m_trackerLabel; + QwtPlotMarker* m_trackerPlotMarker; +}; diff --git a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.cpp b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.cpp index 0fde3bcf15..091cf5cf0b 100644 --- a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.cpp +++ b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.cpp @@ -871,6 +871,15 @@ void RiuRelativePermeabilityPlotPanel::slotSomeCheckBoxStateChanged( int ) plotUiSelectedCurves(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiuRelativePermeabilityPlotPanel::showEvent( QShowEvent* event ) +{ + if ( m_plotUpdater != nullptr ) m_plotUpdater->doDelayedUpdate(); + QWidget::showEvent( event ); +} + //================================================================================================== // // diff --git a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.h b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.h index 5a7194ec2b..32973a8502 100644 --- a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.h +++ b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotPanel.h @@ -126,6 +126,7 @@ private slots: void slotSomeCheckBoxStateChanged( int ); void slotCurrentPlotDataInTextDialog(); void slotShowCurveSelectionWidgets( int state ); + void showEvent( QShowEvent* event ) override; private: RiaDefines::EclipseUnitSystem m_unitSystem; diff --git a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.cpp b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.cpp index db004ddd4f..7cff8d79dd 100644 --- a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.cpp +++ b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.cpp @@ -52,85 +52,23 @@ //-------------------------------------------------------------------------------------------------- RiuRelativePermeabilityPlotUpdater::RiuRelativePermeabilityPlotUpdater( RiuRelativePermeabilityPlotPanel* targetPlotPanel ) : m_targetPlotPanel( targetPlotPanel ) - , m_viewToFollowAnimationFrom( nullptr ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RiuRelativePermeabilityPlotUpdater::updateOnSelectionChanged( const RiuSelectionItem* selectionItem ) +void RiuRelativePermeabilityPlotUpdater::clearPlot() { - if ( !m_targetPlotPanel ) - { - return; - } - - Rim3dView* newFollowAnimView = nullptr; - RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; - - eclipseSelectionItem = extractEclipseSelectionItem( selectionItem, newFollowAnimView ); - - bool mustClearPlot = true; - m_viewToFollowAnimationFrom = nullptr; - - if ( m_targetPlotPanel->isVisible() && eclipseSelectionItem ) - { - if ( queryDataAndUpdatePlot( eclipseSelectionItem->m_resultDefinition, - eclipseSelectionItem->m_timestepIdx, - eclipseSelectionItem->m_gridIndex, - eclipseSelectionItem->m_gridLocalCellIndex, - m_targetPlotPanel ) ) - { - mustClearPlot = false; - - m_viewToFollowAnimationFrom = newFollowAnimView; - } - } - - if ( mustClearPlot ) - { - m_targetPlotPanel->clearPlot(); - } + if ( m_targetPlotPanel ) m_targetPlotPanel->clearPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RiuRelativePermeabilityPlotUpdater::updateOnTimeStepChanged( Rim3dView* changedView ) +QWidget* RiuRelativePermeabilityPlotUpdater::plotPanel() { - if ( !m_targetPlotPanel || !m_targetPlotPanel->isVisible() ) - { - return; - } - - // Don't update the plot if the view that changed time step is different - // from the view that was the source of the current plot - - if ( changedView != m_viewToFollowAnimationFrom ) - { - return; - } - - // Fetch the current global selection and only continue if the selection's view matches the view with time step change - - const RiuSelectionItem* selectionItem = Riu3dSelectionManager::instance()->selectedItem(); - Rim3dView* newFollowAnimView = nullptr; - RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; - - eclipseSelectionItem = extractEclipseSelectionItem( selectionItem, newFollowAnimView ); - - if ( eclipseSelectionItem && newFollowAnimView == changedView ) - { - if ( !queryDataAndUpdatePlot( eclipseSelectionItem->m_resultDefinition, - newFollowAnimView->currentTimeStep(), - eclipseSelectionItem->m_gridIndex, - eclipseSelectionItem->m_gridLocalCellIndex, - m_targetPlotPanel ) ) - { - m_targetPlotPanel->clearPlot(); - } - } + return m_targetPlotPanel; } //-------------------------------------------------------------------------------------------------- @@ -139,10 +77,9 @@ void RiuRelativePermeabilityPlotUpdater::updateOnTimeStepChanged( Rim3dView* cha bool RiuRelativePermeabilityPlotUpdater::queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResDef, size_t timeStepIndex, size_t gridIndex, - size_t gridLocalCellIndex, - RiuRelativePermeabilityPlotPanel* plotPanel ) + size_t gridLocalCellIndex ) { - CVF_ASSERT( plotPanel ); + if ( m_targetPlotPanel == nullptr ) return false; if ( !eclipseResDef ) return false; @@ -207,7 +144,7 @@ bool RiuRelativePermeabilityPlotUpdater::queryDataAndUpdatePlot( const RimEclips QString cellRefText = constructCellReferenceText( eclipseCaseData, gridIndex, gridLocalCellIndex, "SATNUM", cellSATNUM ); QString caseName = eclipseResultCase->caseUserDescription(); - plotPanel->setPlotData( eclipseCaseData->unitsType(), relPermCurveArr, cellSWAT, cellSGAS, caseName, cellRefText ); + m_targetPlotPanel->setPlotData( eclipseCaseData->unitsType(), relPermCurveArr, cellSWAT, cellSGAS, caseName, cellRefText ); return true; } @@ -260,43 +197,6 @@ QString RiuRelativePermeabilityPlotUpdater::constructCellReferenceText( const Ri return QString(); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RiuEclipseSelectionItem* RiuRelativePermeabilityPlotUpdater::extractEclipseSelectionItem( const RiuSelectionItem* selectionItem, - Rim3dView*& newFollowAnimView ) -{ - newFollowAnimView = nullptr; - RiuEclipseSelectionItem* eclipseSelectionItem = nullptr; - - eclipseSelectionItem = dynamic_cast( const_cast( selectionItem ) ); - - if ( eclipseSelectionItem ) - { - // If we clicked in an eclipse view, and hit something using the standard result definition there, - // set this up to follow the animation there. - - RimEclipseView* clickedInEclView = dynamic_cast( eclipseSelectionItem->m_view.p() ); - - if ( clickedInEclView && clickedInEclView->cellResult() == eclipseSelectionItem->m_resultDefinition.p() ) - { - newFollowAnimView = eclipseSelectionItem->m_view; - } - } - else - { - auto intersectionSelItem = dynamic_cast( selectionItem ); - - if ( intersectionSelItem && intersectionSelItem->eclipseSelectionItem() ) - { - eclipseSelectionItem = intersectionSelItem->eclipseSelectionItem(); - newFollowAnimView = intersectionSelItem->view(); - } - } - - return eclipseSelectionItem; -} - //================================================================================================== // // diff --git a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.h b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.h index f6a12b5935..109415da40 100644 --- a/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.h +++ b/ApplicationLibCode/UserInterface/RiuRelativePermeabilityPlotUpdater.h @@ -18,48 +18,39 @@ #pragma once -#include -#include +#include "RiuPlotUpdater.h" -class RiuSelectionItem; class RiuRelativePermeabilityPlotPanel; -class Rim3dView; -class RimEclipseView; class RigEclipseCaseData; class RimEclipseResultDefinition; -class RiuSelectionItem; -class RiuEclipseSelectionItem; //================================================================================================== // // // //================================================================================================== -class RiuRelativePermeabilityPlotUpdater +class RiuRelativePermeabilityPlotUpdater : public RiuPlotUpdater { public: RiuRelativePermeabilityPlotUpdater( RiuRelativePermeabilityPlotPanel* targetPlotPanel ); - void updateOnSelectionChanged( const RiuSelectionItem* selectionItem ); - void updateOnTimeStepChanged( Rim3dView* changedView ); + static QString constructCellReferenceText( const RigEclipseCaseData* eclipseCaseData, + size_t gridIndex, + size_t gridLocalCellIndex, + const QString& valueName, + double cellValue ); - static RiuEclipseSelectionItem* extractEclipseSelectionItem( const RiuSelectionItem* selectionItem, Rim3dView*& newFollowAnimView ); - static QString constructCellReferenceText( const RigEclipseCaseData* eclipseCaseData, - size_t gridIndex, - size_t gridLocalCellIndex, - const QString& valueName, - double cellValue ); +protected: + void clearPlot() override; + QWidget* plotPanel() override; -private: - static bool queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResDef, - size_t timeStepIndex, - size_t gridIndex, - size_t gridLocalCellIndex, - RiuRelativePermeabilityPlotPanel* plotPanel ); + bool queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResDef, + size_t timeStepIndex, + size_t gridIndex, + size_t gridLocalCellIndex ) override; private: QPointer m_targetPlotPanel; - const Rim3dView* m_viewToFollowAnimationFrom; }; //================================================================================================== diff --git a/ApplicationLibCode/UserInterface/RiuResultQwtPlot.cpp b/ApplicationLibCode/UserInterface/RiuResultQwtPlot.cpp index 36f8b78997..ca29f89579 100644 --- a/ApplicationLibCode/UserInterface/RiuResultQwtPlot.cpp +++ b/ApplicationLibCode/UserInterface/RiuResultQwtPlot.cpp @@ -54,6 +54,7 @@ //-------------------------------------------------------------------------------------------------- RiuResultQwtPlot::RiuResultQwtPlot( QWidget* parent ) : RiuDockedQwtPlot( parent ) + , m_timeMarker( nullptr ) { setAutoFillBackground( true ); setDefaults(); @@ -133,20 +134,20 @@ void RiuResultQwtPlot::showTimeStep( const QDateTime& dateTime ) { if ( !dateTime.isValid() ) return; - auto lineMarker = new QwtPlotMarker; + if ( m_timeMarker == nullptr ) + { + m_timeMarker = new QwtPlotMarker(); + QPen pen; + pen.setStyle( Qt::DashLine ); + m_timeMarker->setLinePen( pen ); + m_timeMarker->setLineStyle( QwtPlotMarker::VLine ); + m_timeMarker->setLabel( QString( "Time Step" ) ); + m_timeMarker->setLabelAlignment( Qt::AlignTop | Qt::AlignRight ); + m_timeMarker->setLabelOrientation( Qt::Vertical ); + m_timeMarker->attach( this ); + } - QPen pen; - pen.setStyle( Qt::DashLine ); - lineMarker->setLinePen( pen ); - - lineMarker->setXValue( QwtDate::toDouble( dateTime ) ); - lineMarker->setLineStyle( QwtPlotMarker::VLine ); - lineMarker->setLabel( QString( "Time Step" ) ); - lineMarker->setLabelAlignment( Qt::AlignTop | Qt::AlignRight ); - lineMarker->setLabelOrientation( Qt::Vertical ); - lineMarker->attach( this ); - - m_plotItems.push_back( lineMarker ); + m_timeMarker->setXValue( QwtDate::toDouble( dateTime ) ); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UserInterface/RiuResultQwtPlot.h b/ApplicationLibCode/UserInterface/RiuResultQwtPlot.h index 9a9c4e241f..c525c9fd97 100644 --- a/ApplicationLibCode/UserInterface/RiuResultQwtPlot.h +++ b/ApplicationLibCode/UserInterface/RiuResultQwtPlot.h @@ -28,6 +28,7 @@ class RimCase; class QwtPlotCurve; class QwtPlotGrid; +class QwtPlotMarker; namespace cvf { @@ -77,6 +78,7 @@ private slots: private: std::vector m_plotItems; + QwtPlotMarker* m_timeMarker; std::map m_caseNames; std::map> m_timeSteps; diff --git a/ApplicationLibCode/UserInterface/RiuTimeStepChangedHandler.cpp b/ApplicationLibCode/UserInterface/RiuTimeStepChangedHandler.cpp index 1477035c9e..fb01c12526 100644 --- a/ApplicationLibCode/UserInterface/RiuTimeStepChangedHandler.cpp +++ b/ApplicationLibCode/UserInterface/RiuTimeStepChangedHandler.cpp @@ -20,7 +20,10 @@ #include "RiaGuiApplication.h" +#include "RigEclipseCaseData.h" + #include "Rim3dView.h" +#include "RimEclipseCase.h" #include "RiuMainWindow.h" #include "RiuMohrsCirclePlot.h" @@ -28,10 +31,13 @@ #include "RiuPvtPlotUpdater.h" #include "RiuRelativePermeabilityPlotPanel.h" #include "RiuRelativePermeabilityPlotUpdater.h" +#include "RiuResultQwtPlot.h" #include "cvfDebugTimer.h" #include "cvfTrace.h" +#include + //================================================================================================== /// /// \class RiuTimeStepChangedHandler @@ -69,6 +75,16 @@ void RiuTimeStepChangedHandler::handleTimeStepChanged( Rim3dView* changedView ) RiuPvtPlotUpdater* pvtPlotUpdater = RiuMainWindow::instance()->pvtPlotPanel()->plotUpdater(); pvtPlotUpdater->updateOnTimeStepChanged( changedView ); + int ts = changedView->currentTimeStep(); + + const auto dates = changedView->ownerCase()->timeStepDates(); + if ( dates.size() > (size_t)ts ) + { + auto resultPlot = RiuMainWindow::instance()->resultPlot(); + resultPlot->showTimeStep( dates[ts] ); + resultPlot->replot(); + } + RiuMohrsCirclePlot* mohrsCirclePlot = RiuMainWindow::instance()->mohrsCirclePlot(); if ( mohrsCirclePlot ) mohrsCirclePlot->updateOnTimeStepChanged( changedView ); }