From c220a345211949874d4009b3033ad284809d767b Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Wed, 15 Jul 2026 08:40:57 +0200 Subject: [PATCH] #14203 Add cumulative curve option to Grid Statistics plot A new "Show Cumulative Curve" checkbox on the grid statistics data source adds a cumulative curve on the right axis, in addition to the regular histogram curve. The cumulative curve shares the data source of its non-cumulative counterpart and follows all changes to it. The cumulative sum works with all frequency types and both graph types. Remove the legacy Grid Statistics dialog (RicGridStatisticsDialog) and its context menu command, which provided the previous cumulative display. Closes #14203. --- .../Commands/CMakeLists_files.cmake | 2 - .../RicSnapshotAllViewsToFileFeature.cpp | 10 +- .../RicSnapshotViewToClipboardFeature.cpp | 1 - .../Commands/RicGridStatisticsDialog.cpp | 332 ------------------ .../Commands/RicGridStatisticsDialog.h | 78 ---- .../Commands/RicShowGridStatisticsFeature.cpp | 66 ---- .../Commands/RicShowGridStatisticsFeature.h | 36 -- ...RimEnsembleFractureHistogramDataSource.cpp | 9 +- .../RimEnsembleFractureHistogramDataSource.h | 4 +- ...imEnsembleParameterHistogramDataSource.cpp | 9 +- .../RimEnsembleParameterHistogramDataSource.h | 4 +- ...sembleSummaryVectorHistogramDataSource.cpp | 9 +- ...EnsembleSummaryVectorHistogramDataSource.h | 4 +- .../RimGridStatisticsHistogramDataSource.cpp | 24 +- .../RimGridStatisticsHistogramDataSource.h | 7 +- .../Histogram/RimHistogramCurve.cpp | 160 ++++++++- .../Histogram/RimHistogramCurve.h | 11 + .../Histogram/RimHistogramCurveCollection.cpp | 11 + .../Histogram/RimHistogramCurveCollection.h | 3 +- .../Histogram/RimHistogramDataSource.cpp | 38 +- .../Histogram/RimHistogramDataSource.h | 15 +- .../Rim3dOverlayInfoConfig.cpp | 57 --- .../ProjectDataModel/Rim3dOverlayInfoConfig.h | 9 - .../RimContextCommandBuilder.cpp | 1 - ApplicationLibCode/UnitTests/CMakeLists.txt | 1 + .../UnitTests/RimHistogramDataSource-Test.cpp | 101 ++++++ .../UserInterface/RiuViewerCommands.cpp | 1 - 27 files changed, 366 insertions(+), 637 deletions(-) delete mode 100644 ApplicationLibCode/Commands/RicGridStatisticsDialog.cpp delete mode 100644 ApplicationLibCode/Commands/RicGridStatisticsDialog.h delete mode 100644 ApplicationLibCode/Commands/RicShowGridStatisticsFeature.cpp delete mode 100644 ApplicationLibCode/Commands/RicShowGridStatisticsFeature.h create mode 100644 ApplicationLibCode/UnitTests/RimHistogramDataSource-Test.cpp diff --git a/ApplicationLibCode/Commands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/CMakeLists_files.cmake index 5efb8ec603a..88aa11abf2f 100644 --- a/ApplicationLibCode/Commands/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/CMakeLists_files.cmake @@ -41,8 +41,6 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RicReplaceCaseFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicReloadSummaryCaseFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicFlyToObjectFeature.cpp - ${CMAKE_CURRENT_LIST_DIR}/RicGridStatisticsDialog.cpp - ${CMAKE_CURRENT_LIST_DIR}/RicShowGridStatisticsFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicRecursiveFileSearchDialog.cpp ${CMAKE_CURRENT_LIST_DIR}/RicSummaryCaseRestartDialog.cpp ${CMAKE_CURRENT_LIST_DIR}/RicImportEnsembleFeature.cpp diff --git a/ApplicationLibCode/Commands/ExportCommands/RicSnapshotAllViewsToFileFeature.cpp b/ApplicationLibCode/Commands/ExportCommands/RicSnapshotAllViewsToFileFeature.cpp index c7f0d28ab40..ee22d4b2d4d 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicSnapshotAllViewsToFileFeature.cpp +++ b/ApplicationLibCode/Commands/ExportCommands/RicSnapshotAllViewsToFileFeature.cpp @@ -24,9 +24,8 @@ #include "RiaQStringFormatter.h" #include "RiaViewRedrawScheduler.h" -#include "Rim3dOverlayInfoConfig.h" +#include "Rim3dView.h" #include "RimCase.h" -#include "RimGridView.h" #include "RimMainPlotCollection.h" #include "RimProject.h" #include "RimViewWindow.h" @@ -140,13 +139,6 @@ void RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( const QS QString absoluteFileName = caf::Utils::constructFullFileName( absSnapshotPath, fileName, ".png" ); RicSnapshotViewToFileFeature::saveSnapshotAs( absoluteFileName, riv, width, height ); - - if ( RimGridView* rigv = dynamic_cast( riv ) ) - { - QImage img = rigv->overlayInfoConfig()->statisticsDialogScreenShotImage(); - absoluteFileName = caf::Utils::constructFullFileName( absSnapshotPath, fileName + "_Statistics", ".png" ); - RicSnapshotViewToFileFeature::saveSnapshotAs( absoluteFileName, img ); - } } } diff --git a/ApplicationLibCode/Commands/ExportCommands/RicSnapshotViewToClipboardFeature.cpp b/ApplicationLibCode/Commands/ExportCommands/RicSnapshotViewToClipboardFeature.cpp index 9b72baefc55..c527068df74 100644 --- a/ApplicationLibCode/Commands/ExportCommands/RicSnapshotViewToClipboardFeature.cpp +++ b/ApplicationLibCode/Commands/ExportCommands/RicSnapshotViewToClipboardFeature.cpp @@ -17,7 +17,6 @@ ///////////////////////////////////////////////////////////////////////////////// #include "RicSnapshotViewToClipboardFeature.h" -#include "RicGridStatisticsDialog.h" #include "RiaGuiApplication.h" #include "RiaLogging.h" diff --git a/ApplicationLibCode/Commands/RicGridStatisticsDialog.cpp b/ApplicationLibCode/Commands/RicGridStatisticsDialog.cpp deleted file mode 100644 index 3c29d6ae0ed..00000000000 --- a/ApplicationLibCode/Commands/RicGridStatisticsDialog.cpp +++ /dev/null @@ -1,332 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// -// 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 "RicGridStatisticsDialog.h" -#include "ExportCommands/RicSnapshotFilenameGenerator.h" -#include "ExportCommands/RicSnapshotViewToClipboardFeature.h" -#include "ExportCommands/RicSnapshotViewToFileFeature.h" - -#include "RiaPreferences.h" - -#include "Rim3dOverlayInfoConfig.h" -#include "RimEclipseView.h" - -#include "RiuPlotMainWindow.h" -#include "RiuQwtPlotTools.h" -#include "RiuSummaryQwtPlot.h" -#include "RiuTools.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RicGridStatisticsDialog::RicGridStatisticsDialog( QWidget* parent ) - : QDialog( parent, RiuTools::defaultDialogFlags() ) -{ - m_currentRimView = nullptr; - - // Create widgets - m_toolBar = new QToolBar(); - m_mainViewWidget = new QFrame(); - m_label = new QLabel(); - m_textEdit = new QTextEdit(); - m_historgramPlot = new QwtPlot(); - m_aggregatedPlot = new QwtPlot(); - m_buttons = new QDialogButtonBox( QDialogButtonBox::Close ); - - // Connect to close button signal - connect( m_buttons, SIGNAL( rejected() ), this, SLOT( slotDialogFinished() ) ); - - // Set widget properties - m_textEdit->setReadOnly( true ); - - RiuQwtPlotTools::setCommonPlotBehaviour( m_historgramPlot ); - RiuQwtPlotTools::setCommonPlotBehaviour( m_aggregatedPlot ); - - // Define layout - QVBoxLayout* dialogLayout = new QVBoxLayout(); - dialogLayout->addWidget( m_mainViewWidget ); - - QVBoxLayout* mainViewLayout = new QVBoxLayout(); - mainViewLayout->setContentsMargins( 0, 0, 0, 0 ); - m_mainViewWidget->setLayout( mainViewLayout ); - mainViewLayout->addWidget( m_label ); - mainViewLayout->addWidget( m_textEdit ); - - QVBoxLayout* plotLayout = new QVBoxLayout(); - plotLayout->setSpacing( 0 ); - plotLayout->addWidget( m_historgramPlot ); - plotLayout->addWidget( m_aggregatedPlot ); - mainViewLayout->addLayout( plotLayout ); - - dialogLayout->addWidget( m_buttons ); - - setLayout( dialogLayout ); - - // Toolbar - dialogLayout->setMenuBar( m_toolBar ); - createAndConnectToolbarActions(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RicGridStatisticsDialog::~RicGridStatisticsDialog() -{ -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::setLabel( const QString& labelText ) -{ - m_label->setText( labelText ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::updateFromRimView( RimGridView* rimView ) -{ - m_currentRimView = rimView; - setInfoText( m_currentRimView ); - setHistogramData( m_currentRimView ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QImage RicGridStatisticsDialog::screenShotImage() -{ - QPixmap shot = m_mainViewWidget->grab(); - - return shot.toImage(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::setInfoText( RimGridView* view ) -{ - if ( view && view->overlayInfoConfig() ) - { - Rim3dOverlayInfoConfig* overlayInfo = view->overlayInfoConfig(); - - QString text; - text = overlayInfo->timeStepText(); - text += overlayInfo->caseInfoText(); - text += overlayInfo->resultInfoText( overlayInfo->histogramData() ); - m_textEdit->setText( text ); - adjustTextEditHeightToContent(); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::setHistogramData( RimGridView* view ) -{ - deletePlotItems( m_historgramPlot ); - deletePlotItems( m_aggregatedPlot ); - - if ( view && view->overlayInfoConfig() ) - { - Rim3dOverlayInfoConfig* overlayInfo = view->overlayInfoConfig(); - - RigHistogramData histogramData = overlayInfo->histogramData(); - - if ( histogramData.isHistogramVectorValid() ) - { - auto hist = new QwtPlotHistogram( "Histogram" ); - auto aggr = new QwtPlotCurve( "Aggregated" ); - - hist->setBrush( QBrush( QColor( Qt::darkCyan ) ) ); - hist->setZ( -1 ); - aggr->setStyle( QwtPlotCurve::Steps ); - aggr->setCurveAttribute( QwtPlotCurve::Inverted ); - - QVector histSamples; - QVector aggrSamples; - double xStep = ( histogramData.max - histogramData.min ) / histogramData.histogram.size(); - double xCurr = histogramData.min; - double aggrValue = 0.0; - for ( size_t value : histogramData.histogram ) - { - double xNext = xCurr + xStep; - histSamples.push_back( QwtIntervalSample( value, xCurr, xNext ) ); - - aggrValue += value; - aggrSamples.push_back( QPointF( xCurr, aggrValue ) ); - - xCurr = xNext; - } - - // Axis - double xAxisSize = histogramData.max - histogramData.min; - double xAxisExtension = xAxisSize * 0.02; - m_historgramPlot->setAxisScale( QwtAxis::XBottom, histogramData.min - xAxisExtension, histogramData.max + xAxisExtension ); - m_aggregatedPlot->setAxisScale( QwtAxis::XBottom, histogramData.min - xAxisExtension, histogramData.max + xAxisExtension ); - - // Set y axis label area width - m_historgramPlot->axisScaleDraw( QwtAxis::YLeft )->setMinimumExtent( 60 ); - m_aggregatedPlot->axisScaleDraw( QwtAxis::YLeft )->setMinimumExtent( 60 ); - - // Samples - hist->setSamples( histSamples ); - aggr->setSamples( aggrSamples ); - hist->attach( m_historgramPlot ); - aggr->attach( m_aggregatedPlot ); - - // Markers - setMarkers( histogramData, m_historgramPlot ); - setMarkers( histogramData, m_aggregatedPlot ); - } - } - - // Refresh plot - m_historgramPlot->replot(); - m_aggregatedPlot->replot(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::createAndConnectToolbarActions() -{ - QAction* scrShotToClipboardAction = - m_toolBar->addAction( RicSnapshotViewToClipboardFeature::icon(), RicSnapshotViewToClipboardFeature::text() ); - connect( scrShotToClipboardAction, SIGNAL( triggered() ), this, SLOT( slotScreenShotToClipboard() ) ); - - QAction* scrShotToFileAction = m_toolBar->addAction( RicSnapshotViewToFileFeature::icon(), RicSnapshotViewToFileFeature::text() ); - connect( scrShotToFileAction, SIGNAL( triggered() ), this, SLOT( slotScreenShotToFile() ) ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::deletePlotItems( QwtPlot* plot ) -{ - QwtPlotItemList itemList = plot->itemList(); - for ( auto item : itemList ) - { - if ( dynamic_cast( item ) || dynamic_cast( item ) || dynamic_cast( item ) ) - { - item->detach(); - delete item; - } - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::setMarkers( const RigHistogramData& histData, QwtPlot* plot ) -{ - QwtPlotMarker* marker; - - if ( histData.p10 != HUGE_VAL ) - { - marker = createVerticalPlotMarker( Qt::red, histData.p10 ); - marker->attach( plot ); - } - - if ( histData.p90 != HUGE_VAL ) - { - marker = createVerticalPlotMarker( Qt::red, histData.p90 ); - marker->attach( plot ); - } - - if ( histData.mean != HUGE_VAL ) - { - marker = createVerticalPlotMarker( Qt::blue, histData.mean ); - marker->attach( plot ); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QwtPlotMarker* RicGridStatisticsDialog::createVerticalPlotMarker( const QColor& color, double xValue ) -{ - QwtPlotMarker* marker = new QwtPlotMarker(); - marker->setXValue( xValue ); - marker->setLineStyle( QwtPlotMarker::VLine ); - marker->setLinePen( color, 2, Qt::SolidLine ); - return marker; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::adjustTextEditHeightToContent() -{ - int docHeight = m_textEdit->document()->size().height(); - m_textEdit->setFixedHeight( docHeight + 10 ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::slotDialogFinished() -{ - close(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::slotScreenShotToClipboard() -{ - QImage snapshotImage = screenShotImage(); - RicSnapshotViewToClipboardFeature::copyToClipboard( snapshotImage ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicGridStatisticsDialog::slotScreenShotToFile() -{ - QImage snapshotImage = screenShotImage(); - QString defaultFileBaseName; - if ( m_currentRimView ) - { - defaultFileBaseName = RicSnapshotFilenameGenerator::generateSnapshotFileName( m_currentRimView ); - defaultFileBaseName += "_Statistics"; - } - else - { - defaultFileBaseName = "Snapshot_Statistics"; - } - RicSnapshotViewToFileFeature::saveImageToFile( snapshotImage, defaultFileBaseName ); -} diff --git a/ApplicationLibCode/Commands/RicGridStatisticsDialog.h b/ApplicationLibCode/Commands/RicGridStatisticsDialog.h deleted file mode 100644 index 60e560c1064..00000000000 --- a/ApplicationLibCode/Commands/RicGridStatisticsDialog.h +++ /dev/null @@ -1,78 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// -// 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 "cafPdmPointer.h" - -#include - -class QLabel; -class QTextEdit; -class QDialogButtonBox; -class QwtPlot; -class QwtPlotMarker; -class QMainWindow; -class QToolBar; -class RimEclipseView; -class RimGridView; -class RigHistogramData; - -//================================================================================================== -/// -/// -//================================================================================================== -class RicGridStatisticsDialog : public QDialog -{ - Q_OBJECT - -public: - explicit RicGridStatisticsDialog( QWidget* parent ); - ~RicGridStatisticsDialog() override; - - void setLabel( const QString& labelText ); - void updateFromRimView( RimGridView* rimView ); - QImage screenShotImage(); - -private: - void setInfoText( RimGridView* eclipseView ); - void setHistogramData( RimGridView* eclipseView ); - -private: - void createAndConnectToolbarActions(); - void deletePlotItems( QwtPlot* plot ); - static void setMarkers( const RigHistogramData& histData, QwtPlot* plot ); - static QwtPlotMarker* createVerticalPlotMarker( const QColor& color, double xValue ); - void adjustTextEditHeightToContent(); - -private slots: - void slotDialogFinished(); - void slotScreenShotToClipboard(); - void slotScreenShotToFile(); - -private: - QToolBar* m_toolBar; - QWidget* m_mainViewWidget; - QLabel* m_label; - QTextEdit* m_textEdit; - QwtPlot* m_historgramPlot; - QwtPlot* m_aggregatedPlot; - QDialogButtonBox* m_buttons; - - caf::PdmPointer m_currentRimView; -}; diff --git a/ApplicationLibCode/Commands/RicShowGridStatisticsFeature.cpp b/ApplicationLibCode/Commands/RicShowGridStatisticsFeature.cpp deleted file mode 100644 index b188b1e2512..00000000000 --- a/ApplicationLibCode/Commands/RicShowGridStatisticsFeature.cpp +++ /dev/null @@ -1,66 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// -// 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 "RicShowGridStatisticsFeature.h" - -#include "RiaApplication.h" -#include "RicGridStatisticsDialog.h" -#include "RicWellLogTools.h" - -#include "Rim3dOverlayInfoConfig.h" -#include "RimEclipseView.h" -#include "RimGeoMechView.h" -#include "RimProject.h" - -#include "cafSelectionManagerTools.h" - -#include - -CAF_CMD_SOURCE_INIT( RicShowGridStatisticsFeature, "RicShowGridStatisticsFeature" ); - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RicShowGridStatisticsFeature::isCommandEnabled() const -{ - if ( RicWellLogTools::isWellPathOrSimWellSelectedInView() ) return false; - - return RiaApplication::instance()->activeGridView() != nullptr; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicShowGridStatisticsFeature::onActionTriggered( bool isChecked ) -{ - RimGridView* activeView = RiaApplication::instance()->activeMainOrComparisonGridView(); - - if ( activeView ) - { - activeView->overlayInfoConfig()->showStatisticsInfoDialog(); - } -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RicShowGridStatisticsFeature::setupActionLook( QAction* actionToSetup ) -{ - actionToSetup->setText( "Grid Statistics (legacy)" ); - actionToSetup->setIcon( QIcon( ":/statistics.png" ) ); // Todo: Change icon -} diff --git a/ApplicationLibCode/Commands/RicShowGridStatisticsFeature.h b/ApplicationLibCode/Commands/RicShowGridStatisticsFeature.h deleted file mode 100644 index c52bb5b0c24..00000000000 --- a/ApplicationLibCode/Commands/RicShowGridStatisticsFeature.h +++ /dev/null @@ -1,36 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// -// 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 "cafCmdFeature.h" - -class RicGridStatisticsDialog; - -//================================================================================================== -/// -//================================================================================================== -class RicShowGridStatisticsFeature : public caf::CmdFeature -{ - CAF_CMD_HEADER_INIT; - -protected: - bool isCommandEnabled() const override; - void onActionTriggered( bool isChecked ) override; - void setupActionLook( QAction* actionToSetup ) override; -}; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.cpp b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.cpp index aff494a5c3c..76f00fdc3a4 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.cpp +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.cpp @@ -111,8 +111,9 @@ std::string RimEnsembleFractureHistogramDataSource::unitNameX() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimHistogramDataSource::HistogramResult RimEnsembleFractureHistogramDataSource::compute( RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ) const +RimHistogramDataSource::HistogramResult RimEnsembleFractureHistogramDataSource::compute( RimHistogramPlot::GraphType graphType, + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative ) const { RimHistogramDataSource::HistogramResult result; @@ -126,8 +127,8 @@ RimHistogramDataSource::HistogramResult RimEnsembleFractureHistogramDataSource:: double min = histogramData.min; double max = histogramData.max; - result.valuesX = computeHistogramBins( min, max, m_numBins, graphType ); - result.valuesY = computeHistogramFrequencies( histogramData.histogram, graphType, frequencyType ); + result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative ); + result.valuesY = computeHistogramFrequencies( histogramData.histogram, graphType, frequencyType, cumulative ); result.p10 = histogramData.p10; result.mean = histogramData.mean; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.h b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.h index a1a5e52afc5..d0d3e5141a7 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.h +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleFractureHistogramDataSource.h @@ -45,7 +45,9 @@ class RimEnsembleFractureHistogramDataSource : public RimHistogramDataSource std::string unitNameX() const override; std::string unitNameY() const override; - HistogramResult compute( RimHistogramPlot::GraphType graphType, RimHistogramPlot::FrequencyType frequencyType ) const override; + HistogramResult compute( RimHistogramPlot::GraphType graphType, + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative = false ) const override; void setDefaults() override; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.cpp b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.cpp index 66cc0726ceb..cd1c7279555 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.cpp +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.cpp @@ -147,8 +147,9 @@ std::string RimEnsembleParameterHistogramDataSource::unitNameX() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimHistogramDataSource::HistogramResult RimEnsembleParameterHistogramDataSource::compute( RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ) const +RimHistogramDataSource::HistogramResult RimEnsembleParameterHistogramDataSource::compute( RimHistogramPlot::GraphType graphType, + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative ) const { RimHistogramDataSource::HistogramResult result; @@ -159,7 +160,7 @@ RimHistogramDataSource::HistogramResult RimEnsembleParameterHistogramDataSource: double min = parameter.minValue; double max = parameter.maxValue; - result.valuesX = computeHistogramBins( min, max, m_numBins, graphType ); + result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative ); std::vector values; for ( const QVariant& v : parameter.values ) @@ -171,7 +172,7 @@ RimHistogramDataSource::HistogramResult RimEnsembleParameterHistogramDataSource: RigHistogramCalculator histCalc( min, max, m_numBins, &histogram ); histCalc.addData( values ); - result.valuesY = computeHistogramFrequencies( histogram, graphType, frequencyType ); + result.valuesY = computeHistogramFrequencies( histogram, graphType, frequencyType, cumulative ); double p10, p50, p90, mean; RigStatisticsMath::calculateStatisticsCurves( values, &p10, &p50, &p90, &mean, RigStatisticsMath::PercentileStyle::SWITCHED ); diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.h b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.h index 44807cb83da..464e33af447 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.h +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleParameterHistogramDataSource.h @@ -46,7 +46,9 @@ class RimEnsembleParameterHistogramDataSource : public RimHistogramDataSource std::string unitNameX() const override; std::string unitNameY() const override; - HistogramResult compute( RimHistogramPlot::GraphType graphType, RimHistogramPlot::FrequencyType frequencyType ) const override; + HistogramResult compute( RimHistogramPlot::GraphType graphType, + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative = false ) const override; void setDefaults() override; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.cpp b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.cpp index 265176d0611..4aeea610779 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.cpp +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.cpp @@ -229,8 +229,9 @@ std::vector RimEnsembleSummaryVectorHistogramDataSource::extractValuesFr //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimHistogramDataSource::HistogramResult RimEnsembleSummaryVectorHistogramDataSource::compute( RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ) const +RimHistogramDataSource::HistogramResult RimEnsembleSummaryVectorHistogramDataSource::compute( RimHistogramPlot::GraphType graphType, + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative ) const { RimHistogramDataSource::HistogramResult result; @@ -241,13 +242,13 @@ RimHistogramDataSource::HistogramResult RimEnsembleSummaryVectorHistogramDataSou double min = *min_it; double max = *max_it; - result.valuesX = computeHistogramBins( min, max, m_numBins, graphType ); + result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative ); std::vector histogram; RigHistogramCalculator histCalc( min, max, m_numBins, &histogram ); histCalc.addData( values ); - result.valuesY = computeHistogramFrequencies( histogram, graphType, frequencyType ); + result.valuesY = computeHistogramFrequencies( histogram, graphType, frequencyType, cumulative ); double p10, p50, p90, mean; RigStatisticsMath::calculateStatisticsCurves( values, &p10, &p50, &p90, &mean, RigStatisticsMath::PercentileStyle::SWITCHED ); diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.h b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.h index 27c9cfef178..5e49c406a1a 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.h +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.h @@ -48,7 +48,9 @@ class RimEnsembleSummaryVectorHistogramDataSource : public RimHistogramDataSourc std::string unitNameX() const override; std::string unitNameY() const override; - HistogramResult compute( RimHistogramPlot::GraphType graphType, RimHistogramPlot::FrequencyType frequencyType ) const override; + HistogramResult compute( RimHistogramPlot::GraphType graphType, + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative = false ) const override; void setDefaults() override; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.cpp b/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.cpp index e8b7f885a7e..d4487de66c8 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.cpp +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.cpp @@ -51,6 +51,15 @@ RimGridStatisticsHistogramDataSource::RimGridStatisticsHistogramDataSource() m_property->setTernaryEnabled( false ); CAF_PDM_InitField( &m_numBins, "NumBins", 15, "Number of Bins" ); + CAF_PDM_InitField( &m_cumulative, "Cumulative", false, "Show Cumulative Curve" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimGridStatisticsHistogramDataSource::showCumulativeCurve() const +{ + return m_cumulative(); } //-------------------------------------------------------------------------------------------------- @@ -107,6 +116,7 @@ void RimGridStatisticsHistogramDataSource ::defineUiOrdering( QString uiConfigNa { uiOrdering.add( &m_timeStep ); uiOrdering.add( &m_cellFilterView ); + uiOrdering.add( &m_cumulative ); caf::PdmUiGroup* propertyGroup = uiOrdering.addNewGroup( "Property" ); m_property->uiOrdering( uiConfigName, *propertyGroup ); } @@ -132,6 +142,13 @@ void RimGridStatisticsHistogramDataSource::fieldChangedByUi( const caf::PdmField dataSourceChanged.send(); } } + else if ( changedField == &m_cumulative ) + { + // Handled by the owning curve, which creates or deletes the cumulative companion curve. + // Not sent as dataSourceChanged: the companion curve observes that signal, and must not be + // deleted while it is being emitted. + cumulativeChanged.send(); + } else { dataSourceChanged.send(); @@ -158,7 +175,8 @@ std::string RimGridStatisticsHistogramDataSource::unitNameX() const /// //-------------------------------------------------------------------------------------------------- RimHistogramDataSource::HistogramResult RimGridStatisticsHistogramDataSource::compute( RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ) const + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative ) const { RimHistogramDataSource::HistogramResult result; @@ -168,8 +186,8 @@ RimHistogramDataSource::HistogramResult RimGridStatisticsHistogramDataSource::co double min = histogramData.min; double max = histogramData.max; - result.valuesX = computeHistogramBins( min, max, m_numBins, graphType ); - result.valuesY = computeHistogramFrequencies( histogramData.histogram, graphType, frequencyType ); + result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative ); + result.valuesY = computeHistogramFrequencies( histogramData.histogram, graphType, frequencyType, cumulative ); result.p10 = histogramData.p10; result.mean = histogramData.mean; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.h b/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.h index 4f398b1b63e..3347701f2be 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.h +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimGridStatisticsHistogramDataSource.h @@ -45,7 +45,11 @@ class RimGridStatisticsHistogramDataSource : public RimHistogramDataSource std::string unitNameX() const override; std::string unitNameY() const override; - HistogramResult compute( RimHistogramPlot::GraphType graphType, RimHistogramPlot::FrequencyType frequencyType ) const override; + HistogramResult compute( RimHistogramPlot::GraphType graphType, + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative = false ) const override; + + bool showCumulativeCurve() const override; std::string name() const override; @@ -70,4 +74,5 @@ class RimGridStatisticsHistogramDataSource : public RimHistogramDataSource caf::PdmPtrField m_cellFilterView; caf::PdmChildField m_property; caf::PdmField m_numBins; + caf::PdmField m_cumulative; }; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.cpp b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.cpp index a07dd46a1f9..7b5a5134f94 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.cpp @@ -54,6 +54,12 @@ RimHistogramCurve::RimHistogramCurve() CAF_PDM_InitFieldNoDefault( &m_dataSource, "DataSource", "Data Source" ); m_dataSource.uiCapability()->setUiTreeHidden( true ); + CAF_PDM_InitFieldNoDefault( &m_dataSourceReference, "DataSourceReference", "Data Source Reference" ); + m_dataSourceReference.uiCapability()->setUiHidden( true ); + + CAF_PDM_InitField( &m_isCumulative, "IsCumulative", false, "Cumulative" ); + m_isCumulative.uiCapability()->setUiHidden( true ); + CAF_PDM_InitField( &m_showP10Curve, "ShowP10Curve", true, "P10" ); CAF_PDM_InitField( &m_showP90Curve, "ShowP90Curve", true, "P90" ); CAF_PDM_InitField( &m_showMeanCurve, "ShowMeanCurve", true, "Mean" ); @@ -83,6 +89,9 @@ RimHistogramCurve::~RimHistogramCurve() void RimHistogramCurve::initAfterRead() { setDataSource( m_dataSource ); + + // NB: m_dataSourceReference is not resolved yet. The signal connection for referenced data + // sources is established in onLoadDataAndUpdate(). } //-------------------------------------------------------------------------------------------------- @@ -107,15 +116,64 @@ void RimHistogramCurve::setDataSource( RimHistogramDataSource* dataSource ) { m_dataSource->uiCapability()->setUiTreeHidden( true ); m_dataSource->dataSourceChanged.connect( this, &RimHistogramCurve::onDataSourceChanged ); + m_dataSource->cumulativeChanged.connect( this, &RimHistogramCurve::onCumulativeChanged ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// Use the data source owned by another curve. Used by cumulative curves, which share the data +/// source of their non-cumulative counterpart. +//-------------------------------------------------------------------------------------------------- +void RimHistogramCurve::setDataSourceReference( RimHistogramDataSource* dataSource ) +{ + m_dataSourceReference = dataSource; + connectReferencedDataSourceSignals(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimHistogramCurve::connectReferencedDataSourceSignals() +{ + if ( m_dataSourceReference() ) + { + // Connecting an already connected observer is a no-op + m_dataSourceReference()->dataSourceChanged.connect( this, &RimHistogramCurve::onDataSourceChanged ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimHistogramCurve::setCumulative( bool cumulative ) +{ + m_isCumulative = cumulative; + + if ( cumulative ) + { + // The percentile annotations are shown by the non-cumulative counterpart curve + m_showP10Curve = false; + m_showP90Curve = false; + m_showMeanCurve = false; } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimHistogramCurve::isCumulative() const +{ + return m_isCumulative(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimHistogramDataSource* RimHistogramCurve::dataSource() const { - return m_dataSource; + if ( m_dataSource() ) return m_dataSource(); + + return m_dataSourceReference(); } //-------------------------------------------------------------------------------------------------- @@ -134,8 +192,8 @@ RiuPlotAxis RimHistogramCurve::axisX() const //-------------------------------------------------------------------------------------------------- std::string RimHistogramCurve::unitNameY() const { - if ( m_dataSource ) - return m_dataSource()->unitNameY(); + if ( dataSource() ) + return dataSource()->unitNameY(); else return ""; } @@ -145,8 +203,8 @@ std::string RimHistogramCurve::unitNameY() const //-------------------------------------------------------------------------------------------------- std::string RimHistogramCurve::unitNameX() const { - if ( m_dataSource ) - return m_dataSource()->unitNameX(); + if ( dataSource() ) + return dataSource()->unitNameX(); else return ""; } @@ -156,10 +214,10 @@ std::string RimHistogramCurve::unitNameX() const //-------------------------------------------------------------------------------------------------- std::vector RimHistogramCurve::valuesY() const { - if ( m_dataSource ) + if ( dataSource() ) { RimHistogramPlot* plot = firstAncestorOrThisOfTypeAsserted(); - return m_dataSource()->compute( plot->graphType(), plot->frequencyType() ).valuesY; + return dataSource()->compute( plot->graphType(), plot->frequencyType(), m_isCumulative() ).valuesY; } else return {}; @@ -170,10 +228,10 @@ std::vector RimHistogramCurve::valuesY() const //-------------------------------------------------------------------------------------------------- std::vector RimHistogramCurve::valuesX() const { - if ( m_dataSource ) + if ( dataSource() ) { RimHistogramPlot* plot = firstAncestorOrThisOfTypeAsserted(); - return m_dataSource()->compute( plot->graphType(), plot->frequencyType() ).valuesX; + return dataSource()->compute( plot->graphType(), plot->frequencyType(), m_isCumulative() ).valuesX; } else return {}; @@ -237,9 +295,10 @@ QList RimHistogramCurve::calculateValueOptions( const ca QString RimHistogramCurve::createCurveAutoName() { QString curveName = ""; - if ( m_dataSource ) + if ( dataSource() ) { - curveName = QString::fromStdString( m_dataSource->name() ); + curveName = QString::fromStdString( dataSource()->name() ); + if ( m_isCumulative() ) curveName += ", Cumulative"; } if ( curveName.isEmpty() ) @@ -271,11 +330,13 @@ void RimHistogramCurve::onLoadDataAndUpdate( bool updateParentPlot ) m_annotationTool->detachAllAnnotations(); - if ( isChecked() && m_dataSource() ) + connectReferencedDataSourceSignals(); + + if ( isChecked() && dataSource() ) { RimHistogramPlot* plot = firstAncestorOrThisOfTypeAsserted(); - auto result = m_dataSource->compute( plot->graphType(), plot->frequencyType() ); + auto result = dataSource()->compute( plot->graphType(), plot->frequencyType(), m_isCumulative() ); bool useLogarithmicScale = plot->isLogarithmicScaleEnabled( axisY() ); @@ -486,11 +547,84 @@ void RimHistogramCurve::onDataSourceChanged( const caf::SignalEmitter* emitter ) loadAndUpdateDataAndPlot(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimHistogramCurve::onCumulativeChanged( const caf::SignalEmitter* emitter ) +{ + updateCumulativeCurve(); +} + +//-------------------------------------------------------------------------------------------------- +/// Create or delete the cumulative companion curve to match the data source setting. The companion +/// curve shares this curve's data source and is shown on the right axis. +//-------------------------------------------------------------------------------------------------- +void RimHistogramCurve::updateCumulativeCurve() +{ + if ( m_isCumulative() || !m_dataSource() ) return; + + auto plot = firstAncestorOrThisOfType(); + if ( !plot ) return; + + RimHistogramCurve* cumulativeCurve = nullptr; + for ( RimHistogramCurve* curve : plot->histogramCurves() ) + { + if ( curve != this && curve->isCumulative() && curve->dataSource() == m_dataSource() ) + { + cumulativeCurve = curve; + break; + } + } + + bool showCumulativeCurve = m_dataSource->showCumulativeCurve(); + if ( showCumulativeCurve && !cumulativeCurve ) + { + auto newCurve = new RimHistogramCurve(); + newCurve->setCumulative( true ); + newCurve->setDataSourceReference( m_dataSource() ); + newCurve->setColor( color() ); + newCurve->setFillStyle( Qt::NoBrush ); + + plot->addCurveNoUpdate( newCurve, false ); + + if ( !plot->axisPropertiesForPlotAxis( RiuPlotAxis::defaultRight() ) ) + { + plot->addNewAxisProperties( RiuPlotAxis::defaultRight(), "Right" ); + } + if ( plot->plotWidget() ) plot->plotWidget()->ensureAxisIsCreated( RiuPlotAxis::defaultRight() ); + newCurve->setLeftOrRightAxisY( RiuPlotAxis::defaultRight() ); + newCurve->setTopOrBottomAxisX( RiuPlotAxis::defaultBottom() ); + + newCurve->loadDataAndUpdate( true ); + plot->updateAxes(); + plot->updateConnectedEditors(); + } + else if ( !showCumulativeCurve && cumulativeCurve ) + { + auto curveCollection = cumulativeCurve->firstAncestorOrThisOfType(); + if ( curveCollection ) + { + curveCollection->deleteCurve( cumulativeCurve ); + plot->updateAxes(); + plot->updateConnectedEditors(); + plot->scheduleReplotIfVisible(); + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimHistogramCurve::setAppearanceFromGraphType( RimHistogramPlot::GraphType graphType ) { + if ( m_isCumulative() ) + { + // Cumulative curves are always drawn as lines, also when the histogram is a bar graph + setFillStyle( Qt::NoBrush ); + setFillColorOpacity( 1.0 ); + return; + } + auto fillType = graphType == RimHistogramPlot::GraphType::BAR_GRAPH ? Qt::SolidPattern : Qt::NoBrush; setFillStyle( fillType ); float opacity = graphType == RimHistogramPlot::GraphType::BAR_GRAPH ? 0.2 : 1.0; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.h b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.h index 139eb934390..12d5193cf7d 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.h +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurve.h @@ -58,8 +58,12 @@ class RimHistogramCurve : public RimStackablePlotCurve void updateLegendEntryVisibilityNoPlotUpdate() override; void setDataSource( RimHistogramDataSource* dataSource ); + void setDataSourceReference( RimHistogramDataSource* dataSource ); void setAppearanceFromGraphType( RimHistogramPlot::GraphType graphType ); + void setCumulative( bool cumulative ); + bool isCumulative() const; + RimHistogramDataSource* dataSource() const; protected: @@ -83,6 +87,10 @@ class RimHistogramCurve : public RimStackablePlotCurve void hideXAxisGroup(); void onDataSourceChanged( const caf::SignalEmitter* emitter ); + void onCumulativeChanged( const caf::SignalEmitter* emitter ); + + void updateCumulativeCurve(); + void connectReferencedDataSourceSignals(); void initAfterRead() override; @@ -91,6 +99,9 @@ class RimHistogramCurve : public RimStackablePlotCurve caf::PdmPtrField m_xPlotAxisProperties; caf::PdmChildField m_dataSource; + caf::PdmPtrField m_dataSourceReference; + + caf::PdmField m_isCumulative; caf::PdmField m_showP10Curve; caf::PdmField m_showP90Curve; diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.cpp b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.cpp index 34a6841c12f..4cac885719f 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.cpp @@ -262,6 +262,17 @@ void RimHistogramCurveCollection::onCurvesReordered( const SignalEmitter* emitte void RimHistogramCurveCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector& referringObjects ) { + // Delete cumulative curves when the curve owning their referenced data source is deleted + std::vector orphanedCurves; + for ( RimHistogramCurve* curve : curves() ) + { + if ( curve->isCumulative() && !curve->dataSource() ) orphanedCurves.push_back( curve ); + } + for ( RimHistogramCurve* curve : orphanedCurves ) + { + deleteCurve( curve ); + } + curvesChanged.send(); } diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.h b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.h index 43645d003ae..700b839aad4 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.h +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramCurveCollection.h @@ -50,6 +50,8 @@ class RimHistogramCurveCollection : public caf::PdmObject void updateCurveOrder(); + void deleteCurve( RimHistogramCurve* curve ); + private: void setParentPlotAndReplot( RiuPlotWidget* plot ); void setParentPlotNoReplot( RiuPlotWidget* plot ); @@ -58,7 +60,6 @@ class RimHistogramCurveCollection : public caf::PdmObject void addCurve( RimHistogramCurve* curve ); void insertCurve( RimHistogramCurve* curve, size_t index ); - void deleteCurve( RimHistogramCurve* curve ); void removeCurve( RimHistogramCurve* curve ); void deleteAllCurves(); diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.cpp b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.cpp index fefe5592a33..414c591ca9c 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.cpp +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.cpp @@ -25,6 +25,7 @@ CAF_PDM_XML_ABSTRACT_SOURCE_INIT( RimHistogramDataSource, "HistogramDataSource" //-------------------------------------------------------------------------------------------------- RimHistogramDataSource::RimHistogramDataSource() : dataSourceChanged( this ) + , cumulativeChanged( this ) { CAF_PDM_InitObject( "Histogram Data Source", ); } @@ -39,7 +40,16 @@ RimHistogramDataSource::~RimHistogramDataSource() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::vector RimHistogramDataSource::computeHistogramBins( double min, double max, int numBins, RimHistogramPlot::GraphType graphType ) +bool RimHistogramDataSource::showCumulativeCurve() const +{ + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector + RimHistogramDataSource::computeHistogramBins( double min, double max, int numBins, RimHistogramPlot::GraphType graphType, bool cumulative ) { const double binSize = ( max - min ) / numBins; @@ -57,8 +67,9 @@ std::vector RimHistogramDataSource::computeHistogramBins( double min, do values.push_back( binMin ); values.push_back( binMax ); - // Close last bar on right side - if ( i == numBins - 1 ) values.push_back( binMax ); + // Close last bar on right side. A cumulative curve is not closed: it should end at its + // maximum instead of dropping back to zero. + if ( i == numBins - 1 && !cumulative ) values.push_back( binMax ); } else if ( graphType == RimHistogramPlot::GraphType::LINE_GRAPH ) { @@ -74,10 +85,11 @@ std::vector RimHistogramDataSource::computeHistogramBins( double min, do //-------------------------------------------------------------------------------------------------- std::vector RimHistogramDataSource::computeHistogramFrequencies( const std::vector& values, RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ) + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative ) { std::vector valuesAsDouble( values.begin(), values.end() ); - return computeHistogramFrequencies( valuesAsDouble, graphType, frequencyType ); + return computeHistogramFrequencies( valuesAsDouble, graphType, frequencyType, cumulative ); } //-------------------------------------------------------------------------------------------------- @@ -85,12 +97,15 @@ std::vector RimHistogramDataSource::computeHistogramFrequencies( const s //-------------------------------------------------------------------------------------------------- std::vector RimHistogramDataSource::computeHistogramFrequencies( const std::vector& values, RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ) + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative ) { double sumElements = 0.0; for ( double value : values ) sumElements += value; + double runningSum = 0.0; + std::vector frequencies; for ( size_t i = 0; i < values.size(); i++ ) { @@ -98,6 +113,12 @@ std::vector RimHistogramDataSource::computeHistogramFrequencies( const s if ( frequencyType == RimHistogramPlot::FrequencyType::RELATIVE_FREQUENCY ) value /= sumElements; if ( frequencyType == RimHistogramPlot::FrequencyType::RELATIVE_FREQUENCY_PERCENT ) value = value / sumElements * 100.0; + if ( cumulative ) + { + runningSum += value; + value = runningSum; + } + if ( graphType == RimHistogramPlot::GraphType::BAR_GRAPH ) { // Close first bar on left side @@ -106,8 +127,9 @@ std::vector RimHistogramDataSource::computeHistogramFrequencies( const s frequencies.push_back( value ); frequencies.push_back( value ); - // Close last bar on right side - if ( i == values.size() - 1 ) frequencies.push_back( 0.0 ); + // Close last bar on right side. A cumulative curve is not closed: it should end at its + // maximum instead of dropping back to zero. + if ( i == values.size() - 1 && !cumulative ) frequencies.push_back( 0.0 ); } else if ( graphType == RimHistogramPlot::GraphType::LINE_GRAPH ) { diff --git a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.h b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.h index 78cb67b01eb..e44af82ddcd 100644 --- a/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.h +++ b/ApplicationLibCode/ProjectDataModel/Histogram/RimHistogramDataSource.h @@ -53,21 +53,28 @@ class RimHistogramDataSource : public caf::PdmObject ~RimHistogramDataSource() override; caf::Signal<> dataSourceChanged; + caf::Signal<> cumulativeChanged; virtual std::string unitNameX() const = 0; virtual std::string unitNameY() const = 0; - virtual HistogramResult compute( RimHistogramPlot::GraphType graphType, RimHistogramPlot::FrequencyType frequencyType ) const = 0; + virtual HistogramResult + compute( RimHistogramPlot::GraphType graphType, RimHistogramPlot::FrequencyType frequencyType, bool cumulative = false ) const = 0; + + virtual bool showCumulativeCurve() const; virtual void setDefaults() = 0; virtual std::string name() const = 0; - static std::vector computeHistogramBins( double min, double max, int numBins, RimHistogramPlot::GraphType graphType ); + static std::vector + computeHistogramBins( double min, double max, int numBins, RimHistogramPlot::GraphType graphType, bool cumulative = false ); static std::vector computeHistogramFrequencies( const std::vector& values, RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ); + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative = false ); static std::vector computeHistogramFrequencies( const std::vector& values, RimHistogramPlot::GraphType graphType, - RimHistogramPlot::FrequencyType frequencyType ); + RimHistogramPlot::FrequencyType frequencyType, + bool cumulative = false ); }; diff --git a/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp b/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp index afe8882833d..faeb0c33d20 100644 --- a/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp +++ b/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.cpp @@ -20,14 +20,11 @@ #include "Rim3dOverlayInfoConfig.h" -#include "RiaGuiApplication.h" #include "RiaLogging.h" #include "RiaPreferences.h" #include "RiaQDateTimeTools.h" #include "RiuMessageDialog.h" -#include "RicGridStatisticsDialog.h" - #include "ContourMap/RigContourMapProjection.h" #include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" @@ -236,31 +233,6 @@ QString Rim3dOverlayInfoConfig::sampleCountText( const std::vector& hist return QString( "
Sample Count: %1" ).arg( text ); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RicGridStatisticsDialog* Rim3dOverlayInfoConfig::getOrCreateGridStatisticsDialog() -{ - if ( !m_gridStatisticsDialog ) - { - m_gridStatisticsDialog = new RicGridStatisticsDialog( RiaGuiApplication::widgetToUseAsParent() ); - } - CVF_ASSERT( m_gridStatisticsDialog ); - return m_gridStatisticsDialog; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QImage Rim3dOverlayInfoConfig::statisticsDialogScreenShotImage() -{ - if ( getOrCreateGridStatisticsDialog()->isVisible() ) - { - return getOrCreateGridStatisticsDialog()->screenShotImage(); - } - return QImage(); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -740,29 +712,6 @@ QString Rim3dOverlayInfoConfig::resultInfoText( const RigHistogramData& histData return infoText; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void Rim3dOverlayInfoConfig::showStatisticsInfoDialog( bool raise ) -{ - auto gridView = dynamic_cast( m_viewDef.p() ); - if ( gridView ) - { - RicGridStatisticsDialog* dialog = getOrCreateGridStatisticsDialog(); - // Show dialog before setting data due to text edit auto height setting - dialog->resize( 600, 800 ); - dialog->show(); - - dialog->setLabel( "Grid statistics" ); - dialog->updateFromRimView( gridView ); - - if ( raise ) - { - dialog->raise(); - } - } -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -806,9 +755,6 @@ void Rim3dOverlayInfoConfig::update3DInfo() m_showVolumeWeightedMean = false; } updateEclipse3DInfo( reservoirView ); - - // Update statistics dialog - getOrCreateGridStatisticsDialog()->updateFromRimView( reservoirView ); } RimGeoMechView* geoMechView = dynamic_cast( m_viewDef.p() ); @@ -817,9 +763,6 @@ void Rim3dOverlayInfoConfig::update3DInfo() m_showVolumeWeightedMean = false; updateGeoMech3DInfo( geoMechView ); - - // Update statistics dialog - getOrCreateGridStatisticsDialog()->updateFromRimView( geoMechView ); } RimSeismicView* seisView = dynamic_cast( m_viewDef.p() ); diff --git a/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.h b/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.h index 03517cfbf41..87d2874fb23 100644 --- a/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.h +++ b/ApplicationLibCode/ProjectDataModel/Rim3dOverlayInfoConfig.h @@ -29,8 +29,6 @@ #include "cvfVector2.h" -#include - #include #include @@ -39,7 +37,6 @@ class RimEclipseContourMapView; class RimEclipseView; class RimGeoMechView; class Rim3dView; -class RicGridStatisticsDialog; class RimSeismicView; //================================================================================================== @@ -65,10 +62,6 @@ class Rim3dOverlayInfoConfig : public caf::PdmObject QString caseInfoText(); QString resultInfoText( const RigHistogramData& histData ); - RicGridStatisticsDialog* getOrCreateGridStatisticsDialog(); - void showStatisticsInfoDialog( bool raise = true ); - QImage statisticsDialogScreenShotImage(); - bool showAnimProgress() const; bool showCaseInfo() const; bool showResultInfo() const; @@ -113,6 +106,4 @@ class Rim3dOverlayInfoConfig : public caf::PdmObject cvf::Vec2ui m_position; std::unique_ptr m_histogramCalculator; - - QPointer m_gridStatisticsDialog; }; diff --git a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp index e13113fe473..a8ae56bc502 100644 --- a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp @@ -867,7 +867,6 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() else if ( dynamic_cast( firstUiItem ) ) { menuBuilder << "RicCreateGridStatisticsPlotFeature"; - menuBuilder << "RicShowGridStatisticsFeature"; } else if ( dynamic_cast( firstUiItem ) ) { diff --git a/ApplicationLibCode/UnitTests/CMakeLists.txt b/ApplicationLibCode/UnitTests/CMakeLists.txt index adb0d9e531c..5661cee0f85 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists.txt +++ b/ApplicationLibCode/UnitTests/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCE_UNITTEST_FILES ${CMAKE_CURRENT_LIST_DIR}/RigStatisticsMath-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigStimPlanModelTools-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigWellPathIntersectionTools-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimHistogramDataSource-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RimWellLogExtractionCurveImpl-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RivPipeGeometryGenerator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RivTernaryScalarMapper-Test.cpp diff --git a/ApplicationLibCode/UnitTests/RimHistogramDataSource-Test.cpp b/ApplicationLibCode/UnitTests/RimHistogramDataSource-Test.cpp new file mode 100644 index 00000000000..e7069343a14 --- /dev/null +++ b/ApplicationLibCode/UnitTests/RimHistogramDataSource-Test.cpp @@ -0,0 +1,101 @@ +#include "gtest/gtest.h" + +#include "Histogram/RimHistogramDataSource.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimHistogramDataSourceTest, CumulativeLineGraphRelativePercent ) +{ + std::vector bins = { 1, 2, 3 }; + + std::vector frequencies = + RimHistogramDataSource::computeHistogramFrequencies( bins, + RimHistogramPlot::GraphType::LINE_GRAPH, + RimHistogramPlot::FrequencyType::RELATIVE_FREQUENCY_PERCENT, + true ); + + ASSERT_EQ( 3u, frequencies.size() ); + EXPECT_NEAR( 100.0 / 6.0, frequencies[0], 1e-9 ); + EXPECT_NEAR( 50.0, frequencies[1], 1e-9 ); + EXPECT_NEAR( 100.0, frequencies[2], 1e-9 ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimHistogramDataSourceTest, CumulativeLineGraphAbsolute ) +{ + std::vector bins = { 1, 2, 3 }; + + std::vector frequencies = RimHistogramDataSource::computeHistogramFrequencies( bins, + RimHistogramPlot::GraphType::LINE_GRAPH, + RimHistogramPlot::FrequencyType::ABSOLUTE_FREQUENCY, + true ); + + ASSERT_EQ( 3u, frequencies.size() ); + EXPECT_NEAR( 1.0, frequencies[0], 1e-9 ); + EXPECT_NEAR( 3.0, frequencies[1], 1e-9 ); + EXPECT_NEAR( 6.0, frequencies[2], 1e-9 ); +} + +//-------------------------------------------------------------------------------------------------- +/// The cumulative sum must be applied per bin before the bar graph expansion: the expanded vector +/// starts with a 0.0 closer and each cumulative bin value is duplicated. Unlike a regular +/// histogram, a cumulative curve has no closing 0.0 at the end: it ends at its maximum. +//-------------------------------------------------------------------------------------------------- +TEST( RimHistogramDataSourceTest, CumulativeBarGraphAbsolute ) +{ + std::vector bins = { 1, 2, 3 }; + + std::vector frequencies = RimHistogramDataSource::computeHistogramFrequencies( bins, + RimHistogramPlot::GraphType::BAR_GRAPH, + RimHistogramPlot::FrequencyType::ABSOLUTE_FREQUENCY, + true ); + + std::vector expected = { 0.0, 1.0, 1.0, 3.0, 3.0, 6.0, 6.0 }; + ASSERT_EQ( expected.size(), frequencies.size() ); + for ( size_t i = 0; i < expected.size(); i++ ) + { + EXPECT_NEAR( expected[i], frequencies[i], 1e-9 ); + } + + std::vector xValues = + RimHistogramDataSource::computeHistogramBins( 0.0, 3.0, static_cast( bins.size() ), RimHistogramPlot::GraphType::BAR_GRAPH, true ); + EXPECT_EQ( frequencies.size(), xValues.size() ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimHistogramDataSourceTest, NonCumulativeBarGraphBinsAndFrequenciesMatch ) +{ + std::vector bins = { 1, 2, 3 }; + + std::vector frequencies = + RimHistogramDataSource::computeHistogramFrequencies( bins, + RimHistogramPlot::GraphType::BAR_GRAPH, + RimHistogramPlot::FrequencyType::ABSOLUTE_FREQUENCY ); + + std::vector xValues = + RimHistogramDataSource::computeHistogramBins( 0.0, 3.0, static_cast( bins.size() ), RimHistogramPlot::GraphType::BAR_GRAPH ); + EXPECT_EQ( frequencies.size(), xValues.size() ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimHistogramDataSourceTest, NonCumulativeIsUnchanged ) +{ + std::vector bins = { 1, 2, 3 }; + + std::vector frequencies = + RimHistogramDataSource::computeHistogramFrequencies( bins, + RimHistogramPlot::GraphType::LINE_GRAPH, + RimHistogramPlot::FrequencyType::ABSOLUTE_FREQUENCY ); + + ASSERT_EQ( 3u, frequencies.size() ); + EXPECT_NEAR( 1.0, frequencies[0], 1e-9 ); + EXPECT_NEAR( 2.0, frequencies[1], 1e-9 ); + EXPECT_NEAR( 3.0, frequencies[2], 1e-9 ); +} diff --git a/ApplicationLibCode/UserInterface/RiuViewerCommands.cpp b/ApplicationLibCode/UserInterface/RiuViewerCommands.cpp index e0238703a9b..e0c8c9618b0 100644 --- a/ApplicationLibCode/UserInterface/RiuViewerCommands.cpp +++ b/ApplicationLibCode/UserInterface/RiuViewerCommands.cpp @@ -621,7 +621,6 @@ void RiuViewerCommands::displayContextMenu( QMouseEvent* event ) { menuBuilder.addSeparator(); menuBuilder << "RicCreateGridStatisticsPlotFeature"; - menuBuilder << "RicShowGridStatisticsFeature"; // Grid statistics commands operate on the result shown in the overlay info box, so only // offer them when the context menu is invoked inside that box, not on the view geometry. if ( m_viewer->isMousePosWithinInfoBox( event->x(), event->y() ) )