Move time-step control and grid-only hooks from Rim3dView to RimGridView

* #14760 Move time-step control and grid-only hooks from Rim3dView to RimGridView
* #14760 Store named 3D visualization models in a shared collection instead of per-model fields
This commit is contained in:
Magne Sjaastad authored and GitHub committed 2026-09-16 08:44:00 +02:00
1 parent f73afc5b76
commit 0579dbb468
16 files changed
+362 -237

No files matched your search

@@ -55,6 +55,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RivRefinementRegionPartMgr.cpp
${CMAKE_CURRENT_LIST_DIR}/RivAnnotationTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RivAnnotationSourceInfo.cpp
${CMAKE_CURRENT_LIST_DIR}/RivNamedVisualizationModels.cpp
)
list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
@@ -0,0 +1,77 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RivNamedVisualizationModels.h"
// ModelBasicList's implicit destructor needs cvf::Part to be a complete type (it holds a
// cvf::Collection<Part>), which is only forward-declared in cvfModelBasicList.h.
#include "cvfPart.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ModelBasicList* RivNamedVisualizationModels::findOrCreate( const cvf::String& modelName )
{
auto it = m_models.find( modelName );
if ( it != m_models.end() ) return it->second.p();
cvf::ref<cvf::ModelBasicList> model = new cvf::ModelBasicList;
model->setName( modelName );
return m_models.emplace( modelName, model ).first->second.p();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ModelBasicList* RivNamedVisualizationModels::findOrCreateAndClear( const cvf::String& modelName )
{
auto* model = findOrCreate( modelName );
model->removeAllParts();
return model;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ModelBasicList* RivNamedVisualizationModels::find( const cvf::String& modelName ) const
{
auto it = m_models.find( modelName );
return it != m_models.end() ? const_cast<cvf::ModelBasicList*>( it->second.p() ) : nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<cvf::ModelBasicList*> RivNamedVisualizationModels::allModels() const
{
std::vector<cvf::ModelBasicList*> models;
for ( const auto& [name, model] : m_models )
{
models.push_back( const_cast<cvf::ModelBasicList*>( model.p() ) );
}
return models;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivNamedVisualizationModels::clear()
{
m_models.clear();
}
@@ -0,0 +1,59 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "cvfModelBasicList.h"
#include "cvfObject.h"
#include "cvfString.h"
#include <map>
#include <vector>
//==================================================================================================
///
/// Named collection of cvf::ModelBasicList visualization models, created lazily by name.
///
/// The names of the well-known models shared across several 3D view types are centralized here as
/// static accessors, so the string literals identifying a given model are defined in a single place
///
//==================================================================================================
class RivNamedVisualizationModels
{
public:
static const char* wellPathPipeModelName() { return "WellPathPipeModel"; }
static const char* seismicSectionModelName() { return "SeismicSectionModel"; }
static const char* surfaceModelName() { return "SurfaceModel"; }
static const char* polygonModelName() { return "PolygonModel"; }
static const char* crossSectionModelName() { return "CrossSectionModel"; }
static const char* intersectionModelName() { return "IntersectionModel"; }
static const char* faultReactivationModelName() { return "FaultReactModel"; }
static const char* refinementRegionsModelName() { return "RefinementRegionsModel"; }
static const char* highlightModelName() { return "HighlightModel"; }
static const char* screenSpaceModelName() { return "ScreenSpaceModel"; }
cvf::ModelBasicList* findOrCreate( const cvf::String& modelName );
cvf::ModelBasicList* findOrCreateAndClear( const cvf::String& modelName );
cvf::ModelBasicList* find( const cvf::String& modelName ) const;
std::vector<cvf::ModelBasicList*> allModels() const;
void clear();
private:
std::map<cvf::String, cvf::ref<cvf::ModelBasicList>> m_models;
};
@@ -306,10 +306,10 @@ void RimGeoMechView::onCreateDisplayModel()
cvf::BoundingBox femBBox = femParts()->boundingBox();
m_wellPathPipeVizModel->removeAllParts();
addWellPathsToModel( m_wellPathPipeVizModel.p(), femBBox, ownerCase()->characteristicCellSize() );
auto* wellPathPipeVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::wellPathPipeModelName() );
addWellPathsToModel( wellPathPipeVizModel, femBBox, ownerCase()->characteristicCellSize() );
nativeOrOverrideViewer()->addStaticModelOnce( m_wellPathPipeVizModel.p(), isUsingOverrideViewer() );
nativeOrOverrideViewer()->addStaticModelOnce( wellPathPipeVizModel, isUsingOverrideViewer() );
// Intersections
@@ -318,20 +318,20 @@ void RimGeoMechView::onCreateDisplayModel()
// Seismic sections
cvf::ref<caf::DisplayCoordTransform> transform = displayCoordTransform();
m_seismicVizModel->removeAllParts();
m_seismicSectionCollection->appendPartsToModel( this, m_seismicVizModel.p(), transform.p(), femBBox );
nativeOrOverrideViewer()->addStaticModelOnce( m_seismicVizModel.p(), isUsingOverrideViewer() );
auto* seismicVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::seismicSectionModelName() );
m_seismicSectionCollection->appendPartsToModel( this, seismicVizModel, transform.p(), femBBox );
nativeOrOverrideViewer()->addStaticModelOnce( seismicVizModel, isUsingOverrideViewer() );
// Polygons
appendPolygonPartsToModel( transform.p(), ownerCase()->allCellsBoundingBox() );
// Surfaces
m_surfaceVizModel->removeAllParts();
auto* surfaceVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::surfaceModelName() );
if ( m_surfaceCollection )
{
m_surfaceCollection->appendPartsToModel( m_surfaceVizModel.p(), scaleTransform() );
nativeOrOverrideViewer()->addStaticModelOnce( m_surfaceVizModel.p(), isUsingOverrideViewer() );
m_surfaceCollection->appendPartsToModel( surfaceVizModel, scaleTransform() );
nativeOrOverrideViewer()->addStaticModelOnce( surfaceVizModel, isUsingOverrideViewer() );
}
// If the animation was active before recreating everything, make viewer view current frame
@@ -89,9 +89,8 @@ Rim2dIntersectionView::Rim2dIntersectionView()
m_nameProxy.registerGetMethod( this, &Rim2dIntersectionView::getName );
m_nameProxy.registerSetMethod( this, &Rim2dIntersectionView::setName );
m_showWindow = false;
m_scaleTransform = new cvf::Transform();
m_intersectionVizModel = new cvf::ModelBasicList;
m_showWindow = false;
m_scaleTransform = new cvf::Transform();
( (RiuViewerToViewInterface*)this )->setCameraPosition( sm_defaultViewMatrix );
@@ -147,16 +146,6 @@ RimExtrudedCurveIntersection* Rim2dIntersectionView::intersection() const
return m_intersection();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool Rim2dIntersectionView::isUsingFormationNames() const
{
// Todo:
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -548,16 +537,18 @@ void Rim2dIntersectionView::onCreateDisplayModel()
if ( settingsView && ownerCase() )
{
m_flatWellpathPartMgr = new RivWellPathPartMgr( m_intersection->wellPath(), settingsView );
m_flatWellpathPartMgr->appendFlattenedStaticGeometryPartsToModel( m_intersectionVizModel.p(),
m_flatWellpathPartMgr->appendFlattenedStaticGeometryPartsToModel( m_vizModels.findOrCreate(
RivNamedVisualizationModels::intersectionModelName() ),
displayCoordTransform().p(),
ownerCase()->characteristicCellSize(),
ownerCase()->activeCellsBoundingBox() );
}
}
nativeOrOverrideViewer()->addStaticModelOnce( m_intersectionVizModel.p(), isUsingOverrideViewer() );
nativeOrOverrideViewer()->addStaticModelOnce( m_vizModels.findOrCreate( RivNamedVisualizationModels::intersectionModelName() ),
isUsingOverrideViewer() );
m_intersectionVizModel->updateBoundingBoxesRecursive();
m_vizModels.findOrCreate( RivNamedVisualizationModels::intersectionModelName() )->updateBoundingBoxesRecursive();
if ( viewer() )
{
@@ -760,13 +751,6 @@ void Rim2dIntersectionView::onResetLegendsInViewer()
m_legendConfig()->recreateLegend();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void Rim2dIntersectionView::onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part>* parts )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -774,13 +758,6 @@ void Rim2dIntersectionView::onClampCurrentTimestep()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void Rim2dIntersectionView::onUpdateStaticCellColors()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -853,7 +830,7 @@ void Rim2dIntersectionView::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTree
//--------------------------------------------------------------------------------------------------
void Rim2dIntersectionView::appendIntersectionToModel( bool cellFiltersActive, bool propertyFiltersActive )
{
m_intersectionVizModel->removeAllParts();
auto* intersectionVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::intersectionModelName() );
RimEclipseView* eclView = m_intersection->firstAncestorOrThisOfType<RimEclipseView>();
RimGeoMechView* geoView = m_intersection->firstAncestorOrThisOfType<RimGeoMechView>();
@@ -866,7 +843,7 @@ void Rim2dIntersectionView::appendIntersectionToModel( bool cellFiltersActive, b
{
cvf::UByteArray visibleCells;
eclView->calculateCellVisibility( &visibleCells, { RANGE_FILTERED_WELL_CELLS, RANGE_FILTERED }, m_currentTimeStep );
appendDynamicPartsToModel( m_intersectionVizModel.p(), scaleTransform(), &visibleCells );
appendDynamicPartsToModel( intersectionVizModel, scaleTransform(), &visibleCells );
}
}
else if ( geoView )
@@ -875,24 +852,24 @@ void Rim2dIntersectionView::appendIntersectionToModel( bool cellFiltersActive, b
{
cvf::UByteArray visibleCells;
geoView->calculateCurrentTotalCellVisibility( &visibleCells, m_currentTimeStep );
appendDynamicPartsToModel( m_intersectionVizModel.p(), scaleTransform(), &visibleCells );
appendDynamicPartsToModel( intersectionVizModel, scaleTransform(), &visibleCells );
}
}
// NB! Geometry objects are recreated in appendDynamicPartsToModel(), always call
// appendPartsToModel() after appendDynamicPartsToModel()
appendPartsToModel( m_intersectionVizModel.p(), scaleTransform() );
appendPartsToModel( intersectionVizModel, scaleTransform() );
}
else
{
appendDynamicPartsToModel( m_intersectionVizModel.p(), scaleTransform(), nullptr );
appendDynamicPartsToModel( intersectionVizModel, scaleTransform(), nullptr );
// NB! Geometry objects are recreated in appendDynamicPartsToModel(), always call
// appendPartsToModel() after appendDynamicPartsToModel()
appendPartsToModel( m_intersectionVizModel.p(), scaleTransform() );
appendPartsToModel( intersectionVizModel, scaleTransform() );
}
m_intersectionVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( m_intersectionVizModel.p(), isUsingOverrideViewer() );
intersectionVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( intersectionVizModel, isUsingOverrideViewer() );
}
//--------------------------------------------------------------------------------------------------
@@ -55,7 +55,6 @@ public:
void setIntersection( RimExtrudedCurveIntersection* intersection );
RimExtrudedCurveIntersection* intersection() const;
bool isUsingFormationNames() const override;
void scheduleGeometryRegen( RivCellSetEnum geometryType ) override;
RimCase* ownerCase() const override;
void selectOverlayInfoConfig() override {}
@@ -82,10 +81,8 @@ protected:
bool isGridVisualizationMode() const override;
void defineAxisLabels( cvf::String* xLabel, cvf::String* yLabel, cvf::String* zLabel ) override;
void onCreateDisplayModel() override;
void onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part>* parts ) override;
void onClampCurrentTimestep() override;
void onUpdateDisplayModelForCurrentTimeStep() override;
void onUpdateStaticCellColors() override;
cvf::Transform* scaleTransform() override;
void onResetLegendsInViewer() override;
void onLoadDataAndUpdate() override;
@@ -119,7 +116,6 @@ private:
cvf::ref<RivSimWellPipesPartMgr> m_flatSimWellPipePartMgr;
cvf::ref<RivWellHeadPartMgr> m_flatWellHeadPartMgr;
cvf::ref<RivWellPathPartMgr> m_flatWellpathPartMgr;
cvf::ref<cvf::ModelBasicList> m_intersectionVizModel;
cvf::ref<cvf::Transform> m_scaleTransform;
caf::PdmProxyValueField<QString> m_nameProxy;
@@ -177,15 +177,6 @@ Rim3dView::Rim3dView()
"Use Custom Annotation Strategy",
"Specify the strategy to be applied on all screen space annotations." );
m_seismicVizModel = new cvf::ModelBasicList;
m_seismicVizModel->setName( "SeismicSectionModel" );
m_highlightVizModel = new cvf::ModelBasicList;
m_highlightVizModel->setName( "HighlightModel" );
m_wellPathPipeVizModel = new cvf::ModelBasicList;
m_wellPathPipeVizModel->setName( "WellPathPipeModel" );
m_wellPathsPartManager = new RivWellPathsPartMgr( this );
m_annotationsPartManager = new RivAnnotationsPartMgr( this );
m_measurementPartManager = new RivMeasurementPartMgr( this );
@@ -1415,9 +1406,11 @@ void Rim3dView::createHighlightAndGridBoxDisplayModel()
{
if ( !nativeOrOverrideViewer() ) return;
nativeOrOverrideViewer()->removeStaticModel( m_highlightVizModel.p() );
auto* highlightVizModel = m_vizModels.findOrCreate( RivNamedVisualizationModels::highlightModelName() );
m_highlightVizModel->removeAllParts();
nativeOrOverrideViewer()->removeStaticModel( highlightVizModel );
highlightVizModel->removeAllParts();
cvf::Collection<cvf::Part> parts;
onCreatePartCollectionFromSelection( &parts );
@@ -1425,11 +1418,11 @@ void Rim3dView::createHighlightAndGridBoxDisplayModel()
{
for ( size_t i = 0; i < parts.size(); i++ )
{
m_highlightVizModel->addPart( parts[i].p() );
highlightVizModel->addPart( parts[i].p() );
}
m_highlightVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( m_highlightVizModel.p(), isUsingOverrideViewer() );
highlightVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( highlightVizModel, isUsingOverrideViewer() );
}
updateGridBoxData();
@@ -1773,12 +1766,7 @@ void Rim3dView::updateScreenSpaceModel()
{
if ( !m_viewer || !m_viewer->mainCamera() ) return;
if ( m_screenSpaceModel.isNull() )
{
m_screenSpaceModel = new cvf::ModelBasicList;
m_screenSpaceModel->setName( "ScreenSpaceModel" );
}
m_screenSpaceModel->removeAllParts();
auto* screenSpaceModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::screenSpaceModelName() );
// Build annotation parts and put into screen space model
cvf::Collection<cvf::Part> partCollection;
@@ -1793,9 +1781,9 @@ void Rim3dView::updateScreenSpaceModel()
// The scaling factor is computed using the camera, and this does not work for the flat intersection view
bool computeScalingFactor = ( viewContent() != RiaDefines::View3dContent::FLAT_INTERSECTION );
annoTool.addAnnotationLabels( partCollection, m_viewer->mainCamera(), m_screenSpaceModel.p(), computeScalingFactor );
annoTool.addAnnotationLabels( partCollection, m_viewer->mainCamera(), screenSpaceModel, computeScalingFactor );
nativeOrOverrideViewer()->addStaticModelOnce( m_screenSpaceModel.p(), isUsingOverrideViewer() );
nativeOrOverrideViewer()->addStaticModelOnce( screenSpaceModel, isUsingOverrideViewer() );
}
//--------------------------------------------------------------------------------------------------
+19 -15
View File
@@ -27,6 +27,7 @@
#include "RivAnnotationTools.h"
#include "RivCellSetEnum.h"
#include "RivNamedVisualizationModels.h"
#include "cafAppEnum.h"
#include "cafPdmField.h"
@@ -113,7 +114,8 @@ public:
caf::PdmField<caf::AppEnum<RiaDefines::MeshModeType>> meshMode;
caf::PdmField<caf::AppEnum<SurfaceModeType>> surfaceMode;
virtual RimCase* ownerCase() const = 0;
// Default: no owner case. Grid views (RimGridView) re-abstract this.
virtual RimCase* ownerCase() const { return nullptr; }
RiuViewer* viewer() const;
void setName( const QString& name );
@@ -143,7 +145,7 @@ public:
void disableLighting( bool disable );
bool isLightingDisabled() const;
virtual bool isUsingFormationNames() const = 0;
virtual bool isUsingFormationNames() const { return false; }
cvf::ref<caf::DisplayCoordTransform> displayCoordTransform() const override;
virtual std::vector<RimLegendConfig*> legendConfigs() const = 0;
@@ -247,16 +249,21 @@ protected:
virtual void onResetLegendsInViewer();
virtual void onUpdateScaleTransform();
virtual void onCreateDisplayModel() = 0;
virtual void onUpdateDisplayModelForCurrentTimeStep() = 0;
virtual void onClampCurrentTimestep() = 0;
virtual size_t onTimeStepCountRequested() = 0;
virtual void onCreateDisplayModel() = 0;
// Time step control. Default: no time step (single static frame). Grid views re-abstract these.
virtual void onUpdateDisplayModelForCurrentTimeStep() {}
virtual void onClampCurrentTimestep() { m_currentTimeStep = 0; }
virtual size_t onTimeStepCountRequested() { return 1; }
virtual bool isTimeStepDependentDataVisible() const { return false; }
virtual bool isTimeStepDependentDataVisible() const = 0;
virtual void defineAxisLabels( cvf::String* xLabel, cvf::String* yLabel, cvf::String* zLabel ) = 0;
virtual void onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part>* parts ) = 0;
virtual void onUpdateStaticCellColors() = 0;
virtual void onUpdateLegends() = 0;
// Default: no-op. Grid views (per-cell data) re-abstract these.
virtual void onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part>* parts ) {}
virtual void onUpdateStaticCellColors() {}
virtual void onUpdateLegends() = 0;
virtual cvf::Transform* scaleTransform() = 0;
@@ -288,12 +295,9 @@ protected:
// Timestep Field. Children clamps this differently
caf::PdmField<int> m_currentTimeStep;
// 3D display model data
cvf::ref<cvf::ModelBasicList> m_wellPathPipeVizModel;
cvf::ref<cvf::ModelBasicList> m_seismicVizModel;
// Named viz models (well path pipes, surfaces, intersections, etc.) - see RivNamedVisualizationModels.
RivNamedVisualizationModels m_vizModels;
cvf::ref<RivWellPathsPartMgr> m_wellPathsPartManager;
cvf::ref<cvf::ModelBasicList> m_highlightVizModel;
cvf::ref<cvf::ModelBasicList> m_screenSpaceModel;
caf::PdmField<double> m_scaleZ;
@@ -266,12 +266,6 @@ RimEclipseView::RimEclipseView()
setDeletable( true );
updateAnimations.connect( this, &RimEclipseView::onAnimationsUpdate );
m_faultReactVizModel = new cvf::ModelBasicList;
m_faultReactVizModel->setName( "FaultReactModel" );
m_refinementRegionsVizModel = new cvf::ModelBasicList;
m_refinementRegionsVizModel->setName( "RefinementRegionsModel" );
}
//--------------------------------------------------------------------------------------------------
@@ -803,47 +797,46 @@ void RimEclipseView::onCreateDisplayModel()
// Seismic sections
cvf::ref<caf::DisplayCoordTransform> transform = displayCoordTransform();
m_seismicVizModel->removeAllParts();
m_seismicSectionCollection->appendPartsToModel( this, m_seismicVizModel.p(), transform.p(), ownerCase()->allCellsBoundingBox() );
nativeOrOverrideViewer()->addStaticModelOnce( m_seismicVizModel.p(), isUsingOverrideViewer() );
auto* seismicVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::seismicSectionModelName() );
m_seismicSectionCollection->appendPartsToModel( this, seismicVizModel, transform.p(), ownerCase()->allCellsBoundingBox() );
nativeOrOverrideViewer()->addStaticModelOnce( seismicVizModel, isUsingOverrideViewer() );
// Fault reactivation models
m_faultReactVizModel->removeAllParts();
m_faultReactivationModelCollection->appendPartsToModel( this, m_faultReactVizModel.p(), transform.p(), ownerCase()->allCellsBoundingBox() );
nativeOrOverrideViewer()->addStaticModelOnce( m_faultReactVizModel.p(), isUsingOverrideViewer() );
auto* faultReactVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::faultReactivationModelName() );
m_faultReactivationModelCollection->appendPartsToModel( this, faultReactVizModel, transform.p(), ownerCase()->allCellsBoundingBox() );
nativeOrOverrideViewer()->addStaticModelOnce( faultReactVizModel, isUsingOverrideViewer() );
// Refinement region preview boxes
m_refinementRegionsVizModel->removeAllParts();
auto* refinementRegionsVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::refinementRegionsModelName() );
m_refinementRegionPartManager->buildGeometry( refinementRegionCollection(), eclipseCase(), transform.p() );
m_refinementRegionPartManager->appendStaticPartsToModel( m_refinementRegionsVizModel.p() );
m_refinementRegionPartManager->appendStaticPartsToModel( refinementRegionsVizModel );
m_refinementRegionPartManager->updateCellResultColor( m_currentTimeStep, cellResult() );
m_refinementRegionsVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( m_refinementRegionsVizModel.p(), isUsingOverrideViewer() );
refinementRegionsVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( refinementRegionsVizModel, isUsingOverrideViewer() );
// Surfaces
m_surfaceVizModel->removeAllParts();
auto* surfaceVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::surfaceModelName() );
if ( surfaceInViewCollection() )
{
surfaceInViewCollection()->appendPartsToModel( m_surfaceVizModel.p(), m_reservoirGridPartManager->scaleTransform() );
nativeOrOverrideViewer()->addStaticModelOnce( m_surfaceVizModel.p(), isUsingOverrideViewer() );
surfaceInViewCollection()->appendPartsToModel( surfaceVizModel, m_reservoirGridPartManager->scaleTransform() );
nativeOrOverrideViewer()->addStaticModelOnce( surfaceVizModel, isUsingOverrideViewer() );
}
// Polygons
appendPolygonPartsToModel( transform.p(), ownerCase()->allCellsBoundingBox() );
// Well path model
m_wellPathPipeVizModel->removeAllParts();
auto* wellPathPipeVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::wellPathPipeModelName() );
// NB! StimPlan legend colors must be updated before well path geometry is added to the model
// as the fracture geometry depends on the StimPlan legend colors
fractureColors()->updateLegendData();
addWellPathsToModel( m_wellPathPipeVizModel.p(), currentActiveCellInfo()->geometryBoundingBox(), ownerCase()->characteristicCellSize() );
addWellPathsToModel( wellPathPipeVizModel, currentActiveCellInfo()->geometryBoundingBox(), ownerCase()->characteristicCellSize() );
m_wellPathsPartManager->appendStaticFracturePartsToModel( m_wellPathPipeVizModel.p(), currentActiveCellInfo()->geometryBoundingBox() );
m_wellPathPipeVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( m_wellPathPipeVizModel.p(), isUsingOverrideViewer() );
m_wellPathsPartManager->appendStaticFracturePartsToModel( wellPathPipeVizModel, currentActiveCellInfo()->geometryBoundingBox() );
wellPathPipeVizModel->updateBoundingBoxesRecursive();
nativeOrOverrideViewer()->addStaticModelOnce( wellPathPipeVizModel, isUsingOverrideViewer() );
// Create Scenes from the frameModels
// Animation frames for results display, starts from frame 1
@@ -967,10 +960,10 @@ void RimEclipseView::onUpdateDisplayModelForCurrentTimeStep()
if ( surfaceInViewCollection() )
{
m_surfaceVizModel->removeAllParts();
auto* surfaceVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::surfaceModelName() );
surfaceInViewCollection()->loadData( currentTimeStep() );
surfaceInViewCollection()->appendPartsToModel( m_surfaceVizModel.p(), m_reservoirGridPartManager->scaleTransform() );
surfaceInViewCollection()->appendPartsToModel( surfaceVizModel, m_reservoirGridPartManager->scaleTransform() );
}
updateVisibleCellColors();
@@ -255,9 +255,6 @@ private:
void storeCurrentAndApplyNewCameraPosition( RimEclipseCase* previousCase, RimEclipseCase* newCase );
protected:
cvf::ref<cvf::ModelBasicList> m_faultReactVizModel;
cvf::ref<cvf::ModelBasicList> m_refinementRegionsVizModel;
caf::PdmPtrField<RimEclipseCase*> m_eclipseCase;
caf::PdmField<caf::AppEnum<RimCaseChangeBehaviour>> m_caseChangeBehaviour;
@@ -114,15 +114,6 @@ RimGridView::RimGridView()
CAF_PDM_InitFieldNoDefault( &m_cellFilterCollection, "RangeFilters", "Cell Filter Collection Field" );
m_cellFilterCollection = new RimCellFilterCollection();
m_surfaceVizModel = new cvf::ModelBasicList;
m_surfaceVizModel->setName( "SurfaceModel" );
m_intersectionVizModel = new cvf::ModelBasicList;
m_intersectionVizModel->setName( "CrossSectionModel" );
m_polygonVizModel = new cvf::ModelBasicList;
m_polygonVizModel->setName( "PolygonModel" );
}
//--------------------------------------------------------------------------------------------------
@@ -441,7 +432,7 @@ void RimGridView::onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part
//--------------------------------------------------------------------------------------------------
void RimGridView::appendPolygonPartsToModel( caf::DisplayCoordTransform* scaleTransform, const cvf::BoundingBox& boundingBox )
{
m_polygonVizModel->removeAllParts();
auto* polygonVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::polygonModelName() );
std::vector<RimPolygonInView*> polygonsInView;
if ( m_polygonInViewCollection )
@@ -459,13 +450,13 @@ void RimGridView::appendPolygonPartsToModel( caf::DisplayCoordTransform* scaleTr
{
if ( polygonInView )
{
polygonInView->appendPartsToModel( m_polygonVizModel.p(), scaleTransform, boundingBox );
polygonInView->appendPartsToModel( polygonVizModel, scaleTransform, boundingBox );
}
}
nativeOrOverrideViewer()->addStaticModelOnce( m_polygonVizModel.p(), isUsingOverrideViewer() );
nativeOrOverrideViewer()->addStaticModelOnce( polygonVizModel, isUsingOverrideViewer() );
m_polygonVizModel->updateBoundingBoxesRecursive();
polygonVizModel->updateBoundingBoxesRecursive();
}
//--------------------------------------------------------------------------------------------------
@@ -586,7 +577,7 @@ void RimGridView::appendIntersectionsForCurrentTimeStep()
{
// Remove previous intersection parts for static geometry. This is required to avoid the static parts to be rendered
// in front of the dynamic geometry added in this function. Note that a new model is created for the dynamic geometry
m_intersectionVizModel->removeAllParts();
m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::crossSectionModelName() );
m_intersectionCollection->clearGeometry();
@@ -622,7 +613,7 @@ void RimGridView::appendIntersectionsForCurrentTimeStep()
//--------------------------------------------------------------------------------------------------
void RimGridView::appendIntersectionsToModel( bool cellFiltersActive, bool propertyFiltersActive )
{
m_intersectionVizModel->removeAllParts();
auto* intersectionVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::crossSectionModelName() );
if ( m_intersectionCollection->isActive() )
{
m_intersectionCollection->clearGeometry();
@@ -633,30 +624,30 @@ void RimGridView::appendIntersectionsToModel( bool cellFiltersActive, bool prope
{
cvf::UByteArray visibleCells;
calculateCellVisibility( &visibleCells, { PROPERTY_FILTERED, PROPERTY_FILTERED_WELL_CELLS } );
m_intersectionCollection->appendDynamicPartsToModel( m_intersectionVizModel.p(), scaleTransform(), currentTimeStep(), &visibleCells );
m_intersectionCollection->appendDynamicPartsToModel( intersectionVizModel, scaleTransform(), currentTimeStep(), &visibleCells );
}
else
{
cvf::UByteArray visibleCells;
calculateCellVisibility( &visibleCells, { RANGE_FILTERED_WELL_CELLS, RANGE_FILTERED } );
m_intersectionCollection->appendDynamicPartsToModel( m_intersectionVizModel.p(), scaleTransform(), currentTimeStep(), &visibleCells );
m_intersectionCollection->appendDynamicPartsToModel( intersectionVizModel, scaleTransform(), currentTimeStep(), &visibleCells );
}
// NB! Geometry objects are recreated in appendDynamicPartsToModel(), always call
// appendPartsToModel() after appendDynamicPartsToModel()
m_intersectionCollection->appendPartsToModel( *this, m_intersectionVizModel.p(), scaleTransform() );
m_intersectionCollection->appendPartsToModel( *this, intersectionVizModel, scaleTransform() );
}
else
{
m_intersectionCollection->appendDynamicPartsToModel( m_intersectionVizModel.p(), scaleTransform(), currentTimeStep() );
m_intersectionCollection->appendDynamicPartsToModel( intersectionVizModel, scaleTransform(), currentTimeStep() );
// NB! Geometry objects are recreated in appendDynamicPartsToModel(), always call
// appendPartsToModel() after appendDynamicPartsToModel()
m_intersectionCollection->appendPartsToModel( *this, m_intersectionVizModel.p(), scaleTransform() );
m_intersectionCollection->appendPartsToModel( *this, intersectionVizModel, scaleTransform() );
}
nativeOrOverrideViewer()->addStaticModelOnce( m_intersectionVizModel.p(), isUsingOverrideViewer() );
nativeOrOverrideViewer()->addStaticModelOnce( intersectionVizModel, isUsingOverrideViewer() );
m_intersectionVizModel->updateBoundingBoxesRecursive();
intersectionVizModel->updateBoundingBoxesRecursive();
}
}
@@ -42,6 +42,9 @@ class RimGridView : public Rim3dView
public:
RimGridView();
// Grid views are always owned by a real case
RimCase* ownerCase() const override = 0;
caf::Signal<> cellVisibilityChanged;
void showGridCells( bool enableGridCells );
@@ -82,10 +85,21 @@ protected:
virtual void updateViewFollowingCellFilterUpdates();
void onClearReservoirCellVisibilitiesIfNecessary() override;
virtual void calculateCurrentTotalCellVisibility( cvf::UByteArray* totalVisibility, int timeStep ) = 0;
void selectOverlayInfoConfig() override;
void clearReservoirCellVisibilities();
void addRequiredUiTreeObjects( caf::PdmUiTreeOrdering& uiTreeOrdering );
void appendPolygonPartsToModel( caf::DisplayCoordTransform* scaleTransform, const cvf::BoundingBox& boundingBox );
// Grid views have genuine per-time-step data
void onUpdateDisplayModelForCurrentTimeStep() override = 0;
void onClampCurrentTimestep() override = 0;
size_t onTimeStepCountRequested() override = 0;
bool isTimeStepDependentDataVisible() const override = 0;
// Grid views have genuine per-cell data
bool isUsingFormationNames() const override = 0;
void onUpdateStaticCellColors() override = 0;
void selectOverlayInfoConfig() override;
void clearReservoirCellVisibilities();
void addRequiredUiTreeObjects( caf::PdmUiTreeOrdering& uiTreeOrdering );
void appendPolygonPartsToModel( caf::DisplayCoordTransform* scaleTransform, const cvf::BoundingBox& boundingBox );
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void initAfterRead() override;
@@ -96,10 +110,6 @@ protected:
virtual void calculateCellVisibility( cvf::UByteArray* visibility, std::vector<RivCellSetEnum> geomTypes, int timeStep = 0 ) = 0;
protected:
cvf::ref<cvf::ModelBasicList> m_surfaceVizModel;
cvf::ref<cvf::ModelBasicList> m_intersectionVizModel;
cvf::ref<cvf::ModelBasicList> m_polygonVizModel;
// Fields
caf::PdmChildField<RimIntersectionCollection*> m_intersectionCollection;
@@ -71,9 +71,6 @@ RimSeismicView::RimSeismicView()
m_scaleTransform = new cvf::Transform();
m_surfaceVizModel = new cvf::ModelBasicList;
m_surfaceVizModel->setName( "SurfaceModel" );
setDeletable( true );
}
@@ -125,14 +122,6 @@ RimSeismicSectionCollection* RimSeismicView::seismicSectionCollection() const
return m_seismicSectionCollection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCase* RimSeismicView::ownerCase() const
{
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -149,14 +138,6 @@ bool RimSeismicView::isGridVisualizationMode() const
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimSeismicView::isUsingFormationNames() const
{
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -264,29 +245,29 @@ void RimSeismicView::onCreateDisplayModel()
// Seismic sections
cvf::ref<caf::DisplayCoordTransform> transform = displayCoordTransform();
m_seismicVizModel->removeAllParts();
auto* seismicVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::seismicSectionModelName() );
if ( m_polylinePartMgr.isNull() ) m_polylinePartMgr = new RivPolylinePartMgr( this, this, this );
m_polylinePartMgr->appendDynamicGeometryPartsToModel( m_seismicVizModel.p(), transform.p(), domainBoundingBox() );
m_polylinePartMgr->appendDynamicGeometryPartsToModel( seismicVizModel, transform.p(), domainBoundingBox() );
m_seismicSectionCollection->appendPartsToModel( this, m_seismicVizModel.p(), transform.p(), domainBoundingBox() );
mainScene->addModel( m_seismicVizModel.p() );
m_seismicSectionCollection->appendPartsToModel( this, seismicVizModel, transform.p(), domainBoundingBox() );
mainScene->addModel( seismicVizModel );
nativeOrOverrideViewer()->setMainScene( mainScene.p(), isUsingOverrideViewer() );
// Well path model
m_wellPathPipeVizModel->removeAllParts();
addWellPathsToModel( m_wellPathPipeVizModel.p(), domainBoundingBox(), m_seismicData->inlineSpacing() );
nativeOrOverrideViewer()->addStaticModelOnce( m_wellPathPipeVizModel.p(), isUsingOverrideViewer() );
auto* wellPathPipeVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::wellPathPipeModelName() );
addWellPathsToModel( wellPathPipeVizModel, domainBoundingBox(), m_seismicData->inlineSpacing() );
nativeOrOverrideViewer()->addStaticModelOnce( wellPathPipeVizModel, isUsingOverrideViewer() );
// Surfaces
m_surfaceVizModel->removeAllParts();
auto* surfaceVizModel = m_vizModels.findOrCreateAndClear( RivNamedVisualizationModels::surfaceModelName() );
if ( m_surfaceCollection )
{
bool nativeOnly = true;
m_surfaceCollection->appendPartsToModel( m_surfaceVizModel.p(), scaleTransform(), nativeOnly );
nativeOrOverrideViewer()->addStaticModelOnce( m_surfaceVizModel.p(), isUsingOverrideViewer() );
m_surfaceCollection->appendPartsToModel( surfaceVizModel, scaleTransform(), nativeOnly );
nativeOrOverrideViewer()->addStaticModelOnce( surfaceVizModel, isUsingOverrideViewer() );
}
// Annotations
@@ -307,37 +288,6 @@ void RimSeismicView::onCreateDisplayModel()
m_overlayInfoConfig()->update3DInfo();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSeismicView::onUpdateDisplayModelForCurrentTimeStep()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSeismicView::onClampCurrentTimestep()
{
m_currentTimeStep = 0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimSeismicView::onTimeStepCountRequested()
{
return 1;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimSeismicView::isTimeStepDependentDataVisible() const
{
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -348,22 +298,6 @@ void RimSeismicView::defineAxisLabels( cvf::String* xLabel, cvf::String* yLabel,
*zLabel = "Z";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSeismicView::onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part>* parts )
{
// no action needed, might be needed if we want to hilite something later
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSeismicView::onUpdateStaticCellColors()
{
// no action needed
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -51,10 +51,8 @@ public:
RimSurfaceInViewCollection* surfaceInViewCollection() const;
RimSeismicSectionCollection* seismicSectionCollection() const;
RimCase* ownerCase() const override;
RiaDefines::View3dContent viewContent() const override;
bool isGridVisualizationMode() const override;
bool isUsingFormationNames() const override;
std::vector<RimLegendConfig*> legendConfigs() const override;
void scheduleGeometryRegen( RivCellSetEnum geometryType ) override;
@@ -71,15 +69,9 @@ protected:
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
void onCreateDisplayModel() override;
void onUpdateDisplayModelForCurrentTimeStep() override;
void onClampCurrentTimestep() override;
size_t onTimeStepCountRequested() override;
bool isTimeStepDependentDataVisible() const override;
void defineAxisLabels( cvf::String* xLabel, cvf::String* yLabel, cvf::String* zLabel ) override;
void onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part>* parts ) override;
void onUpdateStaticCellColors() override;
void onUpdateLegends() override;
void onCreateDisplayModel() override;
void defineAxisLabels( cvf::String* xLabel, cvf::String* yLabel, cvf::String* zLabel ) override;
void onUpdateLegends() override;
void onLoadDataAndUpdate() override;
void selectOverlayInfoConfig() override;
@@ -100,8 +92,7 @@ private:
caf::PdmPtrField<RimSeismicDataInterface*> m_seismicData;
cvf::ref<cvf::ModelBasicList> m_surfaceVizModel;
cvf::ref<RivPolylinePartMgr> m_polylinePartMgr;
cvf::ref<RivPolylinePartMgr> m_polylinePartMgr;
cvf::ref<cvf::Transform> m_scaleTransform;
};
@@ -41,6 +41,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RimPlotAxisTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellLogExtractionCurveImpl-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivAnnotationTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivNamedVisualizationModels-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivPipeGeometryGenerator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivIjkIntersectionGeometryGenerator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivSingleCellPartGenerator-Test.cpp
@@ -0,0 +1,106 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RivNamedVisualizationModels.h"
#include "cvfModelBasicList.h"
#include "cvfPart.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RivNamedVisualizationModelsTest, FindOrCreateReturnsSameInstanceForSameName )
{
RivNamedVisualizationModels models;
auto* modelA = models.findOrCreate( "TestModel" );
auto* modelB = models.findOrCreate( "TestModel" );
EXPECT_EQ( modelA, modelB );
EXPECT_EQ( 1u, models.allModels().size() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RivNamedVisualizationModelsTest, FindOrCreateCreatesDistinctInstancesForDifferentNames )
{
RivNamedVisualizationModels models;
auto* modelA = models.findOrCreate( "TestModelA" );
auto* modelB = models.findOrCreate( "TestModelB" );
EXPECT_NE( modelA, modelB );
EXPECT_EQ( 2u, models.allModels().size() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RivNamedVisualizationModelsTest, FindOrCreateAndClearRemovesParts )
{
RivNamedVisualizationModels models;
auto* model = models.findOrCreate( "TestModel" );
model->addPart( new cvf::Part );
EXPECT_EQ( 1u, model->partCount() );
auto* clearedModel = models.findOrCreateAndClear( "TestModel" );
EXPECT_EQ( model, clearedModel );
EXPECT_EQ( 0u, clearedModel->partCount() );
}
//--------------------------------------------------------------------------------------------------
/// The map only keeps the model alive through its own cvf::ref. Verify that clear()/destruction
/// actually releases that reference, rather than e.g. leaking a raw pointer, by keeping an external
/// cvf::ref alive and inspecting the reference count before and after.
//--------------------------------------------------------------------------------------------------
TEST( RivNamedVisualizationModelsTest, ClearReleasesOwnedModels )
{
RivNamedVisualizationModels models;
cvf::ref<cvf::ModelBasicList> externalRef = models.findOrCreate( "TestModel" );
// One reference held by the map's internal storage, one held by externalRef.
EXPECT_EQ( 2, externalRef->refCount() );
models.clear();
// The map has released its reference; only externalRef remains.
EXPECT_EQ( 1, externalRef->refCount() );
EXPECT_TRUE( models.allModels().empty() );
}
//--------------------------------------------------------------------------------------------------
/// Verify that destroying the RivNamedVisualizationModels instance itself (going out of scope)
/// releases the reference held on each named model.
//--------------------------------------------------------------------------------------------------
TEST( RivNamedVisualizationModelsTest, DestructorReleasesOwnedModels )
{
cvf::ref<cvf::ModelBasicList> externalRef;
{
RivNamedVisualizationModels models;
externalRef = models.findOrCreate( "TestModel" );
EXPECT_EQ( 2, externalRef->refCount() );
}
EXPECT_EQ( 1, externalRef->refCount() );
}