///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2016 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 "RimSummaryPlot.h" #include "RiaColorTables.h" #include "RiaColorTools.h" #include "RiaCurveMerger.h" #include "RiaDefines.h" #include "RiaLogging.h" #include "RiaPlotDefines.h" #include "RiaPreferences.h" #include "RiaPreferencesSummary.h" #include "RiaQDateTimeTools.h" #include "RiaRegressionTestRunner.h" #include "RiaStdStringTools.h" #include "RiuMessageDialog.h" #include "RifEclipseSummaryAddressDefines.h" #include "Annotations/RimTimeAxisAnnotationUpdater.h" #include "RimAsciiDataCurve.h" #include "RimCurveStyleRule.h" #include "RimCurveStyleRules.h" #include "RimCurveStyleSettings.h" #include "RimEnsembleCurveSet.h" #include "RimEnsembleCurveSetCollection.h" #include "RimGridTimeHistoryCurve.h" #include "RimMultiPlot.h" #include "RimPlotAxisLogRangeCalculator.h" #include "RimProject.h" #include "RimSummaryAddress.h" #include "RimSummaryAddressCollection.h" #include "RimSummaryCalculationCollection.h" #include "RimSummaryCase.h" #include "RimSummaryCurve.h" #include "RimSummaryCurveAppearanceCalculator.h" #include "RimSummaryCurveCollection.h" #include "RimSummaryCurvesData.h" #include "RimSummaryDeclineCurve.h" #include "RimSummaryEnsemble.h" #include "RimSummaryPlotAxisFormatter.h" #include "RimSummaryPlotControls.h" #include "RimSummaryPlotNameHelper.h" #include "RimSummaryPlotTextProvider.h" #include "RimSummaryPlotTools.h" #include "RimSummaryTimeAxisProperties.h" #include "Summary/RiaSummaryAddressCollectionTools.h" #include "Summary/RiaSummaryCurveDefinition.h" #include "Summary/RiaSummaryDefines.h" #include "Summary/RiaSummaryPlotTools.h" #include "Summary/RiaSummaryTools.h" #include "SummaryPlotCommands/RicSummaryPlotEditorUi.h" #include "RiuAbstractOverlayContentFrame.h" #include "RiuDraggableOverlayFrame.h" #include "RiuPlotAxis.h" #include "RiuPlotMainWindow.h" #include "RiuPlotMainWindowTools.h" #include "RiuQwtPlotCurve.h" #include "RiuQwtPlotItem.h" #include "RiuQwtPlotWidget.h" #include "RiuSummaryQwtPlot.h" #include "cvfColor3.h" #include "cafPdmFieldScriptingCapability.h" #include "cafPdmObjectScriptingCapability.h" #include "cafPdmUiTreeOrdering.h" #include "cafSelectionManager.h" #include "qwt_date.h" #include "qwt_plot.h" #include "qwt_plot_curve.h" #include "qwt_plot_textlabel.h" #include "qwt_text.h" #include #include #include #include #include #include #include #include #include #include #include #include CAF_PDM_SOURCE_INIT( RimSummaryPlot, "SummaryPlot" ); namespace caf { template <> void caf::AppEnum::setUp() { addItem( RimSummaryPlot::EquationBoxPosition::UPPER_LEFT, "UPPER_LEFT", "Upper Left" ); addItem( RimSummaryPlot::EquationBoxPosition::UPPER_RIGHT, "UPPER_RIGHT", "Upper Right" ); addItem( RimSummaryPlot::EquationBoxPosition::LOWER_LEFT, "LOWER_LEFT", "Lower Left" ); addItem( RimSummaryPlot::EquationBoxPosition::LOWER_RIGHT, "LOWER_RIGHT", "Lower Right" ); setDefault( RimSummaryPlot::EquationBoxPosition::UPPER_RIGHT ); } }; // namespace caf namespace { //-------------------------------------------------------------------------------------------------- /// Returns the info required to report time as time from simulation start. Returns nothing if the /// time axis is configured to show dates. //-------------------------------------------------------------------------------------------------- std::optional createTimeFromSimulationStartInfo( RimSummaryPlot* summaryPlot ) { auto* timeAxisProperties = summaryPlot->timeAxisProperties(); if ( !timeAxisProperties || timeAxisProperties->timeMode() != RimSummaryTimeAxisProperties::TIME_FROM_SIMULATION_START ) return {}; return TimeFromSimulationStartInfo{ RiaQDateTimeTools::fromTime_t( summaryPlot->firstTimeStepOfFirstCurve() ), timeAxisProperties->timeUnit(), caf::AppEnum::uiText( timeAxisProperties->timeUnit() ) }; } } // namespace struct RimSummaryPlot::CurveInfo { int curveCount = 0; std::vector curves; std::vector curveSets; void appendCurveInfo( const CurveInfo& other ) { curveCount += other.curveCount; curves.insert( curves.end(), other.curves.begin(), other.curves.end() ); curveSets.insert( curveSets.end(), other.curveSets.begin(), other.curveSets.end() ); }; }; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlot::RimSummaryPlot() : curvesChanged( this ) , axisChanged( this ) , titleChanged( this ) , m_isValid( true ) , axisChangedReloadRequired( this ) , autoTitleChanged( this ) , m_legendPosition( RiuPlotWidget::Legend::BOTTOM ) { CAF_PDM_InitScriptableObject( "Summary Plot", ":/SummaryPlotLight16x16.png", "", "A Summary Plot" ); CAF_PDM_InitScriptableField( &m_useAutoPlotTitle, "IsUsingAutoName", true, "Auto Title" ); CAF_PDM_InitScriptableField( &m_description, "PlotDescription", QString( "Summary Plot" ), "Name" ); CAF_PDM_InitScriptableField( &m_normalizeCurveYValues, "normalizeCurveYValues", false, "Normalize all curves" ); CAF_PDM_InitField( &m_equationBoxPosition, "EquationBoxPosition", caf::AppEnum( EquationBoxPosition::UPPER_RIGHT ), "Equation Box Position" ); CAF_PDM_InitFieldNoDefault( &m_summaryCurveCollection, "SummaryCurveCollection", "" ); m_summaryCurveCollection = new RimSummaryCurveCollection; m_summaryCurveCollection->curvesChanged.connect( this, &RimSummaryPlot::onCurveCollectionChanged ); CAF_PDM_InitScriptableFieldNoDefault( &m_ensembleCurveSetCollection, "EnsembleCurveSetCollection", "" ); m_ensembleCurveSetCollection = new RimEnsembleCurveSetCollection(); CAF_PDM_InitFieldNoDefault( &m_gridTimeHistoryCurves, "GridTimeHistoryCurves", "" ); CAF_PDM_InitFieldNoDefault( &m_asciiDataCurves, "AsciiDataCurves", "" ); CAF_PDM_InitFieldNoDefault( &m_axisPropertiesArray, "AxisProperties", "Axes", ":/Axes16x16.png" ); m_axisPropertiesArray.uiCapability()->setUiTreeHidden( false ); auto leftAxis = addNewAxisProperties( RiuPlotAxis::defaultLeft(), "Left" ); leftAxis->setAlwaysRequired( true ); auto rightAxis = addNewAxisProperties( RiuPlotAxis::defaultRight(), "Right" ); rightAxis->setAlwaysRequired( true ); m_nameHelperAllCurves = std::make_unique(); CAF_PDM_InitFieldNoDefault( &m_sourceStepping, "SourceStepping", "" ); m_sourceStepping = new RimSummaryPlotSourceStepping; m_sourceStepping->setSourceSteppingObject( this ); m_sourceStepping.uiCapability()->setUiTreeChildrenHidden( true ); m_sourceStepping.xmlCapability()->disableIO(); CAF_PDM_InitFieldNoDefault( &m_fallbackPlotName, "AlternateName", "AlternateName" ); m_fallbackPlotName.uiCapability()->setUiReadOnly( true ); m_fallbackPlotName.uiCapability()->setUiHidden( true ); m_fallbackPlotName.xmlCapability()->disableIO(); setPlotInfoLabel( "Filters Active" ); // Obsolete axis fields CAF_PDM_InitFieldNoDefault( &m_leftYAxisProperties_OBSOLETE, "LeftYAxisProperties", "Left Y Axis" ); m_leftYAxisProperties_OBSOLETE.xmlCapability()->setIOWritable( false ); m_leftYAxisProperties_OBSOLETE = new RimPlotAxisProperties; CAF_PDM_InitFieldNoDefault( &m_rightYAxisProperties_OBSOLETE, "RightYAxisProperties", "Right Y Axis" ); m_rightYAxisProperties_OBSOLETE.xmlCapability()->setIOWritable( false ); m_rightYAxisProperties_OBSOLETE = new RimPlotAxisProperties; CAF_PDM_InitFieldNoDefault( &m_bottomAxisProperties_OBSOLETE, "BottomAxisProperties", "Bottom X Axis" ); m_bottomAxisProperties_OBSOLETE.xmlCapability()->setIOWritable( false ); m_bottomAxisProperties_OBSOLETE = new RimPlotAxisProperties; CAF_PDM_InitFieldNoDefault( &m_timeAxisProperties_OBSOLETE, "TimeAxisProperties", "Time Axis" ); m_timeAxisProperties_OBSOLETE.xmlCapability()->setIOWritable( false ); m_timeAxisProperties_OBSOLETE = new RimSummaryTimeAxisProperties; ensureRequiredAxisObjectsForCurves(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlot::~RimSummaryPlot() { m_isValid = false; removeWindowFromDock(); deletePlotCurvesAndPlotWidget(); delete m_summaryCurveCollection; delete m_ensembleCurveSetCollection; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateAxes() { updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_LEFT ); updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_RIGHT ); updateAnnotationsInPlotWidget(); updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM ); updateNumericalAxis( RiaDefines::PlotAxis::PLOT_AXIS_TOP ); updateTimeAxis( timeAxisProperties() ); updatePlotWidgetFromAxisRanges(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateAnnotationsInPlotWidget() { if ( m_summaryPlot ) m_summaryPlot->clearAnnotationObjects(); if ( !plotWidget() ) return; if ( timeAxisProperties() ) { m_summaryPlot->updateAnnotationObjects( timeAxisProperties() ); } if ( auto leftYAxisProperties = axisPropertiesForPlotAxis( RiuPlotAxis::defaultLeft() ) ) { m_summaryPlot->updateAnnotationObjects( leftYAxisProperties ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::isLogarithmicScaleEnabled( RiuPlotAxis plotAxis ) const { auto axisProperties = axisPropertiesForPlotAxis( plotAxis ); if ( !axisProperties ) return false; return axisProperties->isLogarithmicScaleEnabled(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::isCurveHighlightSupported() const { return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryTimeAxisProperties* RimSummaryPlot::timeAxisProperties() { // Find the first time axis (which is correct since there is only one). for ( const auto& ap : m_axisPropertiesArray ) { auto* timeAxis = dynamic_cast( ap.p() ); if ( timeAxis ) return timeAxis; } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- time_t RimSummaryPlot::firstTimeStepOfFirstCurve() { RimSummaryCurve* firstCurve = nullptr; if ( m_summaryCurveCollection ) { std::vector curves = m_summaryCurveCollection->curves(); size_t i = 0; while ( firstCurve == nullptr && i < curves.size() ) { firstCurve = curves[i]; ++i; } } if ( firstCurve && !firstCurve->timeStepsY().empty() ) { return firstCurve->timeStepsY()[0]; } return time_t( 0 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QWidget* RimSummaryPlot::viewWidget() { return plotWidget(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuPlotWidget* RimSummaryPlot::plotWidget() { if ( !m_summaryPlot ) return nullptr; return m_summaryPlot->plotWidget(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuSummaryPlot* RimSummaryPlot::summaryPlotWidget() const { return m_summaryPlot; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryPlot::asciiDataForPlotExport() const { return asciiDataForSummaryPlotExport( RiaDefines::DateTimePeriod::YEAR, false ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::unique_ptr RimSummaryPlot::createTabbedTextProvider() const { return std::make_unique( this ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryPlot::asciiDataForSummaryPlotExport( RiaDefines::DateTimePeriod resamplingPeriod, bool showTimeAsLongString ) const { std::vector allCurves = descendantsIncludingThisOfType(); std::vector crossPlotCurves; std::vector curves; std::vector observedCurves; for ( auto c : allCurves ) { if ( c->axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) { crossPlotCurves.push_back( c ); } else if ( c->summaryCaseY() && c->summaryCaseY()->isObservedData() && !c->isRegressionCurve() ) { observedCurves.push_back( c ); } else { curves.push_back( c ); } } auto gridCurves = m_gridTimeHistoryCurves.childrenByType(); auto asciiCurves = m_asciiDataCurves.childrenByType(); auto timeFromSimulationStart = createTimeFromSimulationStartInfo( const_cast( this ) ); QString text; text += RimSummaryCurvesData::createTextForExport( curves, asciiCurves, gridCurves, resamplingPeriod, showTimeAsLongString, timeFromSimulationStart ); if ( !observedCurves.empty() ) { text += "\n\n------------ Observed Curves --------------"; text += RimSummaryCurvesData::createTextForExport( observedCurves, {}, {}, RiaDefines::DateTimePeriod::NONE, showTimeAsLongString, timeFromSimulationStart ); } text += RimSummaryCurvesData::createTextForCrossPlotCurves( crossPlotCurves ); QString equationsText; for ( auto curve : allCurves ) { if ( auto declineCurve = dynamic_cast( curve ); declineCurve && declineCurve->isChecked() ) { equationsText += "\n" + declineCurve->curveName() + "\n" + declineCurve->equationText() + "\n"; } } if ( !equationsText.isEmpty() ) { text += "\n\n------------ Decline Curve Equations --------------\n" + equationsText; } return text; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::onAxisSelected( RiuPlotAxis axis, bool toggle ) { RiuPlotMainWindowTools::showPlotMainWindow(); caf::PdmObject* itemToSelect = axisPropertiesForPlotAxis( axis ); RiuPlotMainWindowTools::selectOrToggleObject( itemToSelect, toggle ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::curvesForStepping() const { auto curveForStepping = summaryCurveCollection()->curveForSourceStepping(); if ( curveForStepping ) { return { curveForStepping }; } return summaryCurves(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::curveSets() const { return ensembleCurveSetCollection()->curveSets(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::allCurves() const { return summaryCurves(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::summaryAndEnsembleCurves() const { std::vector curves = summaryCurves(); for ( const auto& curveSet : ensembleCurveSetCollection()->curveSets() ) { for ( const auto& curve : curveSet->curves() ) { curves.push_back( curve ); } } return curves; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimSummaryPlot::summaryAndEnsembleCurveDefinitions() const { std::set allCurveDefs; for ( const auto& curve : summaryAndEnsembleCurves() ) { allCurveDefs.insert( RiaSummaryCurveDefinition( curve->summaryCaseY(), curve->summaryAddressY(), curve->isEnsembleCurve() ) ); } return allCurveDefs; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::summaryCurves() const { return m_summaryCurveCollection->curves(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::deleteAllSummaryCurves() { m_summaryCurveCollection->deleteAllCurves(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryCurveCollection* RimSummaryPlot::summaryCurveCollection() const { return m_summaryCurveCollection(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updatePlotTitle() { m_nameHelperAllCurves->clear(); updateNameHelperWithCurveData( m_nameHelperAllCurves.get() ); if ( m_useAutoPlotTitle ) { m_description = m_nameHelperAllCurves->plotTitle(); } if ( m_description().isEmpty() ) { auto multiPlot = firstAncestorOrThisOfType(); size_t index = 0; if ( multiPlot ) index = multiPlot->plotIndex( this ); QString title = QString( "Sub Plot %1" ).arg( index + 1 ); m_fallbackPlotName = title; } updateCurveNames(); updateWindowTitle(); if ( plotWidget() ) { QString plotTitle = description(); plotWidget()->setPlotTitle( plotTitle ); plotWidget()->setPlotTitleEnabled( m_showPlotTitle && !isSubPlot() ); scheduleReplotIfVisible(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RimSummaryNameHelper* RimSummaryPlot::activePlotTitleHelperAllCurves() const { if ( m_useAutoPlotTitle() ) { return m_nameHelperAllCurves.get(); } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RimSummaryNameHelper* RimSummaryPlot::plotTitleHelper() const { return m_nameHelperAllCurves.get(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::copyAxisPropertiesFromOther( const RimSummaryPlot& sourceSummaryPlot ) { for ( auto ap : sourceSummaryPlot.allPlotAxes() ) { QString data = ap->writeObjectToXmlString(); auto axisProperty = axisPropertiesForPlotAxis( ap->plotAxis() ); if ( axisProperty ) { axisProperty->readObjectFromXmlString( data, caf::PdmDefaultObjectFactory::instance() ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::copyAxisPropertiesFromOther( RiaDefines::PlotAxis plotAxisType, const RimSummaryPlot& sourceSummaryPlot ) { for ( auto ap : sourceSummaryPlot.allPlotAxes() ) { if ( ap->plotAxis().axis() != plotAxisType ) continue; QString data = ap->writeObjectToXmlString(); auto axisProperty = axisPropertiesForPlotAxis( ap->plotAxis() ); if ( axisProperty ) { axisProperty->readObjectFromXmlString( data, caf::PdmDefaultObjectFactory::instance() ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::copyMatchingAxisPropertiesFromOther( const RimSummaryPlot& summaryPlot ) { for ( auto apToCopy : summaryPlot.allPlotAxes() ) { for ( auto ap : allPlotAxes() ) { if ( ap->objectName().compare( apToCopy->objectName() ) == 0 ) { QString data = apToCopy->writeObjectToXmlString(); ap->readObjectFromXmlString( data, caf::PdmDefaultObjectFactory::instance() ); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateAll() { if ( plotWidget() ) { updatePlotTitle(); plotWidget()->updateLegend(); updateAxes(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateLegend() { if ( plotWidget() ) { if ( m_showPlotLegends && !isSubPlot() ) { plotWidget()->insertLegend( m_legendPosition ); } else { plotWidget()->clearLegend(); } for ( auto c : summaryCurves() ) { c->updateLegendEntryVisibilityNoPlotUpdate(); } } reattachAllCurves(); if ( plotWidget() ) { plotWidget()->updateLegend(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::setLegendPosition( RiuPlotWidget::Legend position ) { m_legendPosition = position; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::setPlotInfoLabel( const QString& label ) { auto qwtText = QwtText( label ); qwtText.setRenderFlags( Qt::AlignTop | Qt::AlignRight ); QFont font; font.setBold( true ); qwtText.setFont( font ); m_plotInfoLabel = std::make_unique(); m_plotInfoLabel->setText( qwtText ); m_plotInfoLabel->setMargin( 10 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::showPlotInfoLabel( bool show ) { auto* qwtPlotWidget = dynamic_cast( plotWidget() ); if ( !qwtPlotWidget ) return; if ( show ) m_plotInfoLabel->attach( qwtPlotWidget->qwtPlot() ); else m_plotInfoLabel->detach(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updatePlotInfoLabel() { bool anyCurveSetFiltered = false; for ( auto group : m_ensembleCurveSetCollection->curveSets() ) { if ( group->isFiltered() ) { anyCurveSetFiltered = true; break; } } showPlotInfoLabel( anyCurveSetFiltered ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RimSummaryPlot::singleColorCurveCount() const { auto allCurveSets = ensembleCurveSetCollection()->curveSets(); size_t colorIndex = std::count_if( allCurveSets.begin(), allCurveSets.end(), []( RimEnsembleCurveSet* curveSet ) { return RimEnsembleCurveSetColorManager::hasSameColorForAllRealizationCurves( curveSet->colorMode() ); } ); colorIndex += curveCount(); return colorIndex; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::applyDefaultCurveAppearances() { applyDefaultCurveAppearances( summaryCurves() ); applyDefaultCurveAppearances( ensembleCurveSetCollection()->curveSets() ); } namespace { //-------------------------------------------------------------------------------------------------- /// Project-stored custom rules for specific items of the dimensions selected for color or symbol. Later rules win. //-------------------------------------------------------------------------------------------------- std::vector rulesForCurve( RimSummaryCurve* curve ) { auto project = RimProject::current(); if ( !project || !project->curveStyleRules() ) return {}; return project->curveStyleRules()->rulesFor( curve->summaryAddressY(), RimCurveStyleSettings::caseName( curve->summaryCaseY() ) ); } //-------------------------------------------------------------------------------------------------- /// Custom color for an ensemble curve set, if any. Later matching rules win. //-------------------------------------------------------------------------------------------------- std::optional overriddenEnsembleColor( RimEnsembleCurveSet* curveSet ) { auto project = RimProject::current(); if ( !project || !project->curveStyleRules() ) return std::nullopt; const std::string caseName = curveSet->summaryEnsemble() ? curveSet->summaryEnsemble()->name().toStdString() : std::string(); const auto colorDimension = project->curveStyleRules()->settings()->colorDimension(); std::optional color; for ( auto rule : project->curveStyleRules()->rulesFor( curveSet->summaryAddressY(), caseName ) ) { if ( rule->dimension() == colorDimension && rule->overriddenColor() ) color = rule->overriddenColor(); } return color; } //-------------------------------------------------------------------------------------------------- /// Maps the values of a dimension to indices used to pick palette colors and symbols. Cases and wells are /// indexed among all items in the project to give the same item the same look in every plot, all other /// dimensions among the values present in the plot. //-------------------------------------------------------------------------------------------------- class DimensionIndexer { public: using Settings = RimCurveStyleSettings; DimensionIndexer( Settings::Dimension dimension, const std::set& curveDefs ) : m_dimension( dimension ) { if ( dimension == Settings::Dimension::NONE ) return; if ( dimension == Settings::Dimension::CASE || dimension == Settings::Dimension::WELL ) { auto rules = RimProject::current()->curveStyleRules(); rules->registerItemNames( dimension, RimCurveStyleRule::availableNames( dimension ) ); for ( const auto& [name, index] : rules->itemIndices( dimension ) ) { m_indices[name.toStdString()] = index; } } else { std::set values; for ( const auto& curveDef : curveDefs ) { auto value = Settings::dimensionValue( dimension, curveDef.summaryAddressY(), caseName( curveDef ) ); if ( !value.empty() ) values.insert( value ); } int index = 0; for ( const auto& value : values ) { m_indices[value] = index++; } } } static std::string caseName( RimSummaryCase* summaryCase ) { return Settings::caseName( summaryCase ); } static std::string caseName( const RiaSummaryCurveDefinition& curveDef ) { if ( curveDef.summaryCaseY() ) return caseName( curveDef.summaryCaseY() ); return curveDef.ensemble() ? curveDef.ensemble()->name().toStdString() : std::string(); } // -1 if the dimension has no value for the address int index( const RifEclipseSummaryAddress& address, const std::string& caseName ) const { if ( m_dimension == Settings::Dimension::NONE ) return -1; const auto it = m_indices.find( Settings::dimensionValue( m_dimension, address, caseName ) ); return it == m_indices.end() ? -1 : it->second; } Settings::Dimension dimension() const { return m_dimension; } private: Settings::Dimension m_dimension; std::map m_indices; }; //-------------------------------------------------------------------------------------------------- /// Color from the global appearance. Vectors can be colored by phase. //-------------------------------------------------------------------------------------------------- std::optional globalColor( const DimensionIndexer& indexer, const RifEclipseSummaryAddress& address, const std::string& caseName ) { if ( indexer.dimension() == RimCurveStyleSettings::Dimension::NONE ) return std::nullopt; if ( indexer.dimension() == RimCurveStyleSettings::Dimension::VECTOR && RiaPreferencesSummary::current()->colorCurvesByPhase() ) { return RimSummaryCurveAppearanceCalculator::assignColorByPhase( address ); } const int index = indexer.index( address, caseName ); if ( index < 0 ) return std::nullopt; return RimSummaryCurveAppearanceCalculator::cycledPaletteColor( index ); } } // namespace //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::applyDefaultCurveAppearances( std::vector curvesToUpdate ) { std::set allCurveDefs = summaryAndEnsembleCurveDefinitions(); const RimCurveStyleSettings* settings = nullptr; if ( useGlobalCurveAppearance() ) { settings = RimProject::current()->curveStyleRules()->settings(); // Indices depend on all curves in the plot, so all curves must be updated to stay consistent curvesToUpdate = summaryCurves(); } if ( settings ) { RimCurveStyleRule::AvailableNamesCache namesCache; const auto rules = RimProject::current()->curveStyleRules(); const DimensionIndexer colorIndexer( settings->colorDimension(), allCurveDefs ); const DimensionIndexer symbolIndexer( settings->symbolDimension(), allCurveDefs ); for ( auto& curve : curvesToUpdate ) { const auto address = curve->summaryAddressY(); const auto caseName = DimensionIndexer::caseName( curve->summaryCaseY() ); // Curves without a value for the selected dimensions keep their own appearance and stay editable if ( !rules->controlsColor( address, caseName ) && !rules->controlsSymbol( address, caseName ) ) continue; const auto originalColor = curve->color(); curve->resetAppearance(); // Curves where only the symbol is assigned globally keep their color if ( !rules->controlsColor( address, caseName ) ) curve->setColor( originalColor ); else if ( auto color = globalColor( colorIndexer, address, caseName ) ) curve->setColor( *color ); const int symbolIndex = symbolIndexer.index( address, caseName ); if ( symbolIndex >= 0 ) curve->setSymbol( RimSummaryCurveAppearanceCalculator::cycledSymbol( symbolIndex ) ); curve->setCurveAppearanceFromCaseType(); for ( auto rule : rulesForCurve( curve ) ) { rule->applyToCurve( curve, rule->dimension() == settings->colorDimension(), rule->dimension() == settings->symbolDimension() ); } } return; } RimSummaryCurveAppearanceCalculator curveLookCalc( allCurveDefs ); for ( auto& curve : curvesToUpdate ) { curve->resetAppearance(); curveLookCalc.setupCurveLook( curve ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::useGlobalCurveAppearance() const { auto project = RimProject::current(); if ( !project || !project->curveStyleRules() ) return false; return project->curveStyleRules()->useGlobalAppearance(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateCurveAppearanceFromGlobalSettings() { if ( !useGlobalCurveAppearance() ) return; applyGlobalCurveAppearance(); loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::applyGlobalCurveAppearance() { if ( !useGlobalCurveAppearance() ) return; RimCurveStyleRule::AvailableNamesCache namesCache; applyDefaultCurveAppearances( summaryCurves() ); applyDefaultCurveAppearances( ensembleCurveSetCollection()->curveSets() ); updateConnectedEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::applyDefaultCurveAppearances( std::vector ensembleCurvesToUpdate ) { // Indices depend on all curve sets in the plot, so all must be updated to stay consistent RimCurveStyleRule::AvailableNamesCache namesCache; std::optional globalColorIndexer; if ( useGlobalCurveAppearance() ) { ensembleCurvesToUpdate = ensembleCurveSetCollection()->curveSets(); globalColorIndexer.emplace( RimProject::current()->curveStyleRules()->settings()->colorDimension(), summaryAndEnsembleCurveDefinitions() ); } std::vector usedColors; for ( auto c : ensembleCurveSetCollection()->curveSets() ) { // ensembleCurvesToUpdate can be present in the ensembleCurveSetCollection()->curveSets() vector, exclude this from used // colors if ( std::find( ensembleCurvesToUpdate.begin(), ensembleCurvesToUpdate.end(), c ) == ensembleCurvesToUpdate.end() ) { usedColors.push_back( c->mainEnsembleColor() ); } } for ( auto curveSet : ensembleCurvesToUpdate ) { cvf::Color3f curveColor = cvf::Color3f::ORANGE; const auto adr = curveSet->summaryAddressY(); if ( adr.isHistoryVector() ) { curveColor = RiaPreferencesSummary::current()->historyCurveContrastColor(); } else { if ( RimEnsembleCurveSetColorManager::hasSameColorForAllRealizationCurves( curveSet->colorMode() ) ) { if ( globalColorIndexer ) { const auto caseName = curveSet->summaryEnsemble() ? curveSet->summaryEnsemble()->name().toStdString() : std::string(); if ( auto overrideColor = overriddenEnsembleColor( curveSet ) ) { curveSet->setColorAndUpdateCurves( *overrideColor ); } else if ( auto color = globalColor( *globalColorIndexer, adr, caseName ) ) { curveSet->setColorAndUpdateCurves( *color ); } // Curve sets without a value for the color dimension keep their own color continue; } std::vector candidateColors; if ( RiaPreferencesSummary::current()->colorCurvesByPhase() ) { // Put the the phase color as first candidate, will then be used if there is only one ensemble in the plot candidateColors.push_back( RiaColorTools::toQColor( RimSummaryCurveAppearanceCalculator::assignColorByPhase( adr ) ) ); } auto summaryColors = RiaColorTables::summaryCurveDefaultPaletteColors(); for ( int i = 0; i < static_cast( summaryColors.size() ); i++ ) { candidateColors.push_back( summaryColors.cycledQColor( i ) ); } for ( const auto& candidateCol : candidateColors ) { if ( std::find( usedColors.begin(), usedColors.end(), candidateCol ) == usedColors.end() ) { curveColor = RiaColorTools::fromQColorTo3f( candidateCol ); usedColors.push_back( candidateCol ); break; } } } } curveSet->setColor( curveColor ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::setNormalizationEnabled( bool enable ) { m_normalizeCurveYValues = enable; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::isNormalizationEnabled() { return m_normalizeCurveYValues(); } //-------------------------------------------------------------------------------------------------- /// /// Update regular axis with numerical axis values - i.e. not time axis. /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateNumericalAxis( RiaDefines::PlotAxis plotAxis ) { if ( !plotWidget() ) return; for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray ) { RiuPlotAxis riuPlotAxis = axisProperties->plotAxis(); if ( riuPlotAxis.axis() == plotAxis ) { auto* axisProps = dynamic_cast( axisProperties ); if ( !axisProps ) continue; if ( axisProperties->isActive() && hasVisibleCurvesForAxis( riuPlotAxis ) ) { plotWidget()->enableAxis( riuPlotAxis, true ); } else { plotWidget()->enableAxis( riuPlotAxis, false ); } if ( !hasVisibleCurvesForAxis( riuPlotAxis ) ) { axisProps->setNameForUnusedAxis(); } else { std::set timeHistoryQuantities; for ( auto c : visibleTimeHistoryCurvesForAxis( riuPlotAxis ) ) { timeHistoryQuantities.insert( c->quantityName() ); } std::vector curveDefs; for ( auto summaryCurve : summaryCurves() ) { if ( summaryCurve->axisY() == riuPlotAxis ) { curveDefs.emplace_back( summaryCurve->summaryCaseY(), summaryCurve->summaryAddressY(), summaryCurve->isEnsembleCurve() ); } if ( summaryCurve->axisX() == riuPlotAxis ) { RiaSummaryCurveDefinition def; def.setSummaryCaseX( summaryCurve->summaryCaseX() ); def.setSummaryAddressX( summaryCurve->summaryAddressX() ); curveDefs.push_back( def ); } } for ( auto curveSet : ensembleCurveSetCollection()->curveSets() ) { if ( curveSet->axisY() == riuPlotAxis ) { RiaSummaryCurveDefinition def( curveSet->summaryEnsemble(), curveSet->summaryAddressY() ); curveDefs.push_back( def ); } if ( curveSet->axisX() == riuPlotAxis ) { RiaSummaryCurveDefinition def; def.setEnsemble( curveSet->summaryEnsemble() ); def.setSummaryAddressX( curveSet->curveAddress().summaryAddressX() ); curveDefs.push_back( def ); } } RimSummaryPlotAxisFormatter calc( axisProps, {}, curveDefs, visibleAsciiDataCurvesForAxis( riuPlotAxis ), timeHistoryQuantities ); calc.applyAxisPropertiesToPlot( plotWidget() ); } plotWidget()->enableAxisNumberLabels( riuPlotAxis, axisProps->showNumbers() ); RimSummaryTimeAxisProperties::LegendTickmarkCount tickmarkCountEnum = axisProps->majorTickmarkCount(); int maxTickmarkCount = RimPlotAxisPropertiesInterface::tickmarkCountFromEnum( tickmarkCountEnum ); plotWidget()->setAutoTickIntervalCounts( riuPlotAxis, maxTickmarkCount, maxTickmarkCount ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateTimeAxis( RimSummaryTimeAxisProperties* timeAxisProperties ) { if ( !plotWidget() || !timeAxisProperties ) return; bool anyTimeHistoryCurvePresent = false; { if ( !visibleTimeHistoryCurvesForAxis( RimSummaryPlot::plotAxisForTime() ).empty() ) anyTimeHistoryCurvePresent = true; if ( !visibleAsciiDataCurvesForAxis( RimSummaryPlot::plotAxisForTime() ).empty() ) anyTimeHistoryCurvePresent = true; if ( !visibleSummaryCurvesForAxis( RimSummaryPlot::plotAxisForTime() ).empty() ) anyTimeHistoryCurvePresent = true; } if ( !anyTimeHistoryCurvePresent || !timeAxisProperties->isActive() ) { plotWidget()->enableAxis( RimSummaryPlot::plotAxisForTime(), false ); return; } if ( timeAxisProperties->timeMode() == RimSummaryTimeAxisProperties::DATE ) { RiaDefines::DateFormatComponents dateComponents = timeAxisProperties->dateComponents(); RiaDefines::TimeFormatComponents timeComponents = timeAxisProperties->timeComponents(); const QString& dateFormat = timeAxisProperties->dateFormat(); const QString& timeFormat = timeAxisProperties->timeFormat(); m_summaryPlot->useDateBasedTimeAxis( dateFormat, timeFormat, dateComponents, timeComponents ); } else { m_summaryPlot->useTimeBasedTimeAxis(); } plotWidget()->enableAxis( RimSummaryPlot::plotAxisForTime(), true ); plotWidget()->enableAxisNumberLabels( RimSummaryPlot::plotAxisForTime(), timeAxisProperties->showLabels() ); { Qt::AlignmentFlag alignment = Qt::AlignCenter; if ( timeAxisProperties->titlePosition() == RimPlotAxisPropertiesInterface::AXIS_TITLE_END ) { alignment = Qt::AlignRight; } plotWidget()->setAxisFontsAndAlignment( RimSummaryPlot::plotAxisForTime(), timeAxisProperties->titleFontSize(), timeAxisProperties->valuesFontSize(), true, alignment ); plotWidget()->setAxisTitleText( RimSummaryPlot::plotAxisForTime(), timeAxisProperties->title() ); plotWidget()->setAxisTitleEnabled( RimSummaryPlot::plotAxisForTime(), timeAxisProperties->showTitle() ); if ( timeAxisProperties->tickmarkType() == RimSummaryTimeAxisProperties::TickmarkType::TICKMARK_COUNT ) { RimSummaryTimeAxisProperties::LegendTickmarkCount tickmarkCountEnum = timeAxisProperties->majorTickmarkCount(); int maxTickmarkCount = RimPlotAxisPropertiesInterface::tickmarkCountFromEnum( tickmarkCountEnum ); plotWidget()->setAxisMaxMajor( RimSummaryPlot::plotAxisForTime(), maxTickmarkCount ); } else if ( timeAxisProperties->tickmarkType() == RimSummaryTimeAxisProperties::TickmarkType::TICKMARK_CUSTOM ) { createAndSetCustomTimeAxisTickmarks( timeAxisProperties ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateZoomForAxis( RimPlotAxisPropertiesInterface* axisProperties ) { if ( auto timeAxisProps = dynamic_cast( axisProperties ) ) { updateZoomForTimeAxis( timeAxisProps ); return; } if ( auto axisProps = dynamic_cast( axisProperties ) ) { updateZoomForNumericalAxis( axisProps ); return; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateZoomForNumericalAxis( RimPlotAxisProperties* axisProperties ) { if ( !axisProperties ) return; const auto plotAxis = axisProperties->plotAxis(); if ( axisProperties->isAutoZoom() ) { if ( axisProperties->isLogarithmicScaleEnabled() ) { plotWidget()->setAxisScaleType( plotAxis, RiuQwtPlotWidget::AxisScaleType::LOGARITHMIC ); std::vector plotCurves; for ( RimSummaryCurve* c : visibleSummaryCurvesForAxis( plotAxis ) ) { plotCurves.push_back( c ); } for ( RimGridTimeHistoryCurve* c : visibleTimeHistoryCurvesForAxis( plotAxis ) ) { plotCurves.push_back( c ); } for ( RimAsciiDataCurve* c : visibleAsciiDataCurvesForAxis( plotAxis ) ) { plotCurves.push_back( c ); } double min, max; RimPlotAxisLogRangeCalculator calc( plotAxis.axis(), plotCurves ); calc.computeAxisRange( &min, &max ); if ( axisProperties->isAxisInverted() ) { std::swap( min, max ); } plotWidget()->setAxisScale( axisProperties->plotAxis(), min, max ); } else if ( ( plotAxis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT || plotAxis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_RIGHT ) && isOnlyWaterCutCurvesVisible( plotAxis ) ) { plotWidget()->setAxisScale( axisProperties->plotAxis(), 0.0, 1.0 ); } else { plotWidget()->setAxisAutoScale( axisProperties->plotAxis(), true ); } } else { double min = axisProperties->visibleRangeMin(); double max = axisProperties->visibleRangeMax(); if ( axisProperties->isAxisInverted() ) std::swap( min, max ); plotWidget()->setAxisScale( axisProperties->plotAxis(), min, max ); } plotWidget()->setAxisInverted( axisProperties->plotAxis(), axisProperties->isAxisInverted() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateZoomForTimeAxis( RimSummaryTimeAxisProperties* timeAxisProperties ) { if ( !timeAxisProperties || !plotWidget() ) return; if ( timeAxisProperties->isAutoZoom() ) { plotWidget()->setAxisAutoScale( timeAxisProperties->plotAxis(), true ); } else { double min = timeAxisProperties->visibleRangeMin(); double max = timeAxisProperties->visibleRangeMax(); plotWidget()->setAxisScale( timeAxisProperties->plotAxis(), min, max ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::createAndSetCustomTimeAxisTickmarks( RimSummaryTimeAxisProperties* timeAxisProperties ) { if ( !timeAxisProperties || !plotWidget() ) return; const auto [minValue, maxValue] = plotWidget()->axisRange( RimSummaryPlot::plotAxisForTime() ); const auto tickmarkList = timeAxisProperties->createTickmarkList( QwtDate::toDateTime( minValue ), QwtDate::toDateTime( maxValue ) ); plotWidget()->setMajorTicksList( RimSummaryPlot::plotAxisForTime(), tickmarkList, minValue, maxValue ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::overrideTimeAxisSettingsIfTooManyCustomTickmarks( RimSummaryTimeAxisProperties* timeAxisProperties, bool showMessageBox ) { if ( !timeAxisProperties || timeAxisProperties->tickmarkType() != RimSummaryTimeAxisProperties::TickmarkType::TICKMARK_CUSTOM ) { return; } const uint MAX_NUM_TICKS = 100; // Prevent too large number of ticks by overriding time axis interval and step to // prevent large number of tickmarks by accident. const auto [minValue, maxValue] = plotWidget()->axisRange( RimSummaryPlot::plotAxisForTime() ); const double ticksInterval = timeAxisProperties->getTickmarkIntervalDouble(); const auto numTicks = static_cast( std::ceil( ( maxValue - minValue ) / ticksInterval ) ); if ( numTicks > MAX_NUM_TICKS ) { if ( showMessageBox ) { QString errorTitle = "Too many custom tickmarks"; QString errorMessage = QString( "The current configuration generates more than %1 number of custom tickmarks. " "To prevent slow response the configuration will be reset to default: \n\n" "Interval = Years and Interval Step = 1." ) .arg( MAX_NUM_TICKS ); RiuMessageDialog::showError( RiuPlotMainWindow::instance(), errorTitle, errorMessage ); } timeAxisProperties->setTickmarkInterval( RimSummaryTimeAxisProperties::TickmarkInterval::YEARS ); timeAxisProperties->setTickmarkIntervalStep( 1 ); timeAxisProperties->updateConnectedEditors(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::isOnlyWaterCutCurvesVisible( RiuPlotAxis plotAxis ) { auto curves = visibleSummaryCurvesForAxis( plotAxis ); if ( curves.empty() ) return false; for ( const auto& c : curves ) { auto quantityName = c->summaryAddressY().vectorName(); if ( !RiaStdStringTools::endsWith( quantityName, "WCT" ) ) return false; } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::enableCurvePointTracking( bool enable ) { if ( m_summaryPlot ) m_summaryPlot->enableCurvePointTracking( enable ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::any RimSummaryPlot::valueForKey( std::string key ) const { if ( key == "TimeStampZoomOperation" ) { return m_lastZoomTime; } return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuPlotAxis RimSummaryPlot::plotAxisForTime() { return RiuPlotAxis::defaultBottom(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::ensureRequiredAxisObjectsForCurves() { // Always make sure time axis properties are present if ( !timeAxisProperties() ) { auto* axisProperties = new RimSummaryTimeAxisProperties; axisProperties->settingsChanged.connect( this, &RimSummaryPlot::timeAxisSettingsChanged ); axisProperties->requestLoadDataAndUpdate.connect( this, &RimSummaryPlot::timeAxisSettingsChangedReloadRequired ); m_axisPropertiesArray.push_back( axisProperties ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::findOrAssignPlotAxisX( RimSummaryCurve* curve ) { for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray ) { if ( axisProperties->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM ) { auto propertyAxis = dynamic_cast( axisProperties ); if ( propertyAxis ) { curve->setTopOrBottomAxisX( propertyAxis->plotAxis() ); return; } } } if ( curve->summaryCaseX() != nullptr ) { if ( !plotWidget() ) { // Assign a default bottom axis if no plot widget is present. This can happens during project load and transformation to new // cross plot structure in RimMainPlotCollection::initAfterRead() QString axisObjectName = "New Axis"; if ( !curve->summaryAddressX().uiText().empty() ) axisObjectName = QString::fromStdString( curve->summaryAddressX().uiText() ); RiuPlotAxis newPlotAxis = RiuPlotAxis::defaultBottomForSummaryVectors(); addNewAxisProperties( newPlotAxis, axisObjectName ); curve->setTopOrBottomAxisX( newPlotAxis ); return; } if ( plotWidget()->isMultiAxisSupported() ) { QString axisObjectName = "New Axis"; if ( !curve->summaryAddressX().uiText().empty() ) axisObjectName = QString::fromStdString( curve->summaryAddressX().uiText() ); RiuPlotAxis newPlotAxis = plotWidget()->createNextPlotAxis( RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM ); addNewAxisProperties( newPlotAxis, axisObjectName ); if ( plotWidget() ) { plotWidget()->ensureAxisIsCreated( newPlotAxis ); } updateAxes(); curve->setTopOrBottomAxisX( newPlotAxis ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::scheduleReplotIfVisible() { if ( showWindow() && plotWidget() ) plotWidget()->scheduleReplot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::visibleSummaryCurvesForAxis( RiuPlotAxis plotAxis ) const { std::vector curves; if ( m_summaryCurveCollection && m_summaryCurveCollection->isCurvesVisible() ) { for ( RimSummaryCurve* curve : m_summaryCurveCollection->curves() ) { if ( curve->isChecked() && ( curve->axisY() == plotAxis || curve->axisX() == plotAxis ) ) { curves.push_back( curve ); } } } if ( m_ensembleCurveSetCollection && m_ensembleCurveSetCollection->isCurveSetsVisible() ) { for ( RimEnsembleCurveSet* curveSet : m_ensembleCurveSetCollection->curveSets() ) { for ( RimSummaryCurve* curve : curveSet->curves() ) { if ( curve->isChecked() && ( curve->axisY() == plotAxis || curve->axisX() == plotAxis ) ) { curves.push_back( curve ); } } } } return curves; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::hasVisibleCurvesForAxis( RiuPlotAxis plotAxis ) const { if ( !visibleSummaryCurvesForAxis( plotAxis ).empty() ) { return true; } if ( !visibleTimeHistoryCurvesForAxis( plotAxis ).empty() ) { return true; } if ( !visibleAsciiDataCurvesForAxis( plotAxis ).empty() ) { return true; } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimPlotAxisPropertiesInterface* RimSummaryPlot::axisPropertiesForPlotAxis( RiuPlotAxis plotAxis ) const { for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray ) { if ( axisProperties->plotAxis() == plotAxis ) return axisProperties; } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::visibleTimeHistoryCurvesForAxis( RiuPlotAxis plotAxis ) const { std::vector curves; for ( const auto& c : m_gridTimeHistoryCurves ) { if ( c->isChecked() ) { if ( c->yAxis() == plotAxis || plotAxis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM ) { curves.push_back( c ); } } } return curves; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::visibleAsciiDataCurvesForAxis( RiuPlotAxis plotAxis ) const { std::vector curves; for ( const auto& c : m_asciiDataCurves ) { if ( c->isChecked() ) { if ( c->yAxis() == plotAxis || plotAxis.axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM ) { curves.push_back( c ); } } } return curves; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateCaseNameHasChanged() { if ( m_summaryCurveCollection ) { m_summaryCurveCollection->updateCaseNameHasChanged(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateAndRedrawTimeAnnotations() { RimTimeAxisAnnotationUpdater::updateTimeAnnotationObjects( this ); updateAxes(); } //-------------------------------------------------------------------------------------------------- /// Delete all decline curve equation boxes and recreate them for the visible decline curves in project tree order //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::rebuildEquationOverlays() { auto widget = plotWidget(); if ( !widget ) return; if ( m_equationOverlayFrames.empty() && !hasDeclineCurves() ) return; for ( auto& frame : m_equationOverlayFrames ) { if ( frame ) { widget->removeOverlayFrame( frame ); frame->deleteLater(); } } m_equationOverlayFrames.clear(); for ( auto curve : allCurves() ) { if ( auto declineCurve = dynamic_cast( curve ) ) { if ( auto frame = declineCurve->createEquationOverlayFrame( widget->getParentForOverlay(), widget->overlayMargins() ) ) { using Corner = RiuDraggableOverlayFrame::AnchorCorner; switch ( m_equationBoxPosition() ) { case EquationBoxPosition::UPPER_LEFT: frame->setAnchorCorner( Corner::TopLeft ); break; case EquationBoxPosition::UPPER_RIGHT: frame->setAnchorCorner( Corner::TopRight ); break; case EquationBoxPosition::LOWER_LEFT: frame->setAnchorCorner( Corner::BottomLeft ); break; case EquationBoxPosition::LOWER_RIGHT: frame->setAnchorCorner( Corner::BottomRight ); break; } m_equationOverlayFrames.push_back( frame ); widget->addOverlayFrame( frame ); } } } widget->updateLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::setEquationBoxPosition( EquationBoxPosition position ) { m_equationBoxPosition = position; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::hasDeclineCurves() const { for ( auto curve : allCurves() ) { if ( dynamic_cast( curve ) ) return true; } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::scheduleRebuildEquationOverlays() { // Coalesce requests, as every decline curve requests a rebuild when its data is loaded if ( m_equationOverlayRebuildPending ) return; m_equationOverlayRebuildPending = true; caf::PdmPointer plot( this ); QTimer::singleShot( 0, [plot]() { if ( !plot ) return; plot->m_equationOverlayRebuildPending = false; plot->rebuildEquationOverlays(); } ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::zoomAll() { setAutoScaleXEnabled( true ); setAutoScaleYEnabled( true ); updatePlotWidgetFromAxisRanges(); axisChanged.send( this ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::addCurveAndUpdate( RimSummaryCurve* curve, bool autoAssignPlotAxis ) { if ( curve ) { m_summaryCurveCollection->addCurve( curve ); connectCurveToPlot( curve, true, autoAssignPlotAxis ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::addCurveNoUpdate( RimSummaryCurve* curve, bool autoAssignPlotAxis ) { if ( curve ) { m_summaryCurveCollection->addCurve( curve ); connectCurveToPlot( curve, false, autoAssignPlotAxis ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::insertCurve( RimSummaryCurve* curve, size_t insertAtPosition ) { if ( curve ) { m_summaryCurveCollection->insertCurve( curve, insertAtPosition ); connectCurveToPlot( curve, false, true ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::connectCurveToPlot( RimSummaryCurve* curve, bool update, bool autoAssignPlotAxis ) { if ( autoAssignPlotAxis ) assignPlotAxis( curve ); connectCurveSignals( curve ); if ( plotWidget() ) { plotWidget()->ensureAxisIsCreated( curve->axisY() ); if ( update ) { curve->setParentPlotAndReplot( plotWidget() ); updateAxes(); } else { curve->setParentPlotNoReplot( plotWidget() ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::removeCurve( RimSummaryCurve* curve ) { m_summaryCurveCollection->removeCurve( curve ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::deleteCurves( const std::vector& curves ) { for ( const auto curve : curves ) { if ( m_summaryCurveCollection ) { for ( auto& c : m_summaryCurveCollection->curves() ) { if ( c == curve ) { disconnectCurveSignals( curve ); m_summaryCurveCollection->deleteCurve( curve ); continue; } } } if ( m_ensembleCurveSetCollection ) { for ( auto& curveSet : m_ensembleCurveSetCollection->curveSets() ) { for ( auto& c : curveSet->curves() ) { if ( c == curve ) { curveSet->deleteRealizationCurve( curve ); if ( curveSet->curves().empty() ) { if ( curveSet->colorMode() == RimEnsembleCurveSet::ColorMode::BY_ENSEMBLE_PARAM && plotWidget() && curveSet->legendFrame() ) { plotWidget()->removeOverlayFrame( curveSet->legendFrame() ); } m_ensembleCurveSetCollection->deleteCurveSet( curveSet ); } continue; } } } } } RiuPlotMainWindowTools::refreshToolbars(); updateCaseNameHasChanged(); curvesChanged.send(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::deleteCurvesAssosiatedWithCases( const std::set& summaryCases ) { if ( m_summaryCurveCollection ) { return m_summaryCurveCollection->deleteCurvesAssosiatedWithCases( summaryCases ); } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimEnsembleCurveSetCollection* RimSummaryPlot::ensembleCurveSetCollection() const { return m_ensembleCurveSetCollection; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::addGridTimeHistoryCurve( RimGridTimeHistoryCurve* curve ) { CAF_ASSERT( curve ); m_gridTimeHistoryCurves.push_back( curve ); connectCurveSignals( curve ); if ( plotWidget() ) { curve->setParentPlotAndReplot( plotWidget() ); updateAxes(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::gridTimeHistoryCurves() const { return m_gridTimeHistoryCurves.childrenByType(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::addAsciiDataCruve( RimAsciiDataCurve* curve ) { CAF_ASSERT( curve ); m_asciiDataCurves.push_back( curve ); if ( plotWidget() ) { curve->setParentPlotAndReplot( plotWidget() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmFieldHandle* RimSummaryPlot::userDescriptionField() { if ( m_description().isEmpty() ) { return &m_fallbackPlotName; } return &m_description; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::zoomAllForMultiPlot() { if ( auto multiPlot = firstAncestorOrThisOfType() ) { multiPlot->zoomAllAndReleaseUserDefinedRanges(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { RimPlot::fieldChangedByUi( changedField, oldValue, newValue ); if ( changedField == &m_equationBoxPosition ) { rebuildEquationOverlays(); } if ( changedField == &m_description ) { m_useAutoPlotTitle = false; } if ( changedField == &m_useAutoPlotTitle || changedField == &m_description ) { autoTitleChanged.send( m_useAutoPlotTitle() ); } if ( changedField == &m_showPlotTitle || changedField == &m_description || changedField == &m_useAutoPlotTitle ) { updatePlotTitle(); updateConnectedEditors(); if ( !m_useAutoPlotTitle ) { // When auto name of plot is turned off, update the auto name for all curves for ( auto c : summaryCurves() ) { c->updateCurveNameNoLegendUpdate(); } } titleChanged.send(); } if ( changedField == &m_showPlotLegends ) updateLegend(); if ( changedField == &m_normalizeCurveYValues ) { loadDataAndUpdate(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::childFieldChangedByUi( const caf::PdmFieldHandle* changedChildField ) { updateStackedCurveData(); scheduleRebuildEquationOverlays(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateStackedCurveData() { auto anyStackedCurvesPresent = updateStackedCurveDataForRelevantAxes(); if ( plotWidget() && anyStackedCurvesPresent ) { reattachAllCurves(); scheduleReplotIfVisible(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::updateStackedCurveDataForRelevantAxes() { bool anyStackedCurvesPresent = false; for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray ) { if ( axisProperties->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT || axisProperties->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_RIGHT ) { anyStackedCurvesPresent |= updateStackedCurveDataForAxis( axisProperties->plotAxis() ); } } return anyStackedCurvesPresent; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::updateStackedCurveDataForAxis( RiuPlotAxis plotAxis ) { // See RimWellLogTrack::updateStackedCurveData() for vertical plots struct CurveStackingItem { RimStackablePlotCurve* curve; RiaDefines::PhaseType phase; std::vector xValues; std::vector yValues; }; std::vector stackingItems; for ( auto summaryCurve : visibleSummaryCurvesForAxis( plotAxis ) ) { if ( summaryCurve->isStacked() ) { auto xValues = RiuPlotCurve::fromTime_t( summaryCurve->timeStepsY() ); auto yValues = summaryCurve->valuesY(); if ( !xValues.empty() && xValues.size() == yValues.size() ) { stackingItems.push_back( { summaryCurve, summaryCurve->phaseType(), xValues, yValues } ); } } } // Add stack data for time history curves for ( auto gridCurve : gridTimeHistoryCurves() ) { if ( gridCurve->yAxis() == plotAxis && gridCurve->isStacked() && gridCurve->isChecked() ) { auto xValues = RiuPlotCurve::fromTime_t( gridCurve->timeStepValues() ); auto yValues = gridCurve->yValues(); if ( !xValues.empty() && xValues.size() == yValues.size() ) { stackingItems.push_back( { gridCurve, gridCurve->phaseType(), xValues, yValues } ); } } } if ( stackingItems.empty() ) return true; RiaCurveMerger curveMerger( RiaCurveDefines::InterpolationMethod::LINEAR ); std::map curvePhaseCount; for ( auto stackingItem : stackingItems ) { curveMerger.addCurveData( stackingItem.xValues, stackingItem.yValues ); curvePhaseCount[stackingItem.phase]++; } curveMerger.computeInterpolatedValues( true ); std::vector allXValues = curveMerger.allXValues(); std::vector allStackedValues( allXValues.size(), 0.0 ); // Z-position of curve, to draw them in correct order double zPos = -10000.0; for ( size_t index = 0; index < stackingItems.size(); index++ ) { auto yValues = curveMerger.interpolatedYValuesForAllXValues( index ); for ( size_t i = 0; i < allXValues.size(); ++i ) { double value = yValues[i]; if ( value != std::numeric_limits::infinity() ) { allStackedValues[i] += value; } } auto curve = stackingItems[index].curve; curve->setSamplesFromXYValues( allXValues, allStackedValues, false ); curve->setZOrder( zPos ); if ( curve->isStackedWithPhaseColors() ) { curve->assignStackColor( index, curvePhaseCount[curve->phaseType()] ); } zPos -= 1.0; } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ ) { if ( !m_isValid ) return; bool isPlotEditor = ( uiConfigName == RicSummaryPlotEditorUi::CONFIGURATION_NAME ); if ( !isPlotEditor ) uiTreeOrdering.add( &m_axisPropertiesArray ); for ( auto& curve : m_summaryCurveCollection->curves() ) { uiTreeOrdering.add( curve ); } for ( auto& curveSet : m_ensembleCurveSetCollection->curveSets() ) { uiTreeOrdering.add( curveSet ); } if ( !isPlotEditor ) { uiTreeOrdering.add( &m_gridTimeHistoryCurves ); uiTreeOrdering.add( &m_asciiDataCurves ); } uiTreeOrdering.skipRemainingChildren( true ); } //-------------------------------------------------------------------------------------------------- /// The addresses this plot's curves are about to read, grouped by the ensemble owning them. Used to tell a /// source what is coming before any of it is read, see prefetchSummaryData(). //-------------------------------------------------------------------------------------------------- std::map> RimSummaryPlot::summaryAddressesByEnsemble() const { std::map> addressesByEnsemble; auto addAddress = []( auto& addressesByEnsemble, RimSummaryEnsemble* ensemble, const RifEclipseSummaryAddress& address ) { if ( !ensemble || !address.isValid() ) return; addressesByEnsemble[ensemble].push_back( address ); }; if ( m_summaryCurveCollection ) { for ( RimSummaryCurve* curve : m_summaryCurveCollection->curves() ) { if ( !curve ) continue; if ( auto summaryCase = curve->summaryCaseY() ) { addAddress( addressesByEnsemble, summaryCase->firstAncestorOrThisOfType(), curve->summaryAddressY() ); } if ( auto summaryCase = curve->summaryCaseX() ) { addAddress( addressesByEnsemble, summaryCase->firstAncestorOrThisOfType(), curve->summaryAddressX() ); } } } for ( RimEnsembleCurveSet* curveSet : m_ensembleCurveSetCollection->curveSets() ) { if ( !curveSet ) continue; addAddress( addressesByEnsemble, curveSet->summaryEnsemble(), curveSet->summaryAddressY() ); } return addressesByEnsemble; } //-------------------------------------------------------------------------------------------------- /// Tell each ensemble in the plot which addresses its curves are about to read, before any of them read /// one. Curves pull their values one at a time, so a source loading data remotely would otherwise make one /// blocking request per curve; given the whole set up front it can load them together. /// /// This is a hint only. Every curve still loads its own data, and an ensemble that does not need the hint /// ignores it. A plot in a plot window is usually covered by the window prefetching for all its plots at /// once, in which case there is nothing left for this to do. //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::prefetchSummaryData() { for ( const auto& [ensemble, addresses] : summaryAddressesByEnsemble() ) { ensemble->prefetchSummaryData( addresses ); } } //-------------------------------------------------------------------------------------------------- /// Say that this plot is waiting for data. A source can load without waiting, and then the curves are drawn /// with what has arrived so far, leaving a plot looking finished when it is not. Called after every load, so /// the frame appears when the data is asked for and goes away when the last of it has arrived. //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateLoadingOverlayFrame() { if ( !plotWidget() ) return; bool isWaitingForData = false; for ( const auto& [ensemble, addresses] : summaryAddressesByEnsemble() ) { if ( ensemble->isSummaryDataPending( addresses ) ) { isWaitingForData = true; break; } } if ( !isWaitingForData ) { if ( m_loadingOverlayFrame ) { plotWidget()->removeOverlayFrame( m_loadingOverlayFrame ); delete m_loadingOverlayFrame; m_loadingOverlayFrame = nullptr; } return; } if ( !m_loadingOverlayFrame ) { m_loadingOverlayFrame = new RiuDraggableOverlayFrame( plotWidget()->getParentForOverlay(), plotWidget()->overlayMargins() ); m_loadingOverlayFrame->setAnchorCorner( RiuDraggableOverlayFrame::AnchorCorner::TopLeft ); auto* spinnerFrame = new RiuSpinnerOverlayContentFrame( m_loadingOverlayFrame ); m_loadingOverlayFrame->setContentFrame( spinnerFrame ); spinnerFrame->setText( "Loading" ); } plotWidget()->addOverlayFrame( m_loadingOverlayFrame ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::onLoadDataAndUpdate() { updatePlotTitle(); auto plotWindow = firstAncestorOrThisOfType(); if ( plotWindow == nullptr ) updateDockWindowVisibility(); prefetchSummaryData(); if ( m_summaryCurveCollection ) { m_summaryCurveCollection->loadDataAndUpdate( false ); } m_ensembleCurveSetCollection->loadDataAndUpdate( false ); for ( RimGridTimeHistoryCurve* curve : m_gridTimeHistoryCurves ) { curve->loadDataAndUpdate( false ); } for ( RimAsciiDataCurve* curve : m_asciiDataCurves ) { curve->loadDataAndUpdate( false ); } // Load data for regression curves, as they depend on data loaded by curves updated previously in this function if ( m_summaryCurveCollection ) { auto curves = m_summaryCurveCollection->curves(); for ( auto c : curves ) { if ( c->isRegressionCurve() ) { c->loadDataAndUpdate( false ); } } } if ( plotWidget() ) { if ( m_showPlotLegends && !isSubPlot() ) { plotWidget()->insertLegend( m_legendPosition ); } else { plotWidget()->clearLegend(); } plotWidget()->setLegendFontSize( legendFontSize() ); plotWidget()->updateLegend(); } updateAxes(); updateStackedCurveData(); updateLoadingOverlayFrame(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updatePlotWidgetFromAxisRanges() { if ( !plotWidget() ) return; for ( const auto& axisProperty : m_axisPropertiesArray ) { updateZoomForAxis( axisProperty ); } plotWidget()->updateAxes(); updateAxisRangesFromPlotWidget(); plotWidget()->updateZoomDependentCurveProperties(); // Must create and set new custom tickmarks for time axis after zoom update auto* timeAxisProps = timeAxisProperties(); if ( timeAxisProps && timeAxisProps->tickmarkType() == RimSummaryTimeAxisProperties::TickmarkType::TICKMARK_CUSTOM ) { // Protection against too many custom tickmarks const bool showErrorMessageBox = false; overrideTimeAxisSettingsIfTooManyCustomTickmarks( timeAxisProps, showErrorMessageBox ); // Create and set tickmarks based on settings createAndSetCustomTimeAxisTickmarks( timeAxisProps ); } scheduleReplotIfVisible(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateAxisRangesFromPlotWidget() { if ( !plotWidget() ) return; for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray ) { if ( !axisProperties ) continue; if ( !plotWidget()->axisEnabled( axisProperties->plotAxis() ) ) continue; auto [axisMin, axisMax] = plotWidget()->axisRange( axisProperties->plotAxis() ); if ( axisProperties->isAxisInverted() ) std::swap( axisMin, axisMax ); if ( auto propertyAxis = dynamic_cast( axisProperties ) ) { propertyAxis->setAutoValueVisibleRangeMax( axisMax ); propertyAxis->setAutoValueVisibleRangeMin( axisMin ); } axisProperties->setVisibleRangeMax( axisMax ); axisProperties->setVisibleRangeMin( axisMin ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::deletePlotCurvesAndPlotWidget() { if ( isDeletable() ) { detachAllPlotItems(); deleteAllPlotCurves(); if ( m_summaryPlot ) { // The RiuPlotWidget is owned by Qt, and will be deleted when its parent is destructed. m_summaryPlot.clear(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::connectCurveSignals( RimStackablePlotCurve* curve ) { curve->dataChanged.connect( this, &RimSummaryPlot::curveDataChanged ); curve->visibilityChanged.connect( this, &RimSummaryPlot::curveVisibilityChanged ); curve->appearanceChanged.connect( this, &RimSummaryPlot::curveAppearanceChanged ); curve->stackingChanged.connect( this, &RimSummaryPlot::curveStackingChanged ); curve->stackingColorsChanged.connect( this, &RimSummaryPlot::curveStackingColorsChanged ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::disconnectCurveSignals( RimStackablePlotCurve* curve ) { curve->dataChanged.disconnect( this ); curve->visibilityChanged.disconnect( this ); curve->appearanceChanged.disconnect( this ); curve->stackingChanged.disconnect( this ); curve->stackingColorsChanged.disconnect( this ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::curveDataChanged( const caf::SignalEmitter* emitter ) { loadDataAndUpdate(); curvesChanged.send(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::curveVisibilityChanged( const caf::SignalEmitter* emitter, bool visible ) { loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::curveAppearanceChanged( const caf::SignalEmitter* emitter ) { scheduleReplotIfVisible(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::curveStackingChanged( const caf::SignalEmitter* emitter, bool stacked ) { loadDataAndUpdate(); // Change of stacking can result in very large y-axis changes, so zoom all zoomAll(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::curveStackingColorsChanged( const caf::SignalEmitter* emitter, bool stackWithPhaseColors ) { loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::connectAxisSignals( RimPlotAxisProperties* axis ) { axis->settingsChanged.connect( this, &RimSummaryPlot::axisSettingsChanged ); axis->logarithmicChanged.connect( this, &RimSummaryPlot::axisLogarithmicChanged ); axis->axisPositionChanged.connect( this, &RimSummaryPlot::axisPositionChanged ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::axisSettingsChanged( const caf::SignalEmitter* emitter ) { axisChanged.send( this ); updateAxes(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::timeAxisSettingsChanged( const caf::SignalEmitter* emitter ) { axisChanged.send( this ); auto* timeAxisProps = timeAxisProperties(); if ( !timeAxisProps ) { return; } if ( plotWidget() ) { if ( timeAxisProps->isAutoZoom() ) { // Auto zoom (re-)enabled - let the plot widget recompute the visible range from the data updateZoomForTimeAxis( timeAxisProps ); } else { // If not auto zoom - the new date and time ranges must be set and axes updated plotWidget()->setAxisScale( RimSummaryPlot::plotAxisForTime(), timeAxisProps->visibleRangeMin(), timeAxisProps->visibleRangeMax() ); } plotWidget()->updateAxes(); } // Protection against too many custom tickmarks const bool showErrorMessageBox = true; overrideTimeAxisSettingsIfTooManyCustomTickmarks( timeAxisProps, showErrorMessageBox ); updateTimeAxis( timeAxisProps ); scheduleReplotIfVisible(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::timeAxisSettingsChangedReloadRequired( const caf::SignalEmitter* emitter ) { axisChangedReloadRequired.send( this ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::axisLogarithmicChanged( const caf::SignalEmitter* emitter, bool isLogarithmic ) { axisChanged.send( this ); loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimPlotAxisProperties* RimSummaryPlot::addNewAxisProperties( RiaDefines::PlotAxis plotAxis, const QString& name ) { RiuPlotAxis newPlotAxis = plotWidget()->createNextPlotAxis( plotAxis ); return addNewAxisProperties( newPlotAxis, name ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimPlotAxisProperties* RimSummaryPlot::addNewAxisProperties( RiuPlotAxis plotAxis, const QString& name ) { auto* axisProperties = new RimPlotAxisProperties; axisProperties->enableAutoValueForAllFields( true ); axisProperties->setNameAndAxis( name, name, plotAxis.axis(), plotAxis.index() ); m_axisPropertiesArray.push_back( axisProperties ); connectAxisSignals( axisProperties ); return axisProperties; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::visibleCurvesForLegend() { std::vector curves; for ( auto c : summaryCurves() ) { if ( !c->isChecked() ) continue; if ( !c->showInLegend() ) continue; curves.push_back( c ); } for ( auto curveSet : curveSets() ) { if ( !curveSet->isCurvesVisible() ) continue; if ( RimEnsembleCurveSetColorManager::hasSameColorForAllRealizationCurves( curveSet->colorMode() ) ) { auto curveSetCurves = curveSet->curves(); if ( !curveSetCurves.empty() ) curves.push_back( curveSetCurves.front() ); } } return curves; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::axisPositionChanged( const caf::SignalEmitter* emitter, RimPlotAxisProperties* axisProperties, RiuPlotAxis oldPlotAxis, RiuPlotAxis newPlotAxis ) { if ( !axisProperties ) return; if ( plotWidget() && plotWidget()->isMultiAxisSupported() ) { // Make sure the new axis on the correct side exists. RiuPlotAxis fixedUpPlotAxis = plotWidget()->createNextPlotAxis( newPlotAxis.axis() ); // The index can change so need to update. axisProperties->setNameAndAxis( axisProperties->objectName(), axisProperties->axisTitleText(), fixedUpPlotAxis.axis(), fixedUpPlotAxis.index() ); // Move all attached curves for ( auto curve : summaryCurves() ) { if ( curve->axisY() == oldPlotAxis ) curve->setLeftOrRightAxisY( fixedUpPlotAxis ); } for ( auto curveSet : ensembleCurveSetCollection()->curveSets() ) { if ( curveSet->axisY() == oldPlotAxis ) curveSet->setLeftOrRightAxisY( fixedUpPlotAxis ); } // Remove the now unused axis (but keep the default axis) if ( oldPlotAxis != RiuPlotAxis::defaultLeft() && oldPlotAxis != RiuPlotAxis::defaultRight() ) { auto oldAxisProperties = axisPropertiesForPlotAxis( oldPlotAxis ); if ( oldAxisProperties ) m_axisPropertiesArray.removeChild( oldAxisProperties ); plotWidget()->moveAxis( oldPlotAxis, newPlotAxis ); } updateAxes(); } // This is probably to much, but difficult to find the required updates loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::deleteUnlockedGridTimeHistoryCurves() { auto allGridCurves = m_gridTimeHistoryCurves.childrenByType(); for ( auto c : allGridCurves ) { if ( c->isLocked() ) continue; disconnectCurveSignals( c ); m_gridTimeHistoryCurves.removeChild( c ); delete c; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::setDescription( const QString& description ) { m_description = description; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryPlot::description() const { return m_description(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::enableAutoPlotTitle( bool enable ) { m_useAutoPlotTitle = enable; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::autoPlotTitle() const { return m_useAutoPlotTitle; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlot::CurveInfo RimSummaryPlot::handleSummaryCaseDrop( RimSummaryCase* summaryCase ) { std::map, std::set> dataVectorMap; for ( auto& curve : summaryCurves() ) { const auto addr = curve->summaryAddressY(); const auto addrX = curve->summaryAddressX(); // NB! This concept is used to make it possible to avoid adding curves for a case that is already present // To be complete, the summaryCaseX() should also be checked, but this is not done for now dataVectorMap[std::make_pair( addr, addrX )].insert( curve->summaryCaseY() ); } std::vector curves; for ( const auto& [addressPair, cases] : dataVectorMap ) { if ( cases.count( summaryCase ) > 0 ) continue; const auto& [addrY, addrX] = addressPair; curves.push_back( addNewCurve( addrY, summaryCase, addrX, summaryCase ) ); } return { .curveCount = static_cast( curves.size() ), .curves = curves, .curveSets = {} }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlot::CurveInfo RimSummaryPlot::handleEnsembleDrop( RimSummaryEnsemble* ensemble ) { int newCurves = 0; std::vector curveSetsToUpdate; std::map> dataVectorMap; for ( auto& curve : curveSets() ) { const auto addr = curve->curveAddress(); dataVectorMap[addr].insert( curve->summaryEnsemble() ); } for ( const auto& [addr, ensembles] : dataVectorMap ) { if ( ensembles.count( ensemble ) > 0 ) continue; auto curveSet = RiaSummaryPlotTools::addNewEnsembleCurve( this, addr, ensemble ); curveSetsToUpdate.push_back( curveSet ); newCurves++; } return { newCurves, {}, curveSetsToUpdate }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlot::CurveInfo RimSummaryPlot::handleAddressCollectionDrop( RimSummaryAddressCollection* addressCollection ) { int newCurves = 0; std::vector curves; std::vector curveSetsToUpdate; auto droppedName = addressCollection->name().toStdString(); auto summaryCase = RiaSummaryTools::summaryCaseById( addressCollection->caseId() ); auto ensembleCase = RiaSummaryTools::ensembleById( addressCollection->ensembleId() ); std::vector sourceCurveDefs; if ( summaryCase && !ensembleCase ) { for ( auto& curve : summaryCurves() ) { auto srcDef = curve->curveDefinition(); sourceCurveDefs.emplace_back( summaryCase, srcDef.summaryAddressY(), srcDef.isEnsembleCurve() ); } } if ( ensembleCase ) { auto curveSets = m_ensembleCurveSetCollection->curveSets(); for ( auto curveSet : curveSets ) { sourceCurveDefs.emplace_back( ensembleCase, curveSet->curveAddress() ); } } auto candidateCurveDefs = RiaSummaryAddressCollectionTools::buildCurveDefs( sourceCurveDefs, droppedName, addressCollection->contentType(), RiaPreferencesSummary::current()->appendHistoryVectors() ); auto existingSummaryCurves = RiaSummaryAddressCollectionTools::buildAddressCaseMapFromCurves( summaryCurves() ); auto existingEnsembleCurves = RiaSummaryAddressCollectionTools::buildAddressEnsembleMapFromCurveSets( curveSets() ); auto newCurveDefs = RiaSummaryAddressCollectionTools::removeExistingCurveDefs( candidateCurveDefs, existingSummaryCurves, existingEnsembleCurves ); for ( const auto& curveDef : newCurveDefs ) { if ( curveDef.ensemble() ) { auto addresses = curveDef.ensemble()->ensembleSummaryAddresses(); if ( addresses.find( curveDef.summaryAddressY() ) != addresses.end() ) { auto curveSet = RiaSummaryPlotTools::addNewEnsembleCurve( this, curveDef.summaryCurveAddress(), curveDef.ensemble() ); curveSetsToUpdate.push_back( curveSet ); newCurves++; } } else if ( curveDef.summaryCaseY() ) { if ( curveDef.summaryCaseY()->summaryReader() && curveDef.summaryCaseY()->summaryReader()->hasAddress( curveDef.summaryAddressY() ) ) { auto curve = addNewCurve( curveDef.summaryAddressY(), curveDef.summaryCaseY(), curveDef.summaryAddressX(), curveDef.summaryCaseX() ); curves.push_back( curve ); if ( curveDef.summaryCaseX() ) { curve->setAxisTypeX( RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ); curve->setSummaryCaseX( curveDef.summaryCaseX() ); curve->setSummaryAddressX( curveDef.summaryAddressX() ); findOrAssignPlotAxisX( curve ); } newCurves++; } } } return { newCurves, curves, curveSetsToUpdate }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlot::CurveInfo RimSummaryPlot::handleSummaryAddressDrop( RimSummaryAddress* summaryAddr ) { int newCurves = 0; std::vector curves; std::vector curveSetsToUpdate; std::vector newCurveAddresses; newCurveAddresses.push_back( summaryAddr->address() ); if ( !summaryAddr->address().isHistoryVector() && RiaPreferencesSummary::current()->appendHistoryVectors() ) { auto historyAddr = summaryAddr->address(); historyAddr.setVectorName( summaryAddr->address().vectorName() + RifEclipseSummaryAddressDefines::historyIdentifier() ); newCurveAddresses.push_back( historyAddr ); } if ( summaryAddr->isEnsemble() ) { auto dataVectorMap = RiaSummaryAddressCollectionTools::buildAddressEnsembleMapFromCurveSets( curveSets() ); auto ensemble = RiaSummaryTools::ensembleById( summaryAddr->ensembleId() ); if ( ensemble ) { for ( const auto& droppedAddress : newCurveAddresses ) { auto addresses = ensemble->ensembleSummaryAddresses(); if ( addresses.find( droppedAddress ) == addresses.end() ) continue; bool skipAddress = false; if ( dataVectorMap.count( droppedAddress ) > 0 ) { skipAddress = ( dataVectorMap[droppedAddress].count( ensemble ) > 0 ); } if ( !skipAddress ) { auto curveSet = RiaSummaryPlotTools::addNewEnsembleCurve( this, RiaSummaryCurveAddress( RifEclipseSummaryAddress::timeAddress(), droppedAddress ), ensemble ); curveSetsToUpdate.push_back( curveSet ); newCurves++; } } } } else { auto dataVectorMap = RiaSummaryAddressCollectionTools::buildAddressCaseMapFromCurves( summaryCurves() ); auto summaryCase = RiaSummaryTools::summaryCaseById( summaryAddr->caseId() ); if ( summaryCase ) { for ( const auto& droppedAddress : newCurveAddresses ) { if ( !summaryCase->summaryReader() || !summaryCase->summaryReader()->hasAddress( droppedAddress ) ) continue; bool skipAddress = false; if ( dataVectorMap.count( droppedAddress ) > 0 ) { skipAddress = ( dataVectorMap[droppedAddress].count( summaryCase ) > 0 ); } if ( !skipAddress ) { curves.push_back( addNewCurve( droppedAddress, summaryCase, RifEclipseSummaryAddress::timeAddress(), nullptr ) ); newCurves++; } } } } return { newCurves, curves, curveSetsToUpdate }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::handleDroppedObjects( const std::vector& objects ) { CurveInfo curveInfo; for ( auto obj : objects ) { if ( auto summaryCase = dynamic_cast( obj ) ) { curveInfo.appendCurveInfo( handleSummaryCaseDrop( summaryCase ) ); } else if ( auto ensemble = dynamic_cast( obj ) ) { curveInfo.appendCurveInfo( handleEnsembleDrop( ensemble ) ); } else if ( auto summaryAddr = dynamic_cast( obj ) ) { curveInfo.appendCurveInfo( handleSummaryAddressDrop( summaryAddr ) ); } else if ( auto addressCollection = dynamic_cast( obj ) ) { if ( addressCollection->isFolder() ) { for ( auto coll : addressCollection->subFolders() ) { auto localInfo = handleAddressCollectionDrop( coll ); curveInfo.appendCurveInfo( localInfo ); } } else { curveInfo.appendCurveInfo( handleAddressCollectionDrop( addressCollection ) ); } } } if ( curveInfo.curveCount > 0 ) { applyDefaultCurveAppearances( curveInfo.curves ); applyDefaultCurveAppearances( curveInfo.curveSets ); loadDataAndUpdate(); zoomAll(); curvesChanged.send(); } updateConnectedEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryCurve* RimSummaryPlot::addNewCurve( const RifEclipseSummaryAddress& address, RimSummaryCase* summaryCase, const RifEclipseSummaryAddress& addressX, RimSummaryCase* summaryCaseX ) { auto newCurve = RiaSummaryPlotTools::createCurve( summaryCase, address ); // This address is RifEclipseSummaryAddress::time() if the curve is a time plot. Otherwise it is the address of the summary vector // used for the x-axis if ( addressX.category() != RifEclipseSummaryAddressDefines::SummaryCategory::SUMMARY_TIME ) { newCurve->setAxisTypeX( RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ); newCurve->setSummaryAddressX( addressX ); newCurve->setSummaryCaseX( summaryCaseX ); } addCurveNoUpdate( newCurve ); return newCurve; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::onPlotZoomed() { // Disable auto scale in plot engine setAutoScaleXEnabled( false ); setAutoScaleYEnabled( false ); // The user has defined the visible range by interactive zoom, and the range is kept when the data source of the // plot is changed for ( const auto& ap : m_axisPropertiesArray ) { ap->setRangeUserDefined( true ); } // Disable auto value for min/max fields for ( auto p : plotAxes( RimPlotAxisProperties::Orientation::ANY ) ) { p->enableAutoValueMinMax( false ); } updateAxisRangesFromPlotWidget(); timeAxisSettingsChanged( nullptr ); axisChanged.send( this ); m_lastZoomTime = std::chrono::steady_clock::now(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { { auto group = uiOrdering.addNewGroup( "Data Source" ); m_sourceStepping()->uiOrdering( uiConfigName, *group ); } caf::PdmUiGroup* mainOptions = uiOrdering.addNewGroup( "General Plot Options" ); if ( isMainDockedWindow() ) { mainOptions->add( &m_showPlotTitle ); if ( m_showPlotTitle ) { mainOptions->add( &m_useAutoPlotTitle ); mainOptions->add( &m_description ); } } else { mainOptions->add( &m_useAutoPlotTitle ); mainOptions->add( &m_description ); mainOptions->add( &m_colSpan ); } mainOptions->add( &m_normalizeCurveYValues ); if ( hasDeclineCurves() ) mainOptions->add( &m_equationBoxPosition ); if ( isMainDockedWindow() ) { RimPlotWindow::uiOrderingForLegendsAndFonts( uiConfigName, uiOrdering ); } uiOrdering.skipRemainingFields( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiuPlotWidget* RimSummaryPlot::doCreatePlotViewWidget( QWidget* mainWindowParent ) { CAF_ASSERT( mainWindowParent ); if ( !plotWidget() ) { m_summaryPlot = new RiuSummaryQwtPlot( this, mainWindowParent ); QObject::connect( plotWidget(), SIGNAL( curveOrderNeedsUpdate() ), this, SLOT( onUpdateCurveOrder() ) ); for ( const auto& axisProperties : m_axisPropertiesArray ) { plotWidget()->ensureAxisIsCreated( axisProperties->plotAxis() ); } for ( RimGridTimeHistoryCurve* curve : m_gridTimeHistoryCurves ) { curve->setParentPlotNoReplot( plotWidget() ); } for ( RimAsciiDataCurve* curve : m_asciiDataCurves ) { curve->setParentPlotNoReplot( plotWidget() ); } if ( m_summaryCurveCollection ) { m_summaryCurveCollection->setParentPlotNoReplot( plotWidget() ); } if ( m_ensembleCurveSetCollection ) { m_ensembleCurveSetCollection->setParentPlotNoReplot( plotWidget() ); } connect( plotWidget(), SIGNAL( plotZoomed() ), SLOT( onPlotZoomed() ) ); updatePlotTitle(); } return plotWidget(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::deleteViewWidget() { deletePlotCurvesAndPlotWidget(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::initAfterRead() { RimViewWindow::initAfterRead(); if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2021.10.2" ) ) { auto copyAxis = [this]( RiuPlotAxis axis, auto sourceObject ) { auto axisProperties = axisPropertiesForPlotAxis( axis ); if ( axisProperties ) { QString data = sourceObject->writeObjectToXmlString(); // This operation will overwrite the plot axis side, default is left axisProperties->readObjectFromXmlString( data, caf::PdmDefaultObjectFactory::instance() ); auto plotAxisProperties = dynamic_cast( axisProperties ); if ( plotAxisProperties ) { // Reset the plot axis for the axis property plotAxisProperties->setNameAndAxis( axisProperties->objectName(), axisProperties->axisTitleText(), axis.axis(), 0 ); } } }; copyAxis( RiuPlotAxis::defaultLeft(), m_leftYAxisProperties_OBSOLETE.v() ); copyAxis( RiuPlotAxis::defaultRight(), m_rightYAxisProperties_OBSOLETE.v() ); copyAxis( RiuPlotAxis::defaultBottomForSummaryVectors(), m_bottomAxisProperties_OBSOLETE.v() ); copyAxis( RiuPlotAxis::defaultBottom(), m_timeAxisProperties_OBSOLETE.v() ); } for ( const auto& axisProperties : m_axisPropertiesArray ) { auto plotAxisProperties = dynamic_cast( axisProperties.p() ); if ( plotAxisProperties ) { connectAxisSignals( plotAxisProperties ); } auto* timeAxis = dynamic_cast( axisProperties.p() ); if ( timeAxis ) { timeAxis->settingsChanged.connect( this, &RimSummaryPlot::timeAxisSettingsChanged ); timeAxis->requestLoadDataAndUpdate.connect( this, &RimSummaryPlot::timeAxisSettingsChangedReloadRequired ); } } for ( auto curve : summaryCurves() ) { connectCurveSignals( curve ); } for ( auto curve : gridTimeHistoryCurves() ) { connectCurveSignals( curve ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateNameHelperWithCurveData( RimSummaryPlotNameHelper* nameHelper ) const { if ( !nameHelper ) return; nameHelper->clear(); std::vector addresses; std::vector sumCases; std::vector ensembleCases; if ( m_summaryCurveCollection && m_summaryCurveCollection->isCurvesVisible() ) { for ( RimSummaryCurve* curve : m_summaryCurveCollection->curves() ) { addresses.push_back( curve->curveAddress() ); sumCases.push_back( curve->summaryCaseY() ); if ( curve->summaryCaseX() ) { sumCases.push_back( curve->summaryCaseX() ); } } } for ( auto curveSet : m_ensembleCurveSetCollection->curveSets() ) { addresses.push_back( curveSet->curveAddress() ); ensembleCases.push_back( curveSet->summaryEnsemble() ); } nameHelper->clear(); nameHelper->appendAddresses( addresses ); nameHelper->setSummaryCases( sumCases ); nameHelper->setEnsembleCases( ensembleCases ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::doUpdateLayout() { updateFonts(); loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::detachAllPlotItems() { if ( m_summaryCurveCollection ) { m_summaryCurveCollection->detachPlotCurves(); } if ( m_ensembleCurveSetCollection ) { m_ensembleCurveSetCollection->detachPlotCurves(); } for ( RimGridTimeHistoryCurve* curve : m_gridTimeHistoryCurves ) { curve->detach(); } for ( RimAsciiDataCurve* curve : m_asciiDataCurves ) { curve->detach(); } m_plotInfoLabel->detach(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::deleteAllPlotCurves() { for ( auto* c : summaryCurves() ) { c->deletePlotCurve(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::updateCurveNames() { if ( m_summaryCurveCollection->isCurvesVisible() ) { for ( auto c : summaryCurves() ) { if ( c->isChecked() ) { c->updateCurveNameNoLegendUpdate(); } } } for ( auto curveSet : m_ensembleCurveSetCollection->curveSets() ) { curveSet->updateEnsembleLegendItem(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::detachAllCurves() { detachAllPlotItems(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::reattachAllCurves() { if ( m_summaryCurveCollection ) { m_summaryCurveCollection->reattachPlotCurves(); } m_ensembleCurveSetCollection->reattachPlotCurves(); for ( RimGridTimeHistoryCurve* curve : m_gridTimeHistoryCurves ) { curve->reattach(); } for ( RimAsciiDataCurve* curve : m_asciiDataCurves ) { curve->reattach(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::handleGlobalKeyEvent( QKeyEvent* keyEvent ) { return RimSummaryPlotControls::handleKeyEvents( sourceSteppingObjectForKeyEventHandling(), keyEvent ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::onCurveCollectionChanged( const SignalEmitter* emitter ) { curvesChanged.send(); scheduleRebuildEquationOverlays(); updateAndRedrawTimeAnnotations(); updateStackedCurveData(); scheduleReplotIfVisible(); updateAllRequiredEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::onPlotItemSelected( std::shared_ptr plotItem, bool toggle, int sampleIndex ) { auto wrapper = dynamic_cast( plotItem.get() ); if ( !wrapper ) return; auto qwtPlotItem = wrapper->qwtPlotItem(); if ( !qwtPlotItem ) return; auto riuPlotCurve = dynamic_cast( qwtPlotItem ); if ( !riuPlotCurve ) return; auto rimPlotCurve = riuPlotCurve->ownerRimCurve(); RiuPlotMainWindowTools::selectOrToggleObject( rimPlotCurve, toggle ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlotSourceStepping* RimSummaryPlot::sourceSteppingObjectForKeyEventHandling() const { auto axisTypes = m_summaryCurveCollection->horizontalAxisTypes(); if ( axisTypes.contains( RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) ) { return summaryCurveCollection()->sourceSteppingObject(); } return m_sourceStepping; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::setAutoScaleXEnabled( bool enabled ) { for ( const auto& ap : m_axisPropertiesArray ) { if ( ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_TOP || ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM ) { if ( enabled && ap->isRangeUserDefined() ) continue; ap->setAutoZoom( enabled ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::setAutoScaleYEnabled( bool enabled ) { for ( const auto& ap : m_axisPropertiesArray ) { if ( ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT || ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_RIGHT ) { if ( enabled && ap->isRangeUserDefined() ) continue; ap->setAutoZoom( enabled ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RimSummaryPlot::curveCount() const { return m_summaryCurveCollection->curves().size() + m_gridTimeHistoryCurves.size() + m_asciiDataCurves.size(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryPlot::isDeletable() const { auto plotWindow = firstAncestorOrThisOfType(); return plotWindow == nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::allPlotAxes() const { return m_axisPropertiesArray.childrenByType(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryPlot::plotAxes( RimPlotAxisProperties::Orientation orientation ) const { std::vector axisProps; for ( const auto& ap : m_axisPropertiesArray ) { auto plotAxisProp = dynamic_cast( ap.p() ); if ( !plotAxisProp ) continue; if ( ( orientation == RimPlotAxisProperties::Orientation::ANY ) || ( orientation == RimPlotAxisProperties::Orientation::VERTICAL && plotAxisProp->plotAxis().isVertical() ) ) { axisProps.push_back( plotAxisProp ); } else if ( ( orientation == RimPlotAxisProperties::Orientation::ANY ) || ( orientation == RimPlotAxisProperties::Orientation::HORIZONTAL && plotAxisProp->plotAxis().isHorizontal() ) ) { axisProps.push_back( plotAxisProp ); } } return axisProps; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::assignPlotAxis( RimSummaryCurve* destinationCurve ) { RimSummaryPlotTools::assignXPlotAxis( this, allPlotAxes(), destinationCurve ); RimSummaryPlotTools::assignYPlotAxis( this, allPlotAxes(), destinationCurve ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::assignPlotAxis( RimEnsembleCurveSet* curveSet ) { RimSummaryPlotTools::assignYPlotAxis( this, allPlotAxes(), curveSet ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector& referringObjects ) { if ( childArray == &m_axisPropertiesArray ) { for ( caf::PdmObjectHandle* reffingObj : referringObjects ) { auto* curve = dynamic_cast( reffingObj ); auto* curveSet = dynamic_cast( reffingObj ); if ( curve ) { curve->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() ); } else if ( curveSet ) { curveSet->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() ); } } if ( plotWidget() ) { std::set usedPlotAxis; for ( const auto& axisProperties : m_axisPropertiesArray ) { usedPlotAxis.insert( axisProperties->plotAxis() ); } plotWidget()->pruneAxes( usedPlotAxis ); updateAxes(); scheduleReplotIfVisible(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryPlotSourceStepping* RimSummaryPlot::sourceStepper() { return m_sourceStepping(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryPlot::onUpdateCurveOrder() { m_summaryCurveCollection->updateCurveOrder(); }