diff --git a/ApplicationLibCode/ProjectDataModel/RimMainPlotCollection.cpp b/ApplicationLibCode/ProjectDataModel/RimMainPlotCollection.cpp index f66ceac04a..c738d8da65 100644 --- a/ApplicationLibCode/ProjectDataModel/RimMainPlotCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimMainPlotCollection.cpp @@ -20,6 +20,7 @@ #include "RimMainPlotCollection.h" #include "RiaPlotCollectionScheduler.h" +#include "RiaSummaryDefines.h" #include "PlotBuilderCommands/RicSummaryPlotBuilder.h" @@ -42,6 +43,8 @@ #include "RimStimPlanModelPlotCollection.h" #include "RimSummaryAddress.h" #include "RimSummaryCrossPlotCollection.h" +#include "RimSummaryCurve.h" +#include "RimSummaryDataSourceStepping.h" #include "RimSummaryMultiPlot.h" #include "RimSummaryMultiPlotCollection.h" #include "RimSummaryPlotCollection.h" @@ -176,17 +179,51 @@ RimMainPlotCollection* RimMainPlotCollection::current() //-------------------------------------------------------------------------------------------------- void RimMainPlotCollection::initAfterRead() { - std::vector plotsToMove; - for ( auto singlePlot : m_summaryPlotCollection_OBSOLETE()->plots() ) { - plotsToMove.push_back( singlePlot ); + std::vector plotsToMove; + for ( auto singlePlot : m_summaryPlotCollection_OBSOLETE()->plots() ) + { + plotsToMove.push_back( singlePlot ); + } + + for ( auto singlePlot : plotsToMove ) + { + m_summaryPlotCollection_OBSOLETE()->removePlot( singlePlot ); + + RicSummaryPlotBuilder::createAndAppendSingleSummaryMultiPlotNoAutoSettings( singlePlot ); + } } - for ( auto singlePlot : plotsToMove ) + // Move cross plots into summary plot collection + auto crossPlots = m_summaryCrossPlotCollection_OBSOLETE->plots(); + if ( !crossPlots.empty() ) { - m_summaryPlotCollection_OBSOLETE()->removePlot( singlePlot ); + auto* summaryMultiPlot = new RimSummaryMultiPlot; + summaryMultiPlot->setMultiPlotTitle( QString( "Multi Plot %1" ).arg( m_summaryMultiPlotCollection->multiPlots().size() + 1 ) ); + summaryMultiPlot->setAsPlotMdiWindow(); + m_summaryMultiPlotCollection->addSummaryMultiPlot( summaryMultiPlot ); + + for ( auto crossPlot : crossPlots ) + { + m_summaryCrossPlotCollection_OBSOLETE->removePlot( crossPlot ); + summaryMultiPlot->addPlot( crossPlot ); - RicSummaryPlotBuilder::createAndAppendSingleSummaryMultiPlotNoAutoSettings( singlePlot ); + // We want to convert RimSummaryCrossPlot into a RimSummaryPlot. The cross plot is derived from RimSummaryPlot, but we need to + // create a new RimSummaryPlot to be able to store the PDM object as a RimSummaryPlot instead of RimSummaryCrossPlot + auto summaryPlot = new RimSummaryPlot; + summaryMultiPlot->addPlot( summaryPlot ); + + for ( auto curve : crossPlot->allCurves( RimSummaryDataSourceStepping::Axis::Y_AXIS ) ) + { + crossPlot->removeCurve( curve ); + + if ( curve->summaryCaseX() != nullptr ) curve->setAxisTypeX( RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ); + + summaryPlot->insertCurve( curve, std::numeric_limits::max() ); + } + + delete crossPlot; + } } } diff --git a/ApplicationLibCode/ProjectDataModel/RimProject.cpp b/ApplicationLibCode/ProjectDataModel/RimProject.cpp index b03393407c..9ad3c7c769 100644 --- a/ApplicationLibCode/ProjectDataModel/RimProject.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimProject.cpp @@ -1422,11 +1422,6 @@ void RimProject::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, Q uiTreeOrdering.add( m_mainPlotCollection->correlationPlotCollection() ); } - if ( m_mainPlotCollection->summaryCrossPlotCollection() ) - { - uiTreeOrdering.add( m_mainPlotCollection->summaryCrossPlotCollection() ); - } - if ( m_mainPlotCollection->summaryTableCollection() ) { uiTreeOrdering.add( m_mainPlotCollection->summaryTableCollection() ); diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp index bd2b10a1e4..e64cc7fb0b 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp @@ -18,6 +18,7 @@ #include "RimSummaryRegressionAnalysisCurve.h" +#include "RiaLogging.h" #include "RiaQDateTimeTools.h" #include "RiaRegressionTextTools.h" #include "RiaTimeTTools.h" @@ -28,6 +29,7 @@ #include "cafPdmUiDateEditor.h" #include "cafPdmUiLineEditor.h" #include "cafPdmUiSliderEditor.h" +#include "cafPdmUiSliderTools.h" #include "cafPdmUiTextEditor.h" #include "ExponentialRegression.hpp" @@ -94,6 +96,12 @@ RimSummaryRegressionAnalysisCurve::RimSummaryRegressionAnalysisCurve() m_expressionText.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); m_expressionText.uiCapability()->setUiReadOnly( true ); m_expressionText.xmlCapability()->disableIO(); + + CAF_PDM_InitField( &m_valueRangeX, "ValueRangeX", std::make_pair( 0.0, 0.0 ), "Value Range X" ); + m_valueRangeX.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); + + CAF_PDM_InitField( &m_valueRangeY, "ValueRangeY", std::make_pair( 0.0, 0.0 ), "Value Range Y" ); + m_valueRangeY.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); } //-------------------------------------------------------------------------------------------------- @@ -110,11 +118,68 @@ RimSummaryRegressionAnalysisCurve::~RimSummaryRegressionAnalysisCurve() //-------------------------------------------------------------------------------------------------- void RimSummaryRegressionAnalysisCurve::onLoadDataAndUpdate( bool updateParentPlot ) { + auto xValues = RimSummaryCurve::valuesX(); + auto yValues = RimSummaryCurve::valuesY(); + auto timeStepsX = RimSummaryCurve::timeStepsX(); + auto timeStepsY = RimSummaryCurve::timeStepsY(); + + if ( xValues.size() != yValues.size() ) return RiaLogging::error( "X value count and Y value count differs." ); + if ( xValues.size() != timeStepsX.size() ) return RiaLogging::error( "X value count and X time step count differs." ); + if ( xValues.size() != timeStepsY.size() ) return RiaLogging::error( "X value count and Y time step count differs." ); + + if ( timeStepsX != timeStepsY ) + { + return RiaLogging::error( + "Differences in time steps for X and Y axis detected. This is currently not supported. Make sure that the same " + "case is used for both axis." ); + } + + if ( axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) + { + // NB! Assume that time stamps for X and Y are the same + std::vector indicesToRemove; + + // Step 1: Find indices of values which are outside the specified range + for ( size_t i = 0; i < xValues.size(); i++ ) + { + if ( xValues[i] < m_valueRangeX().first || xValues[i] > m_valueRangeX().second ) + { + indicesToRemove.push_back( i ); + } + } + + for ( size_t i = 0; i < yValues.size(); i++ ) + { + if ( yValues[i] < m_valueRangeY().first || yValues[i] > m_valueRangeY().second ) + { + indicesToRemove.push_back( i ); + } + } + + // Step 2: Sort indices in descending order + std::sort( indicesToRemove.rbegin(), indicesToRemove.rend() ); + + // There might be duplicates, remove them + indicesToRemove.erase( std::unique( indicesToRemove.begin(), indicesToRemove.end() ), indicesToRemove.end() ); + + // Step 3: Remove elements at the specified indices + for ( auto index : indicesToRemove ) + { + if ( index < xValues.size() ) + { + xValues.erase( xValues.begin() + index ); + yValues.erase( yValues.begin() + index ); + timeStepsX.erase( timeStepsX.begin() + index ); + timeStepsY.erase( timeStepsY.begin() + index ); + } + } + } + QString descriptionX; - std::tie( m_timeStepsX, m_valuesX, descriptionX ) = computeRegressionCurve( RimSummaryCurve::timeStepsX(), RimSummaryCurve::valuesX() ); + std::tie( m_timeStepsX, m_valuesX, descriptionX ) = computeRegressionCurve( timeStepsX, xValues ); QString descriptionY; - std::tie( m_timeStepsY, m_valuesY, descriptionY ) = computeRegressionCurve( RimSummaryCurve::timeStepsY(), RimSummaryCurve::valuesY() ); + std::tie( m_timeStepsY, m_valuesY, descriptionY ) = computeRegressionCurve( timeStepsY, yValues ); m_expressionText = descriptionY; @@ -260,6 +325,15 @@ void RimSummaryRegressionAnalysisCurve::defineUiOrdering( QString uiConfigName, timeSelectionGroup->add( &m_maxTimeStep ); timeSelectionGroup->add( &m_showTimeSelectionInPlot ); + if ( axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR ) + { + caf::PdmUiGroup* valueRangeXGroup = uiOrdering.addNewGroup( "Value Range X" ); + valueRangeXGroup->add( &m_valueRangeX ); + + caf::PdmUiGroup* valueRangeYGroup = uiOrdering.addNewGroup( "Value Range Y" ); + valueRangeYGroup->add( &m_valueRangeY ); + } + caf::PdmUiGroup* forecastingGroup = uiOrdering.addNewGroup( "Forecasting" ); forecastingGroup->add( &m_forecastForward ); forecastingGroup->add( &m_forecastBackward ); @@ -288,7 +362,8 @@ void RimSummaryRegressionAnalysisCurve::fieldChangedByUi( const caf::PdmFieldHan RimSummaryCurve::fieldChangedByUi( changedField, oldValue, newValue ); if ( changedField == &m_regressionType || changedField == &m_polynomialDegree || changedField == &m_forecastBackward || changedField == &m_forecastForward || changedField == &m_forecastUnit || changedField == &m_minTimeStep || - changedField == &m_maxTimeStep || changedField == &m_showTimeSelectionInPlot ) + changedField == &m_maxTimeStep || changedField == &m_showTimeSelectionInPlot || changedField == &m_valueRangeX || + changedField == &m_valueRangeY ) { loadAndUpdateDataAndPlot(); @@ -349,6 +424,36 @@ void RimSummaryRegressionAnalysisCurve::defineEditorAttribute( const caf::PdmFie myAttr->font = font; } } + else if ( field == &m_valueRangeX ) + { + if ( auto attr = dynamic_cast( attribute ) ) + { + attr->m_decimals = 2; + attr->m_sliderTickCount = 100; + + auto values = RimSummaryCurve::valuesX(); + if ( !values.empty() ) + { + attr->m_minimum = *std::min_element( values.begin(), values.end() ); + attr->m_maximum = *std::max_element( values.begin(), values.end() ); + } + } + } + else if ( field == &m_valueRangeY ) + { + if ( auto attr = dynamic_cast( attribute ) ) + { + attr->m_decimals = 2; + attr->m_sliderTickCount = 100; + + auto values = RimSummaryCurve::valuesY(); + if ( !values.empty() ) + { + attr->m_minimum = *std::min_element( values.begin(), values.end() ); + attr->m_maximum = *std::max_element( values.begin(), values.end() ); + } + } + } } //-------------------------------------------------------------------------------------------------- @@ -551,6 +656,20 @@ void RimSummaryRegressionAnalysisCurve::updateDefaultValues() m_minTimeStep = timeSteps.front(); m_maxTimeStep = timeSteps.back(); } + + auto allValuesX = RimSummaryCurve::valuesX(); + if ( !allValuesX.empty() ) + { + m_valueRangeX = std::make_pair( *std::min_element( allValuesX.begin(), allValuesX.end() ), + *std::max_element( allValuesX.begin(), allValuesX.end() ) ); + } + + auto allValuesY = RimSummaryCurve::valuesY(); + if ( !allValuesY.empty() ) + { + m_valueRangeY = std::make_pair( *std::min_element( allValuesY.begin(), allValuesY.end() ), + *std::max_element( allValuesY.begin(), allValuesY.end() ) ); + } } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h index 6739656bf0..5fd94148de 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h @@ -114,6 +114,7 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve static void appendTimeSteps( std::vector& destinationTimeSteps, const std::set& sourceTimeSteps ); +private: caf::PdmField> m_regressionType; caf::PdmField m_minTimeStep; caf::PdmField m_maxTimeStep; @@ -125,6 +126,9 @@ class RimSummaryRegressionAnalysisCurve : public RimSummaryCurve caf::PdmField m_forecastBackward; caf::PdmField> m_forecastUnit; + caf::PdmField> m_valueRangeX; + caf::PdmField> m_valueRangeY; + caf::PdmPointer m_timeRangeAnnotation; std::vector m_valuesX; std::vector m_timeStepsX;