mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
Auto-assign Y-axis for ensemble curve sets based on unit/vector match
Extends the existing single-curve auto axis assignment (unit and summary vector matching, then alternating left/right) to also cover RimEnsembleCurveSet. Previously ensemble curve sets were always placed on the default left axis, so e.g. FOPT and FOPR ensembles ended up on the same axis despite differing units. - Add RimEnsembleCurveSet::unitNameY(), looked up from the first available summary case reader in the ensemble. - Move X and Y axis assignment from RimSummaryPlot to new RimSummaryPlotTools. Y-axis assignment takes a RiaSummaryAddressModifier::CurveAddressProvider, and compares against both curves and curve sets in the plot. - RimEnsembleCurveSetCollection::addCurveSet gains an autoAssignPlotAxis flag (default true, matching addCurveAndUpdate for single curves). Also fixes a bug in the summary plot editor where ensemble curve sets manually assigned to different Y-axes collapsed onto the same axis when clicking OK. The PdmPtrField axis reference does not resolve across the cloned preview and target plots. Add RimEnsembleCurveSet::findOrAssignLeftOrRightAxisY() to capture and re-apply the axis explicitly. Add RimSummaryPlot-Test.cpp with unit tests for axis auto-assignment of curves and ensemble curve sets. Fixes #14728
This commit is contained in:
1 parent
f32d59775b
commit
044454b9b1
13 files changed
+629
-264
No files matched your search
@@ -503,7 +503,6 @@ void appendCurvesToPlot( RimSummaryPlot* summaryPlot,
|
||||
{
|
||||
auto curveSet = createCurveSet( ensemble, addr );
|
||||
summaryPlot->ensembleCurveSetCollection()->addCurveSet( curveSet );
|
||||
curveSet->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() );
|
||||
}
|
||||
|
||||
for ( const auto summaryCase : summaryCases )
|
||||
@@ -536,7 +535,6 @@ RimEnsembleCurveSet* addNewEnsembleCurve( RimSummaryPlot* summaryPlot, const Ria
|
||||
|
||||
summaryPlot->ensembleCurveSetCollection()->addCurveSet( curveSet );
|
||||
|
||||
curveSet->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() );
|
||||
curveSet->setBottomOrTopAxisX( RiuPlotAxis::defaultBottomForSummaryVectors() );
|
||||
|
||||
summaryPlot->curvesChanged.send();
|
||||
|
||||
@@ -526,9 +526,12 @@ void RicSummaryPlotEditorUi::populateCurveCreator( const RimSummaryPlot& sourceS
|
||||
RimEnsembleCurveSetCollection* previewCurveSetColl = m_previewPlot->ensembleCurveSetCollection();
|
||||
for ( const auto& curveSet : sourceSummaryPlot.ensembleCurveSetCollection()->curveSets() )
|
||||
{
|
||||
auto sourceAxis = curveSet->axisY();
|
||||
|
||||
RimEnsembleCurveSet* newCurveSet = curveSet->clone();
|
||||
newCurveSet->disableStatisticCurves();
|
||||
previewCurveSetColl->addCurveSet( newCurveSet );
|
||||
previewCurveSetColl->addCurveSet( newCurveSet, false );
|
||||
newCurveSet->findOrAssignLeftOrRightAxisY( sourceAxis );
|
||||
|
||||
RimSummaryEnsemble* ensemble = curveSet->summaryEnsemble();
|
||||
curveDefs.emplace_back( ensemble, curveSet->summaryAddressY() );
|
||||
@@ -576,8 +579,11 @@ void RicSummaryPlotEditorUi::updateTargetPlot()
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sourceAxis = editedCurveSet->axisY();
|
||||
|
||||
RimEnsembleCurveSet* newCurveSet = editedCurveSet->clone();
|
||||
m_targetPlot->ensembleCurveSetCollection()->addCurveSet( newCurveSet );
|
||||
m_targetPlot->ensembleCurveSetCollection()->addCurveSet( newCurveSet, false );
|
||||
newCurveSet->findOrAssignLeftOrRightAxisY( sourceAxis );
|
||||
newCurveSet->setParentPlotNoReplot( m_targetPlot->plotWidget() );
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryEnsembleTools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryCaseUpdateBatch.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryPlotReadOut.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryPlotTools.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "Summary/RiaSummaryCurveDefinition.h"
|
||||
#include "Summary/RiaSummaryPlotTools.h"
|
||||
|
||||
#include "RifSummaryReaderInterface.h"
|
||||
|
||||
#include "SummaryPlotCommands/RicSummaryPlotEditorUi.h"
|
||||
|
||||
#include "RimCustomObjectiveFunction.h"
|
||||
@@ -612,6 +614,31 @@ void RimEnsembleCurveSet::findOrAssignBottomAxisX( RiuPlotAxis plotAxis )
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Assign the given Y-axis to this curve set, creating a new axis on the parent plot if the plot
|
||||
/// does not already have an axis of the given type. Used to preserve an explicitly selected axis
|
||||
/// when a curve set is copied between plots (e.g. the summary plot editor preview/target plots).
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleCurveSet::findOrAssignLeftOrRightAxisY( RiuPlotAxis plotAxis )
|
||||
{
|
||||
auto plot = firstAncestorOrThisOfType<RimSummaryPlot>();
|
||||
if ( !plot ) return;
|
||||
|
||||
auto axis = plot->axisPropertiesForPlotAxis( plotAxis );
|
||||
if ( !axis )
|
||||
{
|
||||
axis = plot->addNewAxisProperties( plotAxis, "New Axis" );
|
||||
plot->updateConnectedEditors();
|
||||
}
|
||||
|
||||
m_yPlotAxisProperties = axis;
|
||||
|
||||
for ( RimSummaryCurve* curve : curves() )
|
||||
{
|
||||
curve->setLeftOrRightAxisY( axisY() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -630,6 +657,25 @@ RifEclipseSummaryAddress RimEnsembleCurveSet::summaryAddressY() const
|
||||
return m_yValuesSummaryAddress->address();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string RimEnsembleCurveSet::unitNameY() const
|
||||
{
|
||||
auto ensemble = summaryEnsemble();
|
||||
if ( !ensemble ) return "";
|
||||
|
||||
for ( auto summaryCase : ensemble->allSummaryCases() )
|
||||
{
|
||||
if ( !summaryCase ) continue;
|
||||
|
||||
auto reader = summaryCase->summaryReader();
|
||||
if ( reader ) return reader->unitName( summaryAddressY() );
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
#include <QDate>
|
||||
#include <QPointer>
|
||||
|
||||
#include <string>
|
||||
|
||||
class RimSummaryCase;
|
||||
class RimSummaryEnsemble;
|
||||
class RimSummaryCurve;
|
||||
@@ -114,6 +116,7 @@ public:
|
||||
void setCurveAddress( RiaSummaryCurveAddress address );
|
||||
void setSummaryAddressYAndStatisticsFlag( RifEclipseSummaryAddress address );
|
||||
RifEclipseSummaryAddress summaryAddressY() const;
|
||||
std::string unitNameY() const;
|
||||
RiaSummaryCurveAddress curveAddress() const;
|
||||
std::vector<RimSummaryCurve*> curves() const;
|
||||
|
||||
@@ -189,6 +192,7 @@ public:
|
||||
bool isXAxisSummaryVector() const;
|
||||
RiaDefines::HorizontalAxisType xAxisType() const;
|
||||
void findOrAssignBottomAxisX( RiuPlotAxis plotAxis );
|
||||
void findOrAssignLeftOrRightAxisY( RiuPlotAxis plotAxis );
|
||||
|
||||
std::vector<RimTimeAxisAnnotation*> createTimeAnnotations() const;
|
||||
|
||||
|
||||
@@ -163,11 +163,17 @@ RimEnsembleCurveSet* RimEnsembleCurveSetCollection::findCurveSetFromPlotCurve( c
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEnsembleCurveSetCollection::addCurveSet( RimEnsembleCurveSet* curveSet )
|
||||
void RimEnsembleCurveSetCollection::addCurveSet( RimEnsembleCurveSet* curveSet, bool autoAssignPlotAxis )
|
||||
{
|
||||
if ( curveSet )
|
||||
{
|
||||
m_curveSets.push_back( curveSet );
|
||||
|
||||
if ( autoAssignPlotAxis )
|
||||
{
|
||||
auto plot = firstAncestorOrThisOfType<RimSummaryPlot>();
|
||||
if ( plot ) plot->assignPlotAxis( curveSet );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
RimSummaryCurve* findRimCurveFromPlotCurve( const RiuPlotCurve* qwtCurve ) const;
|
||||
RimEnsembleCurveSet* findCurveSetFromPlotCurve( const RiuPlotCurve* qwtCurve ) const;
|
||||
|
||||
void addCurveSet( RimEnsembleCurveSet* curveSet );
|
||||
void addCurveSet( RimEnsembleCurveSet* curveSet, bool autoAssignPlotAxis = true );
|
||||
void deleteCurveSet( RimEnsembleCurveSet* curveSet );
|
||||
void deleteCurveSets( const std::vector<RimEnsembleCurveSet*> curveSets );
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "RimSummaryPlotControls.h"
|
||||
#include "RimSummaryPlotNameHelper.h"
|
||||
#include "RimSummaryPlotTextProvider.h"
|
||||
#include "RimSummaryPlotTools.h"
|
||||
#include "RimSummaryTimeAxisProperties.h"
|
||||
#include "Summary/RiaSummaryAddressCollectionTools.h"
|
||||
#include "Summary/RiaSummaryCurveDefinition.h"
|
||||
@@ -61,7 +62,6 @@
|
||||
#include "Summary/RiaSummaryPlotTools.h"
|
||||
#include "Summary/RiaSummaryTools.h"
|
||||
#include "SummaryPlotCommands/RicSummaryPlotEditorUi.h"
|
||||
#include "Tools/RimPlotAxisTools.h"
|
||||
|
||||
#include "RiuAbstractOverlayContentFrame.h"
|
||||
#include "RiuDraggableOverlayFrame.h"
|
||||
@@ -3092,265 +3092,16 @@ std::vector<RimPlotAxisProperties*> RimSummaryPlot::plotAxes( RimPlotAxisPropert
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryPlot::assignPlotAxis( RimSummaryCurve* destinationCurve )
|
||||
{
|
||||
assignXPlotAxis( destinationCurve );
|
||||
assignYPlotAxis( destinationCurve );
|
||||
RimSummaryPlotTools::assignXPlotAxis( this, allPlotAxes(), destinationCurve );
|
||||
RimSummaryPlotTools::assignYPlotAxis( this, allPlotAxes(), destinationCurve );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
auto countAxes = []( const std::vector<RimPlotAxisPropertiesInterface*>& axes, RiaDefines::PlotAxis axis )
|
||||
{ return std::count_if( axes.begin(), axes.end(), [axis]( const auto& ap ) { return ap->plotAxis().axis() == axis; } ); };
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryPlot::assignYPlotAxis( RimSummaryCurve* curve )
|
||||
void RimSummaryPlot::assignPlotAxis( RimEnsembleCurveSet* curveSet )
|
||||
{
|
||||
enum class AxisAssignmentStrategy
|
||||
{
|
||||
ALTERNATING,
|
||||
USE_MATCHING_UNIT,
|
||||
USE_MATCHING_VECTOR
|
||||
};
|
||||
|
||||
auto strategy = AxisAssignmentStrategy::USE_MATCHING_UNIT;
|
||||
|
||||
auto destinationUnit = RiaStdStringTools::toUpper( curve->unitNameY() );
|
||||
if ( destinationUnit.empty() ) strategy = AxisAssignmentStrategy::USE_MATCHING_VECTOR;
|
||||
|
||||
auto anyCurveWithUnitText = [this, curve]
|
||||
{
|
||||
for ( auto c : summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
if ( !c->unitNameY().empty() ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if ( !anyCurveWithUnitText() ) strategy = AxisAssignmentStrategy::USE_MATCHING_VECTOR;
|
||||
|
||||
if ( strategy == AxisAssignmentStrategy::USE_MATCHING_VECTOR )
|
||||
{
|
||||
// Special handling if curve unit is matching. Try to match on summary vector name to avoid creation of new axis
|
||||
|
||||
for ( auto c : summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
auto incomingAxisText = RimPlotAxisTools::axisTextForAddress( curve->summaryAddressY() );
|
||||
auto currentAxisText = RimPlotAxisTools::axisTextForAddress( c->summaryAddressY() );
|
||||
if ( incomingAxisText == currentAxisText )
|
||||
{
|
||||
curve->setLeftOrRightAxisY( c->axisY() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::USE_MATCHING_UNIT )
|
||||
{
|
||||
for ( auto c : summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
auto currentUnit = RiaStdStringTools::toUpper( c->unitNameY() );
|
||||
if ( currentUnit == destinationUnit )
|
||||
{
|
||||
for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray )
|
||||
{
|
||||
if ( axisProperties->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT ||
|
||||
axisProperties->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_RIGHT )
|
||||
{
|
||||
curve->setLeftOrRightAxisY( c->axisY() );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strategy = AxisAssignmentStrategy::ALTERNATING;
|
||||
}
|
||||
|
||||
auto isDefaultLeftAndRightUsed = [this]( RimSummaryCurve* currentCurve ) -> std::pair<bool, bool>
|
||||
{
|
||||
bool defaultLeftUsed = false;
|
||||
bool defaultRightUsed = false;
|
||||
|
||||
for ( auto c : summaryCurves() )
|
||||
{
|
||||
if ( c == currentCurve ) continue;
|
||||
|
||||
if ( c->axisY() == RiuPlotAxis::defaultLeft() ) defaultLeftUsed = true;
|
||||
if ( c->axisY() == RiuPlotAxis::defaultRight() ) defaultRightUsed = true;
|
||||
}
|
||||
|
||||
return std::make_pair( defaultLeftUsed, defaultRightUsed );
|
||||
};
|
||||
|
||||
auto [defaultLeftUsed, defaultRightUsed] = isDefaultLeftAndRightUsed( curve );
|
||||
if ( !defaultLeftUsed )
|
||||
{
|
||||
curve->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !defaultRightUsed )
|
||||
{
|
||||
curve->setLeftOrRightAxisY( RiuPlotAxis::defaultRight() );
|
||||
return;
|
||||
}
|
||||
|
||||
RiaDefines::PlotAxis plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_LEFT;
|
||||
if ( strategy == AxisAssignmentStrategy::ALTERNATING )
|
||||
{
|
||||
size_t axisCountLeft = countAxes( m_axisPropertiesArray.childrenByType(), RiaDefines::PlotAxis::PLOT_AXIS_LEFT );
|
||||
size_t axisCountRight = countAxes( m_axisPropertiesArray.childrenByType(), RiaDefines::PlotAxis::PLOT_AXIS_RIGHT );
|
||||
|
||||
if ( axisCountLeft > axisCountRight ) plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_RIGHT;
|
||||
}
|
||||
|
||||
if ( plotWidget() && plotWidget()->isMultiAxisSupported() )
|
||||
{
|
||||
auto newPlotAxis = plotWidget()->createNextPlotAxis( plotAxisType );
|
||||
addNewAxisProperties( newPlotAxis, "New Axis" );
|
||||
|
||||
curve->setLeftOrRightAxisY( newPlotAxis );
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get here, we have no more axes to assign to, use left axis as fallback
|
||||
curve->setLeftOrRightAxisY( RiuPlotAxis::defaultLeft() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryPlot::assignXPlotAxis( RimSummaryCurve* curve )
|
||||
{
|
||||
RiuPlotAxis newPlotAxis = RimSummaryPlot::plotAxisForTime();
|
||||
|
||||
if ( curve->axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR )
|
||||
{
|
||||
enum class AxisAssignmentStrategy
|
||||
{
|
||||
ALL_TOP,
|
||||
ALL_BOTTOM,
|
||||
ALTERNATING,
|
||||
USE_MATCHING_UNIT,
|
||||
USE_MATCHING_VECTOR
|
||||
};
|
||||
|
||||
auto strategy = AxisAssignmentStrategy::USE_MATCHING_UNIT;
|
||||
|
||||
auto destinationUnit = RiaStdStringTools::toUpper( curve->unitNameX() );
|
||||
if ( destinationUnit.empty() ) strategy = AxisAssignmentStrategy::USE_MATCHING_VECTOR;
|
||||
|
||||
auto anyCurveWithUnitText = [this, curve]
|
||||
{
|
||||
for ( auto c : summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
if ( !c->unitNameX().empty() ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if ( !anyCurveWithUnitText() ) strategy = AxisAssignmentStrategy::USE_MATCHING_VECTOR;
|
||||
|
||||
if ( strategy == AxisAssignmentStrategy::USE_MATCHING_VECTOR )
|
||||
{
|
||||
// Special handling if curve unit is matching. Try to match on summary vector name to avoid creation of new axis
|
||||
|
||||
for ( auto c : summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
auto incomingAxisText = RimPlotAxisTools::axisTextForAddress( curve->summaryAddressY() );
|
||||
auto currentAxisText = RimPlotAxisTools::axisTextForAddress( c->summaryAddressY() );
|
||||
if ( incomingAxisText == currentAxisText )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( c->axisX() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::USE_MATCHING_UNIT )
|
||||
{
|
||||
bool isTopUsed = false;
|
||||
bool isBottomUsed = false;
|
||||
|
||||
for ( auto c : summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
if ( c->axisX() == RiuPlotAxis::defaultTop() ) isTopUsed = true;
|
||||
if ( c->axisX() == RiuPlotAxis::defaultBottomForSummaryVectors() ) isBottomUsed = true;
|
||||
|
||||
auto currentUnit = RiaStdStringTools::toUpper( c->unitNameX() );
|
||||
|
||||
if ( currentUnit == destinationUnit )
|
||||
{
|
||||
for ( RimPlotAxisPropertiesInterface* axisProperties : m_axisPropertiesArray )
|
||||
{
|
||||
if ( axisProperties->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_TOP ||
|
||||
axisProperties->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( c->axisX() );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isTopUsed )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( RiuPlotAxis::defaultTop() );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !isBottomUsed )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( RiuPlotAxis::defaultBottomForSummaryVectors() );
|
||||
return;
|
||||
}
|
||||
|
||||
strategy = AxisAssignmentStrategy::ALTERNATING;
|
||||
}
|
||||
|
||||
RiaDefines::PlotAxis plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_TOP;
|
||||
|
||||
if ( strategy == AxisAssignmentStrategy::ALTERNATING )
|
||||
{
|
||||
size_t axisCountTop = countAxes( m_axisPropertiesArray.childrenByType(), RiaDefines::PlotAxis::PLOT_AXIS_TOP );
|
||||
size_t axisCountBot = countAxes( m_axisPropertiesArray.childrenByType(), RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM );
|
||||
|
||||
if ( axisCountTop > axisCountBot ) plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM;
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::ALL_TOP )
|
||||
{
|
||||
plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_TOP;
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::ALL_BOTTOM )
|
||||
{
|
||||
plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM;
|
||||
}
|
||||
|
||||
RiuPlotAxis newPlotAxis = RiuPlotAxis::defaultBottomForSummaryVectors();
|
||||
if ( plotWidget() && plotWidget()->isMultiAxisSupported() )
|
||||
{
|
||||
newPlotAxis = plotWidget()->createNextPlotAxis( plotAxisType );
|
||||
addNewAxisProperties( newPlotAxis, "New Axis" );
|
||||
}
|
||||
}
|
||||
|
||||
curve->setTopOrBottomAxisX( newPlotAxis );
|
||||
RimSummaryPlotTools::assignYPlotAxis( this, allPlotAxes(), curveSet );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -207,6 +207,9 @@ public:
|
||||
RimPlotAxisProperties* addNewAxisProperties( RiaDefines::PlotAxis, const QString& name );
|
||||
RimPlotAxisProperties* addNewAxisProperties( RiuPlotAxis plotAxis, const QString& name );
|
||||
void findOrAssignPlotAxisX( RimSummaryCurve* curve );
|
||||
void assignPlotAxis( RimEnsembleCurveSet* curveSet );
|
||||
|
||||
static RiuPlotAxis plotAxisForTime();
|
||||
|
||||
std::vector<RimPlotCurve*> visibleCurvesForLegend() override;
|
||||
|
||||
@@ -303,8 +306,6 @@ private:
|
||||
|
||||
void ensureRequiredAxisObjectsForCurves();
|
||||
void assignPlotAxis( RimSummaryCurve* curve );
|
||||
void assignYPlotAxis( RimSummaryCurve* curve );
|
||||
void assignXPlotAxis( RimSummaryCurve* curve );
|
||||
|
||||
RimSummaryCurve* addNewCurve( const RifEclipseSummaryAddress& address,
|
||||
RimSummaryCase* summaryCase,
|
||||
@@ -324,8 +325,6 @@ private:
|
||||
|
||||
bool isOnlyWaterCutCurvesVisible( RiuPlotAxis plotAxis );
|
||||
|
||||
static RiuPlotAxis plotAxisForTime();
|
||||
|
||||
private:
|
||||
caf::PdmField<bool> m_normalizeCurveYValues;
|
||||
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026- Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimSummaryPlotTools.h"
|
||||
|
||||
#include "RiaStdStringTools.h"
|
||||
#include "Summary/RiaSummaryAddressModifier.h"
|
||||
|
||||
#include "RimEnsembleCurveSet.h"
|
||||
#include "RimPlotAxisPropertiesInterface.h"
|
||||
#include "RimSummaryCurve.h"
|
||||
#include "RimSummaryPlot.h"
|
||||
#include "Tools/RimPlotAxisTools.h"
|
||||
|
||||
#include "RiuPlotAxis.h"
|
||||
#include "RiuPlotWidget.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
|
||||
namespace internal
|
||||
{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t countAxes( const std::vector<RimPlotAxisPropertiesInterface*>& axes, RiaDefines::PlotAxis axis )
|
||||
{
|
||||
return std::count_if( axes.begin(), axes.end(), [axis]( const auto& ap ) { return ap->plotAxis().axis() == axis; } );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Unit, vector and axis of a curve or ensemble curve set already present in the plot, used to
|
||||
/// auto-assign a newly added curve or curve set to a matching or unused Y-axis.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
struct AxisCandidateInfo
|
||||
{
|
||||
std::string unitName;
|
||||
QString vectorAxisText;
|
||||
RiuPlotAxis axis;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
AxisCandidateInfo axisCandidateInfo( const RiaSummaryAddressModifier::CurveAddressProvider& provider )
|
||||
{
|
||||
return std::visit(
|
||||
[]( auto&& arg )
|
||||
{ return AxisCandidateInfo{ arg->unitNameY(), RimPlotAxisTools::axisTextForAddress( arg->summaryAddressY() ), arg->axisY() }; },
|
||||
provider );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Find the Y-axis to use for a curve or curve set, based on matching unit or summary vector of the other curves and curve sets.
|
||||
/// Creates a new axis if required.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RiuPlotAxis findOrCreateYPlotAxis( RimSummaryPlot* plot,
|
||||
const std::vector<RimPlotAxisPropertiesInterface*>& axisProperties,
|
||||
const AxisCandidateInfo& incoming,
|
||||
const std::vector<AxisCandidateInfo>& others )
|
||||
{
|
||||
enum class AxisAssignmentStrategy
|
||||
{
|
||||
ALTERNATING,
|
||||
USE_MATCHING_UNIT,
|
||||
USE_MATCHING_VECTOR
|
||||
};
|
||||
|
||||
auto strategy = AxisAssignmentStrategy::USE_MATCHING_UNIT;
|
||||
|
||||
auto destinationUnit = RiaStdStringTools::toUpper( incoming.unitName );
|
||||
bool anyOtherWithUnitText = std::any_of( others.begin(), others.end(), []( const auto& o ) { return !o.unitName.empty(); } );
|
||||
if ( destinationUnit.empty() || !anyOtherWithUnitText ) strategy = AxisAssignmentStrategy::USE_MATCHING_VECTOR;
|
||||
|
||||
if ( strategy == AxisAssignmentStrategy::USE_MATCHING_VECTOR )
|
||||
{
|
||||
// Special handling if curve unit is matching. Try to match on summary vector name to avoid creation of new axis
|
||||
for ( const auto& o : others )
|
||||
{
|
||||
if ( incoming.vectorAxisText == o.vectorAxisText ) return o.axis;
|
||||
}
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::USE_MATCHING_UNIT )
|
||||
{
|
||||
bool hasLeftOrRightAxis = std::any_of( axisProperties.begin(),
|
||||
axisProperties.end(),
|
||||
[]( const auto& ap )
|
||||
{
|
||||
return ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_LEFT ||
|
||||
ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_RIGHT;
|
||||
} );
|
||||
if ( hasLeftOrRightAxis )
|
||||
{
|
||||
for ( const auto& o : others )
|
||||
{
|
||||
if ( RiaStdStringTools::toUpper( o.unitName ) == destinationUnit ) return o.axis;
|
||||
}
|
||||
}
|
||||
|
||||
strategy = AxisAssignmentStrategy::ALTERNATING;
|
||||
}
|
||||
|
||||
bool defaultLeftUsed = std::any_of( others.begin(), others.end(), []( const auto& o ) { return o.axis == RiuPlotAxis::defaultLeft(); } );
|
||||
if ( !defaultLeftUsed ) return RiuPlotAxis::defaultLeft();
|
||||
|
||||
bool defaultRightUsed = std::any_of( others.begin(), others.end(), []( const auto& o ) { return o.axis == RiuPlotAxis::defaultRight(); } );
|
||||
if ( !defaultRightUsed ) return RiuPlotAxis::defaultRight();
|
||||
|
||||
RiaDefines::PlotAxis plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_LEFT;
|
||||
if ( strategy == AxisAssignmentStrategy::ALTERNATING )
|
||||
{
|
||||
size_t axisCountLeft = countAxes( axisProperties, RiaDefines::PlotAxis::PLOT_AXIS_LEFT );
|
||||
size_t axisCountRight = countAxes( axisProperties, RiaDefines::PlotAxis::PLOT_AXIS_RIGHT );
|
||||
|
||||
if ( axisCountLeft > axisCountRight ) plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_RIGHT;
|
||||
}
|
||||
|
||||
if ( plot->plotWidget() && plot->plotWidget()->isMultiAxisSupported() )
|
||||
{
|
||||
auto newPlotAxis = plot->plotWidget()->createNextPlotAxis( plotAxisType );
|
||||
plot->addNewAxisProperties( newPlotAxis, "New Axis" );
|
||||
|
||||
return newPlotAxis;
|
||||
}
|
||||
|
||||
// If we get here, we have no more axes to assign to, use left axis as fallback
|
||||
return RiuPlotAxis::defaultLeft();
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryPlotTools::assignYPlotAxis( RimSummaryPlot* plot,
|
||||
const std::vector<RimPlotAxisPropertiesInterface*>& axisProperties,
|
||||
const RiaSummaryAddressModifier::CurveAddressProvider& curveProvider )
|
||||
{
|
||||
std::vector<internal::AxisCandidateInfo> others;
|
||||
for ( const auto& provider : RiaSummaryAddressModifier::createAddressProviders( plot ) )
|
||||
{
|
||||
if ( provider != curveProvider ) others.push_back( internal::axisCandidateInfo( provider ) );
|
||||
}
|
||||
|
||||
const auto axis = internal::findOrCreateYPlotAxis( plot, axisProperties, internal::axisCandidateInfo( curveProvider ), others );
|
||||
|
||||
std::visit( [axis]( auto&& arg ) { arg->setLeftOrRightAxisY( axis ); }, curveProvider );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryPlotTools::assignXPlotAxis( RimSummaryPlot* plot,
|
||||
const std::vector<RimPlotAxisPropertiesInterface*>& axisProperties,
|
||||
RimSummaryCurve* curve )
|
||||
{
|
||||
RiuPlotAxis newPlotAxis = RimSummaryPlot::plotAxisForTime();
|
||||
|
||||
if ( curve->axisTypeX() == RiaDefines::HorizontalAxisType::SUMMARY_VECTOR )
|
||||
{
|
||||
enum class AxisAssignmentStrategy
|
||||
{
|
||||
ALL_TOP,
|
||||
ALL_BOTTOM,
|
||||
ALTERNATING,
|
||||
USE_MATCHING_UNIT,
|
||||
USE_MATCHING_VECTOR
|
||||
};
|
||||
|
||||
auto strategy = AxisAssignmentStrategy::USE_MATCHING_UNIT;
|
||||
|
||||
auto destinationUnit = RiaStdStringTools::toUpper( curve->unitNameX() );
|
||||
if ( destinationUnit.empty() ) strategy = AxisAssignmentStrategy::USE_MATCHING_VECTOR;
|
||||
|
||||
auto anyCurveWithUnitText = [plot, curve]
|
||||
{
|
||||
for ( auto c : plot->summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
if ( !c->unitNameX().empty() ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if ( !anyCurveWithUnitText() ) strategy = AxisAssignmentStrategy::USE_MATCHING_VECTOR;
|
||||
|
||||
if ( strategy == AxisAssignmentStrategy::USE_MATCHING_VECTOR )
|
||||
{
|
||||
// Special handling if curve unit is matching. Try to match on summary vector name to avoid creation of new axis
|
||||
|
||||
for ( auto c : plot->summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
auto incomingAxisText = RimPlotAxisTools::axisTextForAddress( curve->summaryAddressY() );
|
||||
auto currentAxisText = RimPlotAxisTools::axisTextForAddress( c->summaryAddressY() );
|
||||
if ( incomingAxisText == currentAxisText )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( c->axisX() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::USE_MATCHING_UNIT )
|
||||
{
|
||||
bool isTopUsed = false;
|
||||
bool isBottomUsed = false;
|
||||
|
||||
for ( auto c : plot->summaryCurves() )
|
||||
{
|
||||
if ( c == curve ) continue;
|
||||
|
||||
if ( c->axisX() == RiuPlotAxis::defaultTop() ) isTopUsed = true;
|
||||
if ( c->axisX() == RiuPlotAxis::defaultBottomForSummaryVectors() ) isBottomUsed = true;
|
||||
|
||||
auto currentUnit = RiaStdStringTools::toUpper( c->unitNameX() );
|
||||
|
||||
if ( currentUnit == destinationUnit )
|
||||
{
|
||||
for ( RimPlotAxisPropertiesInterface* ap : axisProperties )
|
||||
{
|
||||
if ( ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_TOP ||
|
||||
ap->plotAxis().axis() == RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( c->axisX() );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isTopUsed )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( RiuPlotAxis::defaultTop() );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !isBottomUsed )
|
||||
{
|
||||
curve->setTopOrBottomAxisX( RiuPlotAxis::defaultBottomForSummaryVectors() );
|
||||
return;
|
||||
}
|
||||
|
||||
strategy = AxisAssignmentStrategy::ALTERNATING;
|
||||
}
|
||||
|
||||
RiaDefines::PlotAxis plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_TOP;
|
||||
|
||||
if ( strategy == AxisAssignmentStrategy::ALTERNATING )
|
||||
{
|
||||
size_t axisCountTop = internal::countAxes( axisProperties, RiaDefines::PlotAxis::PLOT_AXIS_TOP );
|
||||
size_t axisCountBot = internal::countAxes( axisProperties, RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM );
|
||||
|
||||
if ( axisCountTop > axisCountBot ) plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM;
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::ALL_TOP )
|
||||
{
|
||||
plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_TOP;
|
||||
}
|
||||
else if ( strategy == AxisAssignmentStrategy::ALL_BOTTOM )
|
||||
{
|
||||
plotAxisType = RiaDefines::PlotAxis::PLOT_AXIS_BOTTOM;
|
||||
}
|
||||
|
||||
RiuPlotAxis newPlotAxis = RiuPlotAxis::defaultBottomForSummaryVectors();
|
||||
if ( plot->plotWidget() && plot->plotWidget()->isMultiAxisSupported() )
|
||||
{
|
||||
newPlotAxis = plot->plotWidget()->createNextPlotAxis( plotAxisType );
|
||||
plot->addNewAxisProperties( newPlotAxis, "New Axis" );
|
||||
}
|
||||
}
|
||||
|
||||
curve->setTopOrBottomAxisX( newPlotAxis );
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026- Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RiaPlotDefines.h"
|
||||
#include "Summary/RiaSummaryAddressModifier.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RimPlotAxisPropertiesInterface;
|
||||
class RimSummaryCurve;
|
||||
class RimSummaryPlot;
|
||||
|
||||
namespace RimSummaryPlotTools
|
||||
{
|
||||
// Assign a Y-axis based on matching unit or summary vector of the other curves and curve sets in the plot. Creates a new
|
||||
// axis if required.
|
||||
void assignYPlotAxis( RimSummaryPlot* plot,
|
||||
const std::vector<RimPlotAxisPropertiesInterface*>& axisProperties,
|
||||
const RiaSummaryAddressModifier::CurveAddressProvider& curveProvider );
|
||||
|
||||
// Assign an X-axis for curves using a summary vector as X-axis, based on matching unit or summary vector of the other curves in
|
||||
// the plot. Creates a new axis if required.
|
||||
void assignXPlotAxis( RimSummaryPlot* plot, const std::vector<RimPlotAxisPropertiesInterface*>& axisProperties, RimSummaryCurve* curve );
|
||||
} // namespace RimSummaryPlotTools
|
||||
@@ -97,6 +97,7 @@ set(SOURCE_UNITTEST_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryTimeAxisProperties-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDeltaSummaryEnsemble-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryEnsemble-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryPlot-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEnsembleCurveFilter-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifActiveCellsReader-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifCsvDataTableFormatter-Test.cpp
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026- Equinor ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "RifEclipseSummaryAddress.h"
|
||||
|
||||
#include "RimEnsembleCurveSet.h"
|
||||
#include "RimEnsembleCurveSetCollection.h"
|
||||
#include "RimMockSummaryCase.h"
|
||||
#include "RimSummaryCurve.h"
|
||||
#include "RimSummaryEnsemble.h"
|
||||
#include "RimSummaryPlot.h"
|
||||
|
||||
#include "RiuPlotAxis.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Regression tests for #14728: a newly added curve or ensemble curve set with a value range/unit
|
||||
/// differing from the curves already present in the plot should automatically be assigned to an
|
||||
/// unused secondary Y-axis, instead of always landing on the same axis as the existing curves.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
namespace
|
||||
{
|
||||
RimSummaryCurve* createCurve( const std::string& unitName, const RifEclipseSummaryAddress& address )
|
||||
{
|
||||
auto* summaryCase = static_cast<RimMockSummaryCase*>( createMockCase( 0 ) );
|
||||
summaryCase->addVector( address, { 0, 100 }, { 1.0, 2.0 }, unitName );
|
||||
|
||||
auto* curve = new RimSummaryCurve();
|
||||
curve->setSummaryCaseY( summaryCase );
|
||||
curve->setSummaryAddressY( address );
|
||||
|
||||
return curve;
|
||||
}
|
||||
|
||||
// RimEnsembleCurveSet::unitNameY() looks up the unit by querying the first summary case in the
|
||||
// ensemble that has a reader, so every mock case added to the ensemble must know about every
|
||||
// address used by any curve set in the same ensemble (mirroring how real ensembles share the same
|
||||
// set of summary vectors across all realizations).
|
||||
RimEnsembleCurveSet* createEnsembleCurveSet( RimSummaryEnsemble* ensemble, const std::string& unitName, const RifEclipseSummaryAddress& address )
|
||||
{
|
||||
for ( auto existingCase : ensemble->allSummaryCases() )
|
||||
{
|
||||
static_cast<RimMockSummaryCase*>( existingCase )->addVector( address, { 0, 100 }, { 1.0, 2.0 }, unitName );
|
||||
}
|
||||
|
||||
auto* summaryCase = static_cast<RimMockSummaryCase*>( createMockCase( 0 ) );
|
||||
summaryCase->addVector( address, { 0, 100 }, { 1.0, 2.0 }, unitName );
|
||||
ensemble->addCase( summaryCase, false );
|
||||
|
||||
auto* curveSet = new RimEnsembleCurveSet();
|
||||
curveSet->setSummaryEnsemble( ensemble );
|
||||
curveSet->setSummaryAddressY( address );
|
||||
|
||||
return curveSet;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The very first curve added to a plot has nothing to match against, so it must land on the default
|
||||
/// (left) axis.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_FirstCurveUsesDefaultLeftAxis )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
|
||||
auto* curve = createCurve( "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.addCurveAndUpdate( curve );
|
||||
|
||||
EXPECT_EQ( RiuPlotAxis::defaultLeft(), curve->axisY() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Two curves sharing the same unit are assumed to have comparable value ranges, so the second curve
|
||||
/// should reuse the first curve's axis instead of creating a new one.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_MatchingUnitCurveReusesSameAxis )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
|
||||
auto* curveA = createCurve( "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.addCurveAndUpdate( curveA );
|
||||
|
||||
auto* curveB = createCurve( "SM3", RifEclipseSummaryAddress::wellAddress( "WOPT", "W-1" ) );
|
||||
plot.addCurveAndUpdate( curveB );
|
||||
|
||||
EXPECT_EQ( curveA->axisY(), curveB->axisY() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A curve with a different unit than the curves already in the plot (e.g. a rate vector added
|
||||
/// alongside a cumulative vector) must be automatically assigned to the unused secondary (right)
|
||||
/// axis rather than being added to the same axis as the existing curve.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_DifferingUnitCurveUsesSecondaryAxis )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
|
||||
auto* fopt = createCurve( "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.addCurveAndUpdate( fopt );
|
||||
|
||||
auto* fopr = createCurve( "SM3/DAY", RifEclipseSummaryAddress::fieldAddress( "FOPR" ) );
|
||||
plot.addCurveAndUpdate( fopr );
|
||||
|
||||
EXPECT_EQ( RiuPlotAxis::defaultLeft(), fopt->axisY() );
|
||||
EXPECT_EQ( RiuPlotAxis::defaultRight(), fopr->axisY() );
|
||||
EXPECT_NE( fopt->axisY(), fopr->axisY() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Same as AssignPlotAxis_FirstCurveUsesDefaultLeftAxis, but for an ensemble curve set. Prior to the
|
||||
/// fix for #14728, ensemble curve sets had no automatic axis assignment logic at all and were always
|
||||
/// forced onto the default left axis regardless of their unit.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_FirstEnsembleCurveSetUsesDefaultLeftAxis )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
RimSummaryEnsemble ensemble;
|
||||
ensemble.setAsEnsemble( true );
|
||||
|
||||
auto* curveSet = createEnsembleCurveSet( &ensemble, "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.ensembleCurveSetCollection()->addCurveSet( curveSet );
|
||||
|
||||
EXPECT_EQ( RiuPlotAxis::defaultLeft(), curveSet->axisY() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Two ensemble curve sets with differing units (e.g. FOPT and FOPR) must be automatically assigned
|
||||
/// to different axes.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_DifferingUnitEnsembleCurveSetsUseDifferentAxes )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
RimSummaryEnsemble ensemble;
|
||||
ensemble.setAsEnsemble( true );
|
||||
|
||||
auto* foptCurveSet = createEnsembleCurveSet( &ensemble, "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.ensembleCurveSetCollection()->addCurveSet( foptCurveSet );
|
||||
|
||||
auto* foprCurveSet = createEnsembleCurveSet( &ensemble, "SM3/DAY", RifEclipseSummaryAddress::fieldAddress( "FOPR" ) );
|
||||
plot.ensembleCurveSetCollection()->addCurveSet( foprCurveSet );
|
||||
|
||||
EXPECT_EQ( RiuPlotAxis::defaultLeft(), foptCurveSet->axisY() );
|
||||
EXPECT_EQ( RiuPlotAxis::defaultRight(), foprCurveSet->axisY() );
|
||||
EXPECT_NE( foptCurveSet->axisY(), foprCurveSet->axisY() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Two ensemble curve sets sharing the same unit are assumed to have comparable value ranges, and
|
||||
/// should reuse the same axis to avoid creating unnecessary additional axes.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_MatchingUnitEnsembleCurveSetsReuseSameAxis )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
RimSummaryEnsemble ensemble;
|
||||
ensemble.setAsEnsemble( true );
|
||||
|
||||
auto* curveSetA = createEnsembleCurveSet( &ensemble, "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.ensembleCurveSetCollection()->addCurveSet( curveSetA );
|
||||
|
||||
auto* curveSetB = createEnsembleCurveSet( &ensemble, "SM3", RifEclipseSummaryAddress::fieldAddress( "FGPT" ) );
|
||||
plot.ensembleCurveSetCollection()->addCurveSet( curveSetB );
|
||||
|
||||
EXPECT_EQ( curveSetA->axisY(), curveSetB->axisY() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A single curve and an ensemble curve set with differing units must be assigned to different axes,
|
||||
/// verifying that curves and ensemble curve sets are compared against each other and not just within
|
||||
/// their own kind.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_SingleCurveAndEnsembleCurveSetWithDifferingUnitsUseDifferentAxes )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
RimSummaryEnsemble ensemble;
|
||||
ensemble.setAsEnsemble( true );
|
||||
|
||||
auto* singleCurve = createCurve( "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.addCurveAndUpdate( singleCurve );
|
||||
|
||||
auto* curveSet = createEnsembleCurveSet( &ensemble, "SM3/DAY", RifEclipseSummaryAddress::fieldAddress( "FOPR" ) );
|
||||
plot.ensembleCurveSetCollection()->addCurveSet( curveSet );
|
||||
|
||||
EXPECT_EQ( RiuPlotAxis::defaultLeft(), singleCurve->axisY() );
|
||||
EXPECT_EQ( RiuPlotAxis::defaultRight(), curveSet->axisY() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Same as above, but with the ensemble curve set added first and the single curve with a differing
|
||||
/// unit added second.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimSummaryPlot, AssignPlotAxis_EnsembleCurveSetAndSingleCurveWithDifferingUnitsUseDifferentAxes )
|
||||
{
|
||||
RimSummaryPlot plot;
|
||||
RimSummaryEnsemble ensemble;
|
||||
ensemble.setAsEnsemble( true );
|
||||
|
||||
auto* curveSet = createEnsembleCurveSet( &ensemble, "SM3", RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
plot.ensembleCurveSetCollection()->addCurveSet( curveSet );
|
||||
|
||||
auto* singleCurve = createCurve( "SM3/DAY", RifEclipseSummaryAddress::fieldAddress( "FOPR" ) );
|
||||
plot.addCurveAndUpdate( singleCurve );
|
||||
|
||||
EXPECT_EQ( RiuPlotAxis::defaultLeft(), curveSet->axisY() );
|
||||
EXPECT_EQ( RiuPlotAxis::defaultRight(), singleCurve->axisY() );
|
||||
}
|
||||
Reference in new issue
Block a user