#14436 Surface intersection curves and bands for Intersection Box and I/J/K Intersection

* Add RivSurfaceIntersectionCurveTools for shared surface intersection curve geometry
* Move the surface intersection collection to RimIntersection
* Add surface curtain footprints for the box and i/j/k intersections
* Build surface intersection curves for the box and i/j/k intersections
* Follow the pillar tilt when projecting a surface onto an intersection
* Estimate the surface intersection by continuing the pillar past the curtain
This commit is contained in:
Magne Sjaastad authored and GitHub committed 2026-08-06 08:06:53 +02:00
1 parent d87a0d3048
commit 0012a65922
31 files changed
+1234 -398

No files matched your search

@@ -22,7 +22,7 @@
#include "RimAnnotationLineAppearance.h"
#include "RimEnsembleSurface.h"
#include "RimExtrudedCurveIntersection.h"
#include "RimIntersection.h"
#include "RimSurface.h"
#include "RimSurfaceCollection.h"
#include "RimSurfaceIntersectionBand.h"
@@ -36,13 +36,23 @@
CAF_CMD_SOURCE_INIT( RicCreateSurfaceIntersectionBandFeature, "RicCreateSurfaceIntersectionBandFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicCreateSurfaceIntersectionBandFeature::isCommandEnabled() const
{
auto* intersection = caf::SelectionManager::instance()->selectedItemAncestorOfType<RimIntersection>();
return intersection && intersection->supportsSurfaceIntersectionCurves();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicCreateSurfaceIntersectionBandFeature::onActionTriggered( bool isChecked )
{
auto* intersection = caf::SelectionManager::instance()->selectedItemAncestorOfType<RimExtrudedCurveIntersection>();
if ( intersection )
auto* intersection = caf::SelectionManager::instance()->selectedItemAncestorOfType<RimIntersection>();
if ( intersection && intersection->supportsSurfaceIntersectionCurves() )
{
RimEnsembleSurface* firstEnsembleSurface = nullptr;
{
@@ -28,6 +28,7 @@ class RicCreateSurfaceIntersectionBandFeature : public caf::CmdFeature
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
@@ -18,7 +18,7 @@
#include "RicCreateSurfaceIntersectionCurveFeature.h"
#include "RimExtrudedCurveIntersection.h"
#include "RimIntersection.h"
#include "RimSurfaceIntersectionCurve.h"
#include "Riu3DMainWindowTools.h"
@@ -29,13 +29,23 @@
CAF_CMD_SOURCE_INIT( RicCreateSurfaceIntersectionCurveFeature, "RicCreateSurfaceIntersectionCurveFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicCreateSurfaceIntersectionCurveFeature::isCommandEnabled() const
{
auto* intersection = caf::SelectionManager::instance()->selectedItemAncestorOfType<RimIntersection>();
return intersection && intersection->supportsSurfaceIntersectionCurves();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicCreateSurfaceIntersectionCurveFeature::onActionTriggered( bool isChecked )
{
auto* intersection = caf::SelectionManager::instance()->selectedItemAncestorOfType<RimExtrudedCurveIntersection>();
if ( intersection )
auto* intersection = caf::SelectionManager::instance()->selectedItemAncestorOfType<RimIntersection>();
if ( intersection && intersection->supportsSurfaceIntersectionCurves() )
{
auto curve = intersection->addIntersectionCurve();
intersection->updateAllRequiredEditors();
@@ -28,6 +28,7 @@ class RicCreateSurfaceIntersectionCurveFeature : public caf::CmdFeature
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
@@ -42,6 +42,7 @@
#include "RivPartPriority.h"
#include "RivResultToTextureMapper.h"
#include "RivScalarMapperUtils.h"
#include "RivSurfaceIntersectionCurveTools.h"
#include "RivTernaryScalarMapper.h"
#include "RivTernaryTextureCoordsCreator.h"
@@ -96,7 +97,7 @@ void RivBoxIntersectionPartMgr::updateCellResultColor( int timeStepIndex )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivBoxIntersectionPartMgr::generatePartGeometry( cvf::UByteArray* visibleCells )
void RivBoxIntersectionPartMgr::generatePartGeometry( cvf::UByteArray* visibleCells, cvf::Transform* scaleTransform )
{
bool useBufferObjects = true;
// Surface geometry
@@ -151,6 +152,9 @@ void RivBoxIntersectionPartMgr::generatePartGeometry( cvf::UByteArray* visibleCe
}
}
if ( scaleTransform )
m_annotationParts = RivSurfaceIntersectionCurveTools::createAnnotationParts( m_rimIntersectionBox, *scaleTransform );
updatePartEffect();
}
@@ -206,6 +210,18 @@ void RivBoxIntersectionPartMgr::appendMeshLinePartsToModel( cvf::ModelBasicList*
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivBoxIntersectionPartMgr::appendAnnotationPartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform )
{
for ( size_t i = 0; i < m_annotationParts.size(); i++ )
{
m_annotationParts[i]->setTransform( scaleTransform );
model->addPart( m_annotationParts.at( i ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -20,6 +20,7 @@
#include "RivBoxIntersectionGeometryGenerator.h"
#include "cvfCollection.h"
#include "cvfObject.h"
namespace cvf
@@ -60,10 +61,11 @@ public:
void appendNativeIntersectionFacesToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform );
void appendMeshLinePartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform );
void appendAnnotationPartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform );
const RivIntersectionGeometryGeneratorInterface* intersectionGeometryGenerator() const;
void generatePartGeometry( cvf::UByteArray* visibleCells );
void generatePartGeometry( cvf::UByteArray* visibleCells, cvf::Transform* scaleTransform );
private:
void updatePartEffect();
@@ -73,9 +75,10 @@ private:
cvf::Color3f m_defaultColor;
cvf::ref<cvf::Part> m_intersectionBoxFaces;
cvf::ref<cvf::Part> m_intersectionBoxGridLines;
cvf::ref<cvf::Vec2fArray> m_intersectionBoxFacesTextureCoords;
cvf::ref<cvf::Part> m_intersectionBoxFaces;
cvf::ref<cvf::Part> m_intersectionBoxGridLines;
cvf::Collection<cvf::Part> m_annotationParts;
cvf::ref<cvf::Vec2fArray> m_intersectionBoxFacesTextureCoords;
cvf::ref<RivBoxIntersectionGeometryGenerator> m_intersectionBoxGenerator;
};
@@ -160,52 +160,24 @@ void RivExtrudedCurveIntersectionGeometryGenerator::calculateSurfaceIntersection
{
m_transformedSurfaceIntersectionPolylines.clear();
if ( !m_polylines.empty() )
if ( m_polylines.empty() ) return;
const auto surfaces = RivSurfaceIntersectionCurveTools::referencedSurfaces( m_intersection->surfaceIntersectionCollection() );
if ( surfaces.empty() ) return;
// Vertical pillars along the same polyline as the one used to build the geometry
const auto curtain = m_intersection->surfaceCurtain();
if ( !curtain.isValid() ) return;
// The polyline is flattened when the curve is displayed in a 2D intersection view
auto pointTransform = [this]( const cvf::Vec3d& point, size_t segmentIndex )
{
auto firstPolyLine = m_polylines.front();
const size_t lineIndex = 0;
return transformPointByPolylineSegmentIndex( point, lineIndex, segmentIndex );
};
std::vector<RimSurface*> surfaces;
for ( auto curve : m_intersection->surfaceIntersectionCurves() )
{
if ( curve->surface() ) surfaces.push_back( curve->surface() );
}
for ( auto band : m_intersection->surfaceIntersectionBands() )
{
if ( band->surface1() ) surfaces.push_back( band->surface1() );
if ( band->surface2() ) surfaces.push_back( band->surface2() );
}
for ( auto rimSurface : surfaces )
{
if ( !rimSurface ) return;
rimSurface->loadDataIfRequired();
auto surface = rimSurface->surfaceData();
if ( !surface ) return;
std::vector<cvf::Vec3d> transformedSurfacePolyline;
// Resample polyline to required resolution
const double maxLineSegmentLength = 1.0;
const auto resampledPolyline = RigSurfaceResampler::computeResampledPolylineWithSegmentInfo( firstPolyLine, maxLineSegmentLength );
for ( const auto& [point, segmentIndex] : resampledPolyline )
{
cvf::Vec3d pointAbove = cvf::Vec3d( point.x(), point.y(), 10000.0 );
cvf::Vec3d pointBelow = cvf::Vec3d( point.x(), point.y(), -10000.0 );
cvf::Vec3d intersectionPoint;
bool foundMatch = RigSurfaceResampler::findClosestPointOnSurface( surface, pointAbove, pointBelow, intersectionPoint );
if ( foundMatch )
{
const size_t lineIndex = 0;
transformedSurfacePolyline.push_back( transformPointByPolylineSegmentIndex( intersectionPoint, lineIndex, segmentIndex ) );
}
}
if ( !transformedSurfacePolyline.empty() ) m_transformedSurfaceIntersectionPolylines[rimSurface] = transformedSurfacePolyline;
}
}
m_transformedSurfaceIntersectionPolylines =
RivSurfaceIntersectionCurveTools::computeSurfaceCurtainPolylines( surfaces, curtain, pointTransform );
}
class MeshLinesAccumulator
@@ -844,7 +816,7 @@ bool RivExtrudedCurveIntersectionGeometryGenerator::isAnyGeometryPresent() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<RimSurface*, std::vector<cvf::Vec3d>> RivExtrudedCurveIntersectionGeometryGenerator::transformedSurfaceIntersectionPolylines() const
std::map<RimSurface*, RivSurfaceCurtainPolyline> RivExtrudedCurveIntersectionGeometryGenerator::transformedSurfaceIntersectionPolylines() const
{
return m_transformedSurfaceIntersectionPolylines;
}
@@ -22,6 +22,7 @@
#include "cafPdmPointer.h"
#include "RivIntersectionGeometryGeneratorInterface.h"
#include "RivSurfaceIntersectionCurveTools.h"
#include "cvfArray.h"
@@ -76,7 +77,7 @@ public:
// GeomGen Interface
bool isAnyGeometryPresent() const override;
std::map<RimSurface*, std::vector<cvf::Vec3d>> transformedSurfaceIntersectionPolylines() const;
std::map<RimSurface*, RivSurfaceCurtainPolyline> transformedSurfaceIntersectionPolylines() const;
const std::vector<size_t>& triangleToCellIndex() const override;
const std::vector<RivIntersectionVertexWeights>& triangleVxToCellCornerInterpolationWeights() const override;
@@ -116,5 +117,5 @@ private:
std::vector<std::pair<QString, cvf::Vec3d>> m_faultMeshLabelAndAnchorPositions;
std::map<RimSurface*, std::vector<cvf::Vec3d>> m_transformedSurfaceIntersectionPolylines;
std::map<RimSurface*, RivSurfaceCurtainPolyline> m_transformedSurfaceIntersectionPolylines;
};
@@ -64,6 +64,7 @@
#include "RivResultToTextureMapper.h"
#include "RivScalarMapperUtils.h"
#include "RivSimWellPipeSourceInfo.h"
#include "RivSurfaceIntersectionCurveTools.h"
#include "RivTernaryScalarMapper.h"
#include "RivTernaryTextureCoordsCreator.h"
#include "RivWellPathSourceInfo.h"
@@ -325,7 +326,7 @@ void RivExtrudedCurveIntersectionPartMgr::generatePartGeometry( cvf::UByteArray*
applySingleColorEffect();
createAnnotationSurfaceParts( useBufferObjects, scaleTransform );
if ( scaleTransform ) createAnnotationSurfaceParts( *scaleTransform );
}
//--------------------------------------------------------------------------------------------------
@@ -602,199 +603,12 @@ void RivExtrudedCurveIntersectionPartMgr::createExtrusionDirParts( bool useBuffe
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivExtrudedCurveIntersectionPartMgr::createAnnotationSurfaceParts( bool useBufferObjects, cvf::Transform* scaleTransform )
void RivExtrudedCurveIntersectionPartMgr::createAnnotationSurfaceParts( cvf::Transform& scaleTransform )
{
m_annotationParts.clear();
auto surfPolys = m_intersectionGenerator->transformedSurfaceIntersectionPolylines();
for ( auto curve : m_rimIntersection->surfaceIntersectionCurves() )
{
if ( !curve->isChecked() ) continue;
auto surface = curve->surface();
if ( !surface ) continue;
if ( surfPolys.count( surface ) == 0 ) continue;
auto polylines = surfPolys[surface];
auto part = createCurvePart( polylines,
useBufferObjects,
surface->fullName(),
curve->lineAppearance()->color(),
curve->lineAppearance()->thickness(),
scaleTransform );
if ( part.notNull() ) m_annotationParts.push_back( part.p() );
}
for ( auto band : m_rimIntersection->surfaceIntersectionBands() )
{
if ( !band->isChecked() ) continue;
auto surface1 = band->surface1();
auto surface2 = band->surface2();
if ( !surface1 || !surface2 ) continue;
if ( surfPolys.count( surface1 ) == 0 ) continue;
if ( surfPolys.count( surface2 ) == 0 ) continue;
auto polylineA = surfPolys[surface1];
auto polylineB = surfPolys[surface2];
// Create curve parts
{
auto part = createCurvePart( polylineA,
useBufferObjects,
surface1->fullName(),
band->lineAppearance()->color(),
band->lineAppearance()->thickness(),
scaleTransform );
if ( part.notNull() ) m_annotationParts.push_back( part.p() );
}
{
auto part = createCurvePart( polylineB,
useBufferObjects,
surface2->fullName(),
band->lineAppearance()->color(),
band->lineAppearance()->thickness(),
scaleTransform );
if ( part.notNull() ) m_annotationParts.push_back( part.p() );
}
// Create a quad strip between the two polylines
size_t pointCount = std::min( polylineA.size(), polylineB.size() );
if ( pointCount > 1 )
{
cvf::GeometryBuilderDrawableGeo geoBuilder;
for ( size_t i = 1; i < pointCount; i++ )
{
const auto& pA0 = polylineA[i - 1];
const auto& pA1 = polylineA[i];
const auto& pB0 = polylineB[i - 1];
const auto& pB1 = polylineB[i];
geoBuilder.addQuadByVertices( cvf::Vec3f( pA0 ), cvf::Vec3f( pA1 ), cvf::Vec3f( pB1 ), cvf::Vec3f( pB0 ) );
}
cvf::ref<cvf::DrawableGeo> geo = geoBuilder.drawableGeo();
if ( geo.notNull() )
{
geo->computeNormals();
if ( useBufferObjects )
{
geo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
}
cvf::ref<cvf::Part> part = new cvf::Part;
part->setName( "Surface Intersection Band" );
part->setDrawable( geo.p() );
part->updateBoundingBox();
part->setEnableMask( intersectionCellFaceBit );
part->setPriority( RivPartPriority::PartType::Transparent );
auto color = cvf::Color4f( band->bandColor(), band->bandOpacity() );
caf::SurfaceEffectGenerator geometryEffgen( color, caf::PO_NEG_LARGE );
cvf::ref<cvf::Effect> geometryOnlyEffect = geometryEffgen.generateUnCachedEffect();
{
cvf::ref<cvf::RenderStatePolygonOffset> polyOffset = new cvf::RenderStatePolygonOffset;
polyOffset->enableFillMode( true );
// The factor value is defined by enums in
// EffectGenerator::createAndConfigurePolygonOffsetRenderState() Use a factor that is more negative
// than the existing enums
const double offsetFactor = -5;
polyOffset->setFactor( offsetFactor );
polyOffset->setUnits( band->polygonOffsetUnit() );
geometryOnlyEffect->setRenderState( polyOffset.p() );
}
part->setEffect( geometryOnlyEffect.p() );
m_annotationParts.push_back( part.p() );
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<cvf::Part> RivExtrudedCurveIntersectionPartMgr::createCurvePart( const std::vector<cvf::Vec3d>& polylines,
bool useBufferObjects,
const QString& description,
const cvf::Color3f& color,
float lineWidth,
cvf::Transform* scaleTransform )
{
auto polylineGeo = RivPolylineGenerator::createLineAlongPolylineDrawable( polylines );
if ( polylineGeo.notNull() )
{
if ( useBufferObjects )
{
polylineGeo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
}
cvf::ref<cvf::Part> part = new cvf::Part;
part->setName( "Intersection " + description.toStdString() );
part->setDrawable( polylineGeo.p() );
part->updateBoundingBox();
part->setPriority( RivPartPriority::PartType::FaultMeshLines );
caf::MeshEffectGenerator lineEffGen( color );
lineEffGen.setLineWidth( lineWidth );
cvf::ref<cvf::Effect> eff = lineEffGen.generateUnCachedEffect();
cvf::ref<cvf::RenderStatePolygonOffset> polyOffset = new cvf::RenderStatePolygonOffset;
polyOffset->enableFillMode( true );
polyOffset->setFactor( -5 );
const double maxOffsetFactor = -1000;
polyOffset->setUnits( maxOffsetFactor );
eff->setRenderState( polyOffset.p() );
part->setEffect( eff.p() );
if ( part.notNull() && scaleTransform )
{
// The polylines are defined in the display coordinate system without Z-scaling. The z-scaling is applied to the visualization
// parts using Part::setTransform(Transform* transform)
// The annotation objects are defined by display coordinates, so apply the Z-scaling to the coordinates
std::vector<cvf::Vec3d> displayCoords;
const auto& mat = scaleTransform->worldTransform();
for ( const auto& p : polylines )
{
displayCoords.push_back( p.getTransformedPoint( mat ) );
}
// Add annotation info to be used to display label in Rim3dView::onViewNavigationChanged()
// Set the source info on one part only, as this data is only used for display of labels
auto annoObj = new RivAnnotationSourceInfo( description.toStdString(), displayCoords );
annoObj->setLabelPositionStrategyHint( RivAnnotationTools::LabelPositionStrategy::RIGHT );
annoObj->setShowColor( true );
annoObj->setColor( color );
part->setSourceInfo( annoObj );
}
return part;
}
return nullptr;
m_annotationParts =
RivSurfaceIntersectionCurveTools::createAnnotationParts( m_rimIntersection->surfaceIntersectionCollection(),
m_intersectionGenerator->transformedSurfaceIntersectionPolylines(),
scaleTransform );
}
//--------------------------------------------------------------------------------------------------
@@ -87,14 +87,7 @@ private:
void createFaultLabelParts( const std::vector<std::pair<QString, cvf::Vec3d>>& labelAndAnchors );
void createPolyLineParts( bool useBufferObjects );
void createExtrusionDirParts( bool useBufferObjects );
void createAnnotationSurfaceParts( bool useBufferObjects, cvf::Transform* scaleTransform );
cvf::ref<cvf::Part> createCurvePart( const std::vector<cvf::Vec3d>& polylines,
bool useBufferObjects,
const QString& description,
const cvf::Color3f& color,
float lineWidth,
cvf::Transform* scaleTransform );
void createAnnotationSurfaceParts( cvf::Transform& scaleTransform );
private:
caf::PdmPointer<RimExtrudedCurveIntersection> m_rimIntersection;
@@ -148,42 +148,27 @@ void RivIjkIntersectionGeometryGenerator::calculateArrays( cvf::UByteArray* visi
const size_t nk = m_mainGrid->cellCountK();
if ( ni == 0 || nj == 0 || nk == 0 ) return;
// Determine which cell face the surface follows and the i/j/k loop bounds
// Determine which cell face the surface follows
RimIjkIntersection::GridAxis axis = m_intersectionDefinition->axis();
bool neg = m_intersectionDefinition->useNegativeFace();
const RigBoundingBoxIjk<caf::VecIjk0> range = m_intersectionDefinition->ijkRange();
// Snap the fixed index into the grid so a stale project file still renders the border slice
const size_t fixed = static_cast<size_t>( std::max( 0, m_intersectionDefinition->fixedIndex() ) );
// Pick the cell face the surface follows and collapse the fixed axis of the range to the fixed index.
// The i()/j()/k() accessors are read-only, so the assignments must use x()/y()/z()
auto computeFaceAndRange = [&]() -> std::pair<FaceType, RigBoundingBoxIjk<caf::VecIjk0>>
auto computeFace = [&]() -> FaceType
{
caf::VecIjk0 min = range.min();
caf::VecIjk0 max = range.max();
switch ( axis )
{
case RimIjkIntersection::GridAxis::AXIS_I:
min.x() = max.x() = std::min( fixed, ni - 1 );
return { neg ? FaceType::NEG_I : FaceType::POS_I, { min, max } };
return neg ? FaceType::NEG_I : FaceType::POS_I;
case RimIjkIntersection::GridAxis::AXIS_J:
min.y() = max.y() = std::min( fixed, nj - 1 );
return { neg ? FaceType::NEG_J : FaceType::POS_J, { min, max } };
return neg ? FaceType::NEG_J : FaceType::POS_J;
case RimIjkIntersection::GridAxis::AXIS_K:
default:
min.z() = max.z() = std::min( fixed, nk - 1 );
return { neg ? FaceType::NEG_K : FaceType::POS_K, { min, max } };
return neg ? FaceType::NEG_K : FaceType::POS_K;
}
};
const auto [face, requestedRange] = computeFaceAndRange();
const FaceType face = computeFace();
const RigBoundingBoxIjk<caf::VecIjk0> gridBounds( caf::VecIjk0::ZERO, caf::VecIjk0( ni - 1, nj - 1, nk - 1 ) );
const auto visibleRange = requestedRange.clamp( gridBounds );
const auto visibleRange = m_intersectionDefinition->clampedCellRange( m_mainGrid.p() );
if ( !visibleRange ) return;
cvf::ubyte faceVtxIdx[4];
@@ -30,6 +30,7 @@
#include "RivMeshLinesSourceInfo.h"
#include "RivPartPriority.h"
#include "RivScalarMapperUtils.h"
#include "RivSurfaceIntersectionCurveTools.h"
#include "cafEffectGenerator.h"
@@ -91,7 +92,7 @@ void RivIjkIntersectionPartMgr::updateCellResultColor( int timeStepIndex )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivIjkIntersectionPartMgr::generatePartGeometry( cvf::UByteArray* visibleCells )
void RivIjkIntersectionPartMgr::generatePartGeometry( cvf::UByteArray* visibleCells, cvf::Transform* scaleTransform )
{
bool useBufferObjects = true;
// Surface geometry
@@ -146,6 +147,8 @@ void RivIjkIntersectionPartMgr::generatePartGeometry( cvf::UByteArray* visibleCe
}
}
if ( scaleTransform ) m_annotationParts = RivSurfaceIntersectionCurveTools::createAnnotationParts( m_rimIntersection, *scaleTransform );
updatePartEffect();
}
@@ -197,6 +200,18 @@ void RivIjkIntersectionPartMgr::appendMeshLinePartsToModel( cvf::ModelBasicList*
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivIjkIntersectionPartMgr::appendAnnotationPartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform )
{
for ( size_t i = 0; i < m_annotationParts.size(); i++ )
{
m_annotationParts[i]->setTransform( scaleTransform );
model->addPart( m_annotationParts.at( i ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -20,6 +20,7 @@
#include "RivIjkIntersectionGeometryGenerator.h"
#include "cvfCollection.h"
#include "cvfObject.h"
namespace cvf
@@ -46,10 +47,11 @@ public:
void appendNativeIntersectionFacesToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform );
void appendMeshLinePartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform );
void appendAnnotationPartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform );
const RivIntersectionGeometryGeneratorInterface* intersectionGeometryGenerator() const;
void generatePartGeometry( cvf::UByteArray* visibleCells );
void generatePartGeometry( cvf::UByteArray* visibleCells, cvf::Transform* scaleTransform );
private:
void updatePartEffect();
@@ -59,9 +61,10 @@ private:
cvf::Color3f m_defaultColor;
cvf::ref<cvf::Part> m_intersectionFaces;
cvf::ref<cvf::Part> m_intersectionGridLines;
cvf::ref<cvf::Vec2fArray> m_intersectionFacesTextureCoords;
cvf::ref<cvf::Part> m_intersectionFaces;
cvf::ref<cvf::Part> m_intersectionGridLines;
cvf::Collection<cvf::Part> m_annotationParts;
cvf::ref<cvf::Vec2fArray> m_intersectionFacesTextureCoords;
cvf::ref<RivIjkIntersectionGeometryGenerator> m_intersectionGenerator;
};
@@ -2,6 +2,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RivSurfacePartMgr.cpp
${CMAKE_CURRENT_LIST_DIR}/RivSurfaceIntersectionGeometryGenerator.cpp
${CMAKE_CURRENT_LIST_DIR}/RivReservoirSurfaceIntersectionSourceInfo.cpp
${CMAKE_CURRENT_LIST_DIR}/RivSurfaceIntersectionCurveTools.cpp
)
list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
@@ -0,0 +1,513 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RivSurfaceIntersectionCurveTools.h"
#include "Surface/RigSurface.h"
#include "Surface/RigSurfaceResampler.h"
#include "Rim3dView.h"
#include "RimAnnotationLineAppearance.h"
#include "RimCase.h"
#include "RimGridView.h"
#include "RimIntersection.h"
#include "RimSurface.h"
#include "RimSurfaceIntersectionBand.h"
#include "RimSurfaceIntersectionCollection.h"
#include "RimSurfaceIntersectionCurve.h"
#include "RivAnnotationSourceInfo.h"
#include "RivPartPriority.h"
#include "RivPolylineGenerator.h"
#include "cafEffectGenerator.h"
#include "cvfDrawableGeo.h"
#include "cvfGeometryBuilderDrawableGeo.h"
#include "cvfPart.h"
#include "cvfRenderStatePolygonOffset.h"
#include "cvfTransform.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <utility>
namespace
{
// Distance between the points the footprint is resampled to before the surface is looked up
const double maxLineSegmentLength = 1.0;
// Added to the extent a pillar is continued to, to keep a surface touching the end inside the search
const double pillarExtensionMargin = 1.0;
//--------------------------------------------------------------------------------------------------
/// Move a point onto the line through the pillar, at the depth of the point. The point is allowed to
/// fall outside the pillar itself, so a surface above or below the intersection is placed along the
/// continuation of the pillar rather than beside it.
//--------------------------------------------------------------------------------------------------
cvf::Vec3d projectOntoPillarLine( const cvf::Vec3d& pillarTop, const cvf::Vec3d& pillarBottom, const cvf::Vec3d& point )
{
const double pillarHeight = pillarTop.z() - pillarBottom.z();
if ( std::fabs( pillarHeight ) < 1.0e-9 ) return point;
const double t = ( pillarTop.z() - point.z() ) / pillarHeight;
return pillarTop + t * ( pillarBottom - pillarTop );
}
//--------------------------------------------------------------------------------------------------
/// The depth range covered by a surface, used to decide how far a pillar has to be continued
//--------------------------------------------------------------------------------------------------
std::pair<double, double> surfaceZRange( const RigSurface& surface )
{
double lowZ = std::numeric_limits<double>::max();
double highZ = -std::numeric_limits<double>::max();
for ( const auto& vertex : surface.vertices() )
{
lowZ = std::min( lowZ, vertex.z() );
highZ = std::max( highZ, vertex.z() );
}
return { lowZ, highZ };
}
//--------------------------------------------------------------------------------------------------
/// Find where the surface crosses the pillar. A surface that does not cross the curtain is estimated
/// by continuing the pillar in both directions, keeping the tilt of the pillar, so the curve carries
/// on past the extent of the intersection instead of stopping. The pillar is continued only as far as
/// the surface reaches, to keep the search for candidate triangles small.
//--------------------------------------------------------------------------------------------------
bool findSurfacePointOnPillar( RigSurface& surface,
const cvf::Vec3d& pillarTop,
const cvf::Vec3d& pillarBottom,
double surfaceLowZ,
double surfaceHighZ,
cvf::Vec3d& point )
{
if ( RigSurfaceResampler::computeIntersectionWithLine( &surface, pillarTop, pillarBottom, point ) ) return true;
if ( std::fabs( pillarTop.z() - pillarBottom.z() ) > 1.0e-9 )
{
const double searchHighZ = std::max( std::max( pillarTop.z(), pillarBottom.z() ), surfaceHighZ + pillarExtensionMargin );
const double searchLowZ = std::min( std::min( pillarTop.z(), pillarBottom.z() ), surfaceLowZ - pillarExtensionMargin );
const cvf::Vec3d extendedTop = projectOntoPillarLine( pillarTop, pillarBottom, cvf::Vec3d( 0.0, 0.0, searchHighZ ) );
const cvf::Vec3d extendedBottom = projectOntoPillarLine( pillarTop, pillarBottom, cvf::Vec3d( 0.0, 0.0, searchLowZ ) );
if ( RigSurfaceResampler::computeIntersectionWithLine( &surface, extendedTop, extendedBottom, point ) ) return true;
}
// The surface may not cover this position at all. Search in the XY plane around the pillar itself, as
// a continued pillar would move the search away from the intersection.
return RigSurfaceResampler::findClosestPointOnSurface( &surface, pillarTop, pillarBottom, point );
}
} // namespace
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::vector<cvf::Vec3d>> RivSurfaceCurtainPolyline::validRuns() const
{
std::vector<std::vector<cvf::Vec3d>> runs;
std::vector<cvf::Vec3d> currentRun;
for ( const auto& point : points )
{
if ( !point.isUndefined() )
{
currentRun.push_back( point );
}
else if ( currentRun.size() > 1 )
{
runs.push_back( currentRun );
currentRun.clear();
}
else
{
currentRun.clear();
}
}
if ( currentRun.size() > 1 ) runs.push_back( currentRun );
return runs;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimSurface*> RivSurfaceIntersectionCurveTools::referencedSurfaces( const RimSurfaceIntersectionCollection* surfaceIntersections )
{
if ( !surfaceIntersections ) return {};
std::vector<RimSurface*> surfaces;
auto appendSurface = [&surfaces]( RimSurface* surface )
{
if ( !surface ) return;
if ( std::find( surfaces.begin(), surfaces.end(), surface ) != surfaces.end() ) return;
surfaces.push_back( surface );
};
for ( auto curve : surfaceIntersections->surfaceIntersectionCurves() )
{
appendSurface( curve->surface() );
}
for ( auto band : surfaceIntersections->surfaceIntersectionBands() )
{
appendSurface( band->surface1() );
appendSurface( band->surface2() );
}
return surfaces;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<RimSurface*, RivSurfaceCurtainPolyline>
RivSurfaceIntersectionCurveTools::computeSurfaceCurtainPolylines( const std::vector<RimSurface*>& surfaces,
const RimIntersectionCurtain& curtain,
const std::function<cvf::Vec3d( const cvf::Vec3d&, size_t )>& pointTransform )
{
std::map<RimSurface*, RivSurfaceCurtainPolyline> surfacePolylines;
if ( !curtain.isValid() ) return surfacePolylines;
const auto resampledTrace = RigSurfaceResampler::computeResampledPolylineWithSegmentInfo( curtain.trace, maxLineSegmentLength );
if ( resampledTrace.empty() ) return surfacePolylines;
// Both ends of a pillar are interpolated using the position of the resampled point within its trace
// segment, so a resampled pillar stays on the curtain
std::vector<std::pair<cvf::Vec3d, cvf::Vec3d>> resampledPillars;
std::vector<size_t> segmentIndices;
resampledPillars.reserve( resampledTrace.size() );
segmentIndices.reserve( resampledTrace.size() );
for ( const auto& [point, segmentIndex] : resampledTrace )
{
if ( segmentIndex + 1 >= curtain.pillars.size() ) continue;
const auto& traceStart = curtain.trace[segmentIndex];
const auto& traceEnd = curtain.trace[segmentIndex + 1];
const double segmentLength = ( traceEnd - traceStart ).length();
const double t = segmentLength > 0.0 ? std::clamp( ( point - traceStart ).length() / segmentLength, 0.0, 1.0 ) : 0.0;
const auto& pillarStart = curtain.pillars[segmentIndex];
const auto& pillarEnd = curtain.pillars[segmentIndex + 1];
const cvf::Vec3d top = pillarStart.first + t * ( pillarEnd.first - pillarStart.first );
const cvf::Vec3d bottom = pillarStart.second + t * ( pillarEnd.second - pillarStart.second );
resampledPillars.emplace_back( top, bottom );
segmentIndices.push_back( segmentIndex );
}
for ( auto rimSurface : surfaces )
{
if ( !rimSurface ) continue;
rimSurface->loadDataIfRequired();
auto surface = rimSurface->surfaceData();
if ( !surface ) continue;
RivSurfaceCurtainPolyline curtainPolyline;
curtainPolyline.points.reserve( resampledPillars.size() );
const auto [surfaceLowZ, surfaceHighZ] = surfaceZRange( *surface );
bool anyValidPoint = false;
for ( size_t i = 0; i < resampledPillars.size(); i++ )
{
const auto& [pillarTop, pillarBottom] = resampledPillars[i];
cvf::Vec3d intersectionPoint;
const bool isValid = findSurfacePointOnPillar( *surface, pillarTop, pillarBottom, surfaceLowZ, surfaceHighZ, intersectionPoint );
// A point is always appended, also when the surface was not hit, to keep the two curves of a band index aligned
if ( isValid )
{
intersectionPoint = pointTransform( projectOntoPillarLine( pillarTop, pillarBottom, intersectionPoint ), segmentIndices[i] );
}
else
{
intersectionPoint = cvf::Vec3d::UNDEFINED;
}
curtainPolyline.points.push_back( intersectionPoint );
anyValidPoint = anyValidPoint || isValid;
}
if ( anyValidPoint ) surfacePolylines[rimSurface] = curtainPolyline;
}
return surfacePolylines;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Collection<cvf::Part>
RivSurfaceIntersectionCurveTools::createAnnotationParts( const RimSurfaceIntersectionCollection* surfaceIntersections,
const std::map<RimSurface*, RivSurfaceCurtainPolyline>& surfacePolylines,
cvf::Transform& scaleTransform )
{
cvf::Collection<cvf::Part> parts;
if ( !surfaceIntersections ) return parts;
auto appendParts = [&parts]( cvf::Collection<cvf::Part>& partsToAppend )
{
for ( size_t i = 0; i < partsToAppend.size(); i++ )
{
parts.push_back( partsToAppend.at( i ) );
}
};
for ( auto curve : surfaceIntersections->surfaceIntersectionCurves() )
{
if ( !curve->isChecked() ) continue;
auto surface = curve->surface();
if ( !surface ) continue;
auto it = surfacePolylines.find( surface );
if ( it == surfacePolylines.end() ) continue;
auto curveParts = createCurveParts( it->second,
surface->fullName(),
curve->lineAppearance()->color(),
curve->lineAppearance()->thickness(),
scaleTransform );
appendParts( curveParts );
}
for ( auto band : surfaceIntersections->surfaceIntersectionBands() )
{
if ( !band->isChecked() ) continue;
auto surface1 = band->surface1();
auto surface2 = band->surface2();
if ( !surface1 || !surface2 ) continue;
auto it1 = surfacePolylines.find( surface1 );
auto it2 = surfacePolylines.find( surface2 );
if ( it1 == surfacePolylines.end() || it2 == surfacePolylines.end() ) continue;
const auto& polylineA = it1->second;
const auto& polylineB = it2->second;
auto curvePartsA =
createCurveParts( polylineA, surface1->fullName(), band->lineAppearance()->color(), band->lineAppearance()->thickness(), scaleTransform );
appendParts( curvePartsA );
auto curvePartsB =
createCurveParts( polylineB, surface2->fullName(), band->lineAppearance()->color(), band->lineAppearance()->thickness(), scaleTransform );
appendParts( curvePartsB );
auto bandPart = createBandPart( polylineA, polylineB, band->bandColor(), band->bandOpacity(), band->polygonOffsetUnit() );
if ( bandPart.notNull() ) parts.push_back( bandPart.p() );
}
return parts;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Collection<cvf::Part> RivSurfaceIntersectionCurveTools::createAnnotationParts( const RimIntersection* intersection,
cvf::Transform& scaleTransform )
{
if ( !intersection || !intersection->supportsSurfaceIntersectionCurves() ) return {};
const auto surfaces = referencedSurfaces( intersection->surfaceIntersectionCollection() );
if ( surfaces.empty() ) return {};
const auto curtain = intersection->surfaceCurtain();
if ( !curtain.isValid() ) return {};
// The visualization parts are built in display coordinates
cvf::Vec3d displayOffset( 0.0, 0.0, 0.0 );
{
auto gridView = intersection->firstAncestorOrThisOfType<RimGridView>();
if ( gridView && gridView->ownerCase() ) displayOffset = gridView->ownerCase()->displayModelOffset();
}
auto pointTransform = [displayOffset]( const cvf::Vec3d& point, size_t segmentIndex ) { return point - displayOffset; };
const auto surfacePolylines = computeSurfaceCurtainPolylines( surfaces, curtain, pointTransform );
return createAnnotationParts( intersection->surfaceIntersectionCollection(), surfacePolylines, scaleTransform );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Collection<cvf::Part> RivSurfaceIntersectionCurveTools::createCurveParts( const RivSurfaceCurtainPolyline& polyline,
const QString& description,
const cvf::Color3f& color,
float lineWidth,
cvf::Transform& scaleTransform )
{
cvf::Collection<cvf::Part> parts;
std::vector<cvf::Vec3d> allDisplayCoords;
for ( const auto& run : polyline.validRuns() )
{
auto part = createCurvePart( run, color, lineWidth );
if ( part.isNull() ) continue;
part->setName( "Intersection " + description.toStdString() );
parts.push_back( part.p() );
// The polylines are defined in the display coordinate system without Z-scaling. The z-scaling is applied to the
// visualization parts using Part::setTransform(Transform* transform)
// The annotation objects are defined by display coordinates, so apply the Z-scaling to the coordinates
const auto& mat = scaleTransform.worldTransform();
for ( const auto& p : run )
{
allDisplayCoords.push_back( p.getTransformedPoint( mat ) );
}
}
if ( !parts.empty() && !allDisplayCoords.empty() )
{
// Add annotation info to be used to display label in Rim3dView::onViewNavigationChanged()
// Set the source info on one part only, as this data is only used for display of labels
auto annoObj = new RivAnnotationSourceInfo( description.toStdString(), allDisplayCoords );
annoObj->setLabelPositionStrategyHint( RivAnnotationTools::LabelPositionStrategy::RIGHT );
annoObj->setShowColor( true );
annoObj->setColor( color );
parts[0]->setSourceInfo( annoObj );
}
return parts;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<cvf::Part>
RivSurfaceIntersectionCurveTools::createCurvePart( const std::vector<cvf::Vec3d>& polyline, const cvf::Color3f& color, float lineWidth )
{
auto polylineGeo = RivPolylineGenerator::createLineAlongPolylineDrawable( polyline );
if ( polylineGeo.isNull() ) return nullptr;
polylineGeo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
cvf::ref<cvf::Part> part = new cvf::Part;
part->setDrawable( polylineGeo.p() );
part->updateBoundingBox();
part->setPriority( RivPartPriority::PartType::FaultMeshLines );
caf::MeshEffectGenerator lineEffGen( color );
lineEffGen.setLineWidth( lineWidth );
cvf::ref<cvf::Effect> eff = lineEffGen.generateUnCachedEffect();
cvf::ref<cvf::RenderStatePolygonOffset> polyOffset = new cvf::RenderStatePolygonOffset;
polyOffset->enableFillMode( true );
polyOffset->setFactor( -5 );
const double maxOffsetFactor = -1000;
polyOffset->setUnits( maxOffsetFactor );
eff->setRenderState( polyOffset.p() );
part->setEffect( eff.p() );
return part;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<cvf::Part> RivSurfaceIntersectionCurveTools::createBandPart( const RivSurfaceCurtainPolyline& polylineA,
const RivSurfaceCurtainPolyline& polylineB,
const cvf::Color3f& color,
float opacity,
double polygonOffsetUnit )
{
const size_t pointCount = std::min( polylineA.points.size(), polylineB.points.size() );
if ( pointCount < 2 ) return nullptr;
cvf::GeometryBuilderDrawableGeo geoBuilder;
bool anyQuad = false;
for ( size_t i = 1; i < pointCount; i++ )
{
const auto& pA0 = polylineA.points[i - 1];
const auto& pA1 = polylineA.points[i];
const auto& pB0 = polylineB.points[i - 1];
const auto& pB1 = polylineB.points[i];
// A quad requires both surfaces to be present at both ends of the segment
if ( pA0.isUndefined() || pA1.isUndefined() ) continue;
if ( pB0.isUndefined() || pB1.isUndefined() ) continue;
geoBuilder.addQuadByVertices( cvf::Vec3f( pA0 ), cvf::Vec3f( pA1 ), cvf::Vec3f( pB1 ), cvf::Vec3f( pB0 ) );
anyQuad = true;
}
if ( !anyQuad ) return nullptr;
cvf::ref<cvf::DrawableGeo> geo = geoBuilder.drawableGeo();
if ( geo.isNull() ) return nullptr;
geo->computeNormals();
geo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
cvf::ref<cvf::Part> part = new cvf::Part;
part->setName( "Surface Intersection Band" );
part->setDrawable( geo.p() );
part->updateBoundingBox();
part->setEnableMask( intersectionCellFaceBit );
part->setPriority( RivPartPriority::PartType::Transparent );
caf::SurfaceEffectGenerator geometryEffgen( cvf::Color4f( color, opacity ), caf::PO_NEG_LARGE );
cvf::ref<cvf::Effect> geometryOnlyEffect = geometryEffgen.generateUnCachedEffect();
{
cvf::ref<cvf::RenderStatePolygonOffset> polyOffset = new cvf::RenderStatePolygonOffset;
polyOffset->enableFillMode( true );
// The factor value is defined by enums in EffectGenerator::createAndConfigurePolygonOffsetRenderState()
// Use a factor that is more negative than the existing enums
const double offsetFactor = -5;
polyOffset->setFactor( offsetFactor );
polyOffset->setUnits( polygonOffsetUnit );
geometryOnlyEffect->setRenderState( polyOffset.p() );
}
part->setEffect( geometryOnlyEffect.p() );
return part;
}
@@ -0,0 +1,102 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimIntersection.h"
#include "cvfCollection.h"
#include "cvfColor3.h"
#include "cvfObject.h"
#include "cvfVector3.h"
#include <QString>
#include <functional>
#include <map>
#include <vector>
class RimSurface;
class RimSurfaceIntersectionCollection;
namespace cvf
{
class Part;
class Transform;
} // namespace cvf
//==================================================================================================
/// The curve created when a surface is projected onto an intersection. One entry per resampled
/// position along the intersection, set to cvf::Vec3d::UNDEFINED where the pillar at that position
/// missed the surface. The missing points are kept in the array instead of being removed, so the two
/// curves of a band stay index aligned.
//==================================================================================================
class RivSurfaceCurtainPolyline
{
public:
std::vector<cvf::Vec3d> points;
/// The stretches of consecutive valid points that are long enough to draw a line
std::vector<std::vector<cvf::Vec3d>> validRuns() const;
};
//==================================================================================================
/// Creation of surface intersection curves and bands. Shared by all intersection types that produce
/// a curtain, and that therefore can find the curve by looking the surface up along the pillar the
/// curtain is spanned between at each position along the intersection.
//==================================================================================================
class RivSurfaceIntersectionCurveTools
{
public:
static std::vector<RimSurface*> referencedSurfaces( const RimSurfaceIntersectionCollection* surfaceIntersections );
/// The surface is looked up along the pillars of the curtain, so the curve follows a tilted
/// curtain instead of a vertical plane through it. The curtain is resampled along its trace to
/// the resolution the curve is drawn at, and the pillars are interpolated to match.
///
/// pointTransform maps (worldPoint, traceSegmentIndex) to the output coordinate system. Use the
/// identity for intersections drawn in 3D only, or the flattening transform when the curve is
/// also displayed in a 2D intersection view.
static std::map<RimSurface*, RivSurfaceCurtainPolyline>
computeSurfaceCurtainPolylines( const std::vector<RimSurface*>& surfaces,
const RimIntersectionCurtain& curtain,
const std::function<cvf::Vec3d( const cvf::Vec3d&, size_t )>& pointTransform );
static cvf::Collection<cvf::Part> createAnnotationParts( const RimSurfaceIntersectionCollection* surfaceIntersections,
const std::map<RimSurface*, RivSurfaceCurtainPolyline>& surfacePolylines,
cvf::Transform& scaleTransform );
/// Curves and bands for an intersection that is drawn in 3D only, computed from the pillars
/// reported by the intersection itself
static cvf::Collection<cvf::Part> createAnnotationParts( const RimIntersection* intersection, cvf::Transform& scaleTransform );
private:
static cvf::Collection<cvf::Part> createCurveParts( const RivSurfaceCurtainPolyline& polyline,
const QString& description,
const cvf::Color3f& color,
float lineWidth,
cvf::Transform& scaleTransform );
static cvf::ref<cvf::Part> createCurvePart( const std::vector<cvf::Vec3d>& polyline, const cvf::Color3f& color, float lineWidth );
static cvf::ref<cvf::Part> createBandPart( const RivSurfaceCurtainPolyline& polylineA,
const RivSurfaceCurtainPolyline& polylineB,
const cvf::Color3f& color,
float opacity,
double polygonOffsetUnit );
};
@@ -28,10 +28,12 @@
#include "RivBoxIntersectionPartMgr.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafDisplayCoordTransform.h"
#include "cafPdmUiDoubleSliderEditor.h"
#include "cafPdmUiPushButtonEditor.h"
#include "cafPdmUiSliderEditor.h"
#include "cafPdmUiTreeOrdering.h"
namespace caf
{
@@ -188,6 +190,35 @@ RimBoxIntersection::SinglePlaneState RimBoxIntersection::singlePlaneState() cons
return m_singlePlaneState();
}
//--------------------------------------------------------------------------------------------------
/// Only the single plane states X and Y give a vertical curtain. A depth slice, and the full box,
/// would require the surface to be contoured instead of projected along a vertical ray.
//--------------------------------------------------------------------------------------------------
bool RimBoxIntersection::supportsSurfaceIntersectionCurves() const
{
return m_singlePlaneState() == PLANE_STATE_X || m_singlePlaneState() == PLANE_STATE_Y;
}
//--------------------------------------------------------------------------------------------------
/// The pillars span the depth range of the box, so the curve is clipped to the box face. The depth
/// fields are positive downwards, while z is positive upwards.
//--------------------------------------------------------------------------------------------------
RimIntersectionCurtain RimBoxIntersection::surfaceCurtain() const
{
std::vector<cvf::Vec3d> trace;
if ( m_singlePlaneState() == PLANE_STATE_X )
{
trace = { cvf::Vec3d( m_minXCoord, m_minYCoord, 0.0 ), cvf::Vec3d( m_minXCoord, m_maxYCoord, 0.0 ) };
}
else if ( m_singlePlaneState() == PLANE_STATE_Y )
{
trace = { cvf::Vec3d( m_minXCoord, m_minYCoord, 0.0 ), cvf::Vec3d( m_maxXCoord, m_minYCoord, 0.0 ) };
}
return verticalCurtain( trace, -m_minDepth(), -m_maxDepth() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -504,6 +535,26 @@ void RimBoxIntersection::defineUiOrdering( QString uiConfigName, caf::PdmUiOrder
defineSeparateDataSourceUi( uiConfigName, uiOrdering );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimBoxIntersection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
appendCommonMenuItems( menuBuilder );
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimBoxIntersection::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
{
appendSurfaceIntersectionsToTreeOrdering( uiTreeOrdering );
uiTreeOrdering.skipRemainingChildren( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -80,12 +80,20 @@ public:
const RivIntersectionGeometryGeneratorInterface* intersectionGeometryGenerator() const override;
bool supportsSurfaceIntersectionCurves() const override;
RimIntersectionCurtain surfaceCurtain() const override;
void rebuildGeometryAndScheduleCreateDisplayModel() override;
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
protected:
caf::PdmFieldHandle* userDescriptionField() final;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
void initAfterRead() override;
protected slots:
@@ -96,7 +104,6 @@ private:
friend class RimIntersectionCollection;
void updateBoxManipulatorGeometry();
void rebuildGeometryAndScheduleCreateDisplayModel();
void updateVisibility();
void clampSinglePlaneValues();
void switchSingelPlaneState();
@@ -47,6 +47,7 @@
#include "RivExtrudedCurveIntersectionPartMgr.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmFieldScriptingCapabilityCvfVec3d.h"
#include "cafPdmObjectScriptingCapability.h"
@@ -253,10 +254,6 @@ RimExtrudedCurveIntersection::RimExtrudedCurveIntersection()
caf::PdmUiPushButtonEditor::configureEditorLabelLeft( &m_inputTwoAzimuthPointsFromViewerEnabled );
m_inputTwoAzimuthPointsFromViewerEnabled = false;
CAF_PDM_InitFieldNoDefault( &m_surfaceIntersections, "SurfaceIntersections", "Surface Intersections" );
m_surfaceIntersections = new RimSurfaceIntersectionCollection;
m_surfaceIntersections->objectChanged.connect( this, &RimExtrudedCurveIntersection::onSurfaceIntersectionsChanged );
CAF_PDM_InitField( &m_depthUpperThreshold, "UpperThreshold", -300000.0, "Upper Threshold" );
m_depthUpperThreshold.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() );
@@ -731,19 +728,26 @@ QList<caf::PdmOptionItemInfo> RimExtrudedCurveIntersection::calculateValueOption
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimExtrudedCurveIntersection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
appendCommonMenuItems( menuBuilder );
menuBuilder << "RicSeismicSectionFromIntersectionFeature";
menuBuilder.addSeparator();
menuBuilder << "RicNewIntersectionViewFeature";
menuBuilder.addSeparator();
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimExtrudedCurveIntersection::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
{
for ( auto c : m_surfaceIntersections->surfaceIntersectionCurves() )
{
uiTreeOrdering.add( c );
}
for ( auto c : m_surfaceIntersections->surfaceIntersectionBands() )
{
uiTreeOrdering.add( c );
}
appendSurfaceIntersectionsToTreeOrdering( uiTreeOrdering );
uiTreeOrdering.skipRemainingChildren( true );
}
@@ -1298,33 +1302,24 @@ bool RimExtrudedCurveIntersection::showIntersectionGeometry() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimSurfaceIntersectionCurve*> RimExtrudedCurveIntersection::surfaceIntersectionCurves() const
bool RimExtrudedCurveIntersection::supportsSurfaceIntersectionCurves() const
{
return m_surfaceIntersections->surfaceIntersectionCurves();
return true;
}
//--------------------------------------------------------------------------------------------------
///
/// The first polyline is used, as the surface intersection curve follows the same line as the
/// intersection geometry itself. The curtain is treated as a vertical extrusion, also when the
/// intersection has a tilted extrusion direction.
//--------------------------------------------------------------------------------------------------
std::vector<RimSurfaceIntersectionBand*> RimExtrudedCurveIntersection::surfaceIntersectionBands() const
RimIntersectionCurtain RimExtrudedCurveIntersection::surfaceCurtain() const
{
return m_surfaceIntersections->surfaceIntersectionBands();
}
auto lines = polyLines();
if ( lines.empty() ) return {};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSurfaceIntersectionCurve* RimExtrudedCurveIntersection::addIntersectionCurve()
{
return m_surfaceIntersections->addIntersectionCurve();
}
const double extent = defaultCurtainExtent();
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSurfaceIntersectionBand* RimExtrudedCurveIntersection::addIntersectionBand()
{
return m_surfaceIntersections->addIntersectionBand();
return verticalCurtain( lines.front(), extent, -extent );
}
//--------------------------------------------------------------------------------------------------
@@ -1388,15 +1383,6 @@ void RimExtrudedCurveIntersection::appendOptionItemsForSources( int
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimExtrudedCurveIntersection::onSurfaceIntersectionsChanged( const caf::SignalEmitter* emitter )
{
updateAllRequiredEditors();
rebuildGeometryAndScheduleCreateDisplayModel();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -129,15 +129,15 @@ public:
double extentLength();
bool hasDefiningPoints() const;
std::vector<RimSurfaceIntersectionCurve*> surfaceIntersectionCurves() const;
std::vector<RimSurfaceIntersectionBand*> surfaceIntersectionBands() const;
RimSurfaceIntersectionCurve* addIntersectionCurve();
RimSurfaceIntersectionBand* addIntersectionBand();
bool supportsSurfaceIntersectionCurves() const override;
RimIntersectionCurtain surfaceCurtain() const override;
bool showIntersectionGeometry() const;
int branchIndex() const;
void rebuildGeometryAndScheduleCreateDisplayModel();
void rebuildGeometryAndScheduleCreateDisplayModel() override;
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
private:
caf::PdmFieldHandle* userDescriptionField() final;
@@ -159,8 +159,6 @@ private:
void appendOptionItemsForSources( int currentLevel, RimSurfaceCollection* currentCollection, QList<caf::PdmOptionItemInfo>& options ) const;
void onSurfaceIntersectionsChanged( const caf::SignalEmitter* emitter );
std::vector<cvf::Vec3d> pointsXYD() const;
void setPointsFromXYD( const std::vector<cvf::Vec3d>& pointsXYD );
@@ -207,8 +205,6 @@ private:
caf::PdmField<std::vector<cvf::Vec3d>> m_customExtrusionPoints;
caf::PdmField<std::vector<cvf::Vec3d>> m_twoAzimuthPoints;
caf::PdmChildField<RimSurfaceIntersectionCollection*> m_surfaceIntersections;
cvf::ref<RivExtrudedCurveIntersectionPartMgr> m_crossSectionPartMgr;
std::vector<std::vector<cvf::Vec3d>> m_simulationWellBranchCenterlines;
@@ -25,7 +25,9 @@
#include "RivIjkIntersectionPartMgr.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafPdmUiSliderEditor.h"
#include "cafPdmUiTreeOrdering.h"
#include <algorithm>
@@ -201,6 +203,129 @@ void RimIjkIntersection::setToDefaultValues()
m_fixedIndex = nk / 2 + 1;
}
//--------------------------------------------------------------------------------------------------
/// The I and J axes give a vertical curtain. A K slice is horizontal, and would require the surface
/// to be contoured instead of projected along a vertical ray.
//--------------------------------------------------------------------------------------------------
bool RimIjkIntersection::supportsSurfaceIntersectionCurves() const
{
return m_axis() != GridAxis::AXIS_K;
}
//--------------------------------------------------------------------------------------------------
/// Walk the cells of the index plane along the varying axis and pick the cell face corners. Each
/// pillar runs from the top of the first k layer to the bottom of the last k layer, so a tilted
/// pillar gives a curve that lies on the curtain rather than on a vertical plane through it.
//--------------------------------------------------------------------------------------------------
RimIntersectionCurtain RimIjkIntersection::surfaceCurtain() const
{
if ( !supportsSurfaceIntersectionCurves() ) return {};
RigMainGrid* grid = mainGrid();
if ( !grid ) return {};
const auto cellRange = clampedCellRange( grid );
if ( !cellRange ) return {};
const bool alongJ = m_axis() == GridAxis::AXIS_I;
// Corner indices of the cell face, see the hex vertex numbering in StructGridInterface::cellFaceVertexIndices().
// The first corner is at the start of the varying axis, the second at the end of the same axis. Corners 0-3 are
// the low k side of the cell, and adding 4 gives the corner on the same pillar at the high k side.
const int cornersPerKSide = 4;
int startCorner = 0;
int endCorner = 0;
if ( alongJ )
{
startCorner = m_useNegativeFace() ? 0 : 1;
endCorner = m_useNegativeFace() ? 3 : 2;
}
else
{
startCorner = m_useNegativeFace() ? 0 : 3;
endCorner = m_useNegativeFace() ? 1 : 2;
}
// The fixed axis is collapsed to a single index by clampedCellRange()
const size_t fixedI = cellRange->min().i();
const size_t fixedJ = cellRange->min().j();
const size_t firstK = cellRange->min().k();
const size_t lastK = cellRange->max().k();
const size_t varyingMin = alongJ ? cellRange->min().j() : cellRange->min().i();
const size_t varyingMax = alongJ ? cellRange->max().j() : cellRange->max().i();
RimIntersectionCurtain curtain;
// The pillar at one position is spanned between the first and the last cell of the k range. The
// top of the pillar is used as the trace, so the curve is resampled along the top of the curtain.
auto appendPillar = [&]( size_t i, size_t j, int corner )
{
const size_t topCellIndex = grid->cellIndexFromIJK( i, j, firstK );
const size_t bottomCellIndex = grid->cellIndexFromIJK( i, j, lastK );
if ( topCellIndex == cvf::UNDEFINED_SIZE_T || bottomCellIndex == cvf::UNDEFINED_SIZE_T ) return;
if ( grid->cell( topCellIndex ).isInvalid() || grid->cell( bottomCellIndex ).isInvalid() ) return;
const auto topCorners = grid->cellCornerVertices( topCellIndex );
const auto bottomCorners = grid->cellCornerVertices( bottomCellIndex );
curtain.trace.push_back( topCorners[corner] );
curtain.pillars.emplace_back( topCorners[corner], bottomCorners[corner + cornersPerKSide] );
};
for ( size_t varying = varyingMin; varying <= varyingMax; ++varying )
{
const size_t i = alongJ ? fixedI : varying;
const size_t j = alongJ ? varying : fixedJ;
appendPillar( i, j, startCorner );
if ( varying == varyingMax ) appendPillar( i, j, endCorner );
}
return curtain;
}
//--------------------------------------------------------------------------------------------------
/// The index range of the visible cells, with the fixed axis collapsed to the fixed index
//--------------------------------------------------------------------------------------------------
std::optional<RigBoundingBoxIjk<caf::VecIjk0>> RimIjkIntersection::clampedCellRange( const RigMainGrid* grid ) const
{
if ( !grid ) return {};
const size_t ni = grid->cellCountI();
const size_t nj = grid->cellCountJ();
const size_t nk = grid->cellCountK();
if ( ni == 0 || nj == 0 || nk == 0 ) return {};
const auto range = ijkRange();
caf::VecIjk0 min = range.min();
caf::VecIjk0 max = range.max();
const size_t fixed = static_cast<size_t>( std::max( 0, fixedIndex() ) );
switch ( m_axis() )
{
case GridAxis::AXIS_I:
min.x() = max.x() = std::min( fixed, ni - 1 );
break;
case GridAxis::AXIS_J:
min.y() = max.y() = std::min( fixed, nj - 1 );
break;
case GridAxis::AXIS_K:
default:
min.z() = max.z() = std::min( fixed, nk - 1 );
break;
}
const RigBoundingBoxIjk<caf::VecIjk0> gridBounds( caf::VecIjk0::ZERO, caf::VecIjk0( ni - 1, nj - 1, nk - 1 ) );
return RigBoundingBoxIjk<caf::VecIjk0>( min, max ).clamp( gridBounds );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -347,6 +472,26 @@ void RimIjkIntersection::defineUiOrdering( QString uiConfigName, caf::PdmUiOrder
uiOrdering.skipRemainingFields( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIjkIntersection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
appendCommonMenuItems( menuBuilder );
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIjkIntersection::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
{
appendSurfaceIntersectionsToTreeOrdering( uiTreeOrdering );
uiTreeOrdering.skipRemainingChildren( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -26,6 +26,8 @@
#include "cafPdmField.h"
#include "cafVecIjk.h"
#include <optional>
class RigMainGrid;
class RivIjkIntersectionPartMgr;
@@ -75,16 +77,27 @@ public:
const RivIntersectionGeometryGeneratorInterface* intersectionGeometryGenerator() const override;
bool supportsSurfaceIntersectionCurves() const override;
RimIntersectionCurtain surfaceCurtain() const override;
void rebuildGeometryAndScheduleCreateDisplayModel() override;
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
/// The index range of the visible cells, with the fixed axis collapsed to the fixed index. The
/// grid is passed in, as the intersection is not always reachable from a view.
std::optional<RigBoundingBoxIjk<caf::VecIjk0>> clampedCellRange( const RigMainGrid* grid ) const;
protected:
caf::PdmFieldHandle* userDescriptionField() final;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
private:
int axisCellCount() const;
void rebuildGeometryAndScheduleCreateDisplayModel();
int axisCellCount() const;
private:
caf::PdmField<QString> m_name;
@@ -28,11 +28,16 @@
#include "RimGridView.h"
#include "RimIntersectionResultDefinition.h"
#include "RimIntersectionResultsDefinitionCollection.h"
#include "RimSurfaceIntersectionBand.h"
#include "RimSurfaceIntersectionCollection.h"
#include "RimSurfaceIntersectionCurve.h"
#include "RivEclipseIntersectionGrid.h"
#include "RivFemIntersectionGrid.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafPdmUiCheckBoxEditor.h"
#include "cafPdmUiTreeOrdering.h"
CAF_PDM_ABSTRACT_SOURCE_INIT( RimIntersection, "RimIntersectionHandle" );
@@ -49,6 +54,10 @@ RimIntersection::RimIntersection()
CAF_PDM_InitField( &m_useSeparateDataSource, "UseSeparateIntersectionDataSource", true, "Enable" );
CAF_PDM_InitFieldNoDefault( &m_separateDataSource, "SeparateIntersectionDataSource", "Source" );
CAF_PDM_InitFieldNoDefault( &m_surfaceIntersections, "SurfaceIntersections", "Surface Intersections" );
m_surfaceIntersections = new RimSurfaceIntersectionCollection;
m_surfaceIntersections->objectChanged.connect( this, &RimIntersection::onSurfaceIntersectionsChanged );
}
//--------------------------------------------------------------------------------------------------
@@ -184,6 +193,134 @@ void RimIntersection::updateDefaultSeparateDataSource()
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSurfaceIntersectionCollection* RimIntersection::surfaceIntersectionCollection() const
{
return m_surfaceIntersections();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimSurfaceIntersectionCurve*> RimIntersection::surfaceIntersectionCurves() const
{
return m_surfaceIntersections->surfaceIntersectionCurves();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimSurfaceIntersectionBand*> RimIntersection::surfaceIntersectionBands() const
{
return m_surfaceIntersections->surfaceIntersectionBands();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSurfaceIntersectionCurve* RimIntersection::addIntersectionCurve()
{
return m_surfaceIntersections->addIntersectionCurve();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSurfaceIntersectionBand* RimIntersection::addIntersectionBand()
{
return m_surfaceIntersections->addIntersectionBand();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimIntersection::supportsSurfaceIntersectionCurves() const
{
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimIntersectionCurtain RimIntersection::surfaceCurtain() const
{
return {};
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimIntersection::defaultCurtainExtent()
{
return 10000.0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimIntersectionCurtain RimIntersection::verticalCurtain( const std::vector<cvf::Vec3d>& trace, double topZ, double bottomZ )
{
RimIntersectionCurtain curtain;
curtain.trace = trace;
curtain.pillars.reserve( trace.size() );
for ( const auto& point : trace )
{
curtain.pillars.emplace_back( cvf::Vec3d( point.x(), point.y(), topZ ), cvf::Vec3d( point.x(), point.y(), bottomZ ) );
}
return curtain;
}
//--------------------------------------------------------------------------------------------------
/// Overridden by the intersection types that build their geometry from a part manager
//--------------------------------------------------------------------------------------------------
void RimIntersection::rebuildGeometryAndScheduleCreateDisplayModel()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIntersection::appendSurfaceIntersectionsToTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering )
{
for ( auto c : m_surfaceIntersections->surfaceIntersectionCurves() )
{
uiTreeOrdering.add( c );
}
for ( auto c : m_surfaceIntersections->surfaceIntersectionBands() )
{
uiTreeOrdering.add( c );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIntersection::appendCommonMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicCreateSurfaceIntersectionBandFeature";
menuBuilder << "RicCreateSurfaceIntersectionCurveFeature";
menuBuilder.addSeparator();
menuBuilder << "RicPasteIntersectionsFeature";
menuBuilder.addSeparator();
menuBuilder << "RicAppendIntersectionFeature";
menuBuilder << "RicAppendIntersectionBoxFeature";
menuBuilder << "RicAppendIjkIntersectionFeature";
menuBuilder.addSeparator();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIntersection::onSurfaceIntersectionsChanged( const caf::SignalEmitter* emitter )
{
updateAllRequiredEditors();
rebuildGeometryAndScheduleCreateDisplayModel();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -17,17 +17,49 @@
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafPdmChildField.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include "cafPdmPtrField.h"
#include "cvfObject.h"
#include "cvfVector3.h"
#include <utility>
#include <vector>
class RimIntersectionResultDefinition;
class RivIntersectionHexGridInterface;
class RimIntersectionResultsDefinitionCollection;
class RimSurfaceIntersectionBand;
class RimSurfaceIntersectionCollection;
class RimSurfaceIntersectionCurve;
class RivIntersectionGeometryGeneratorInterface;
namespace caf
{
class CmdFeatureMenuBuilder;
class PdmUiTreeOrdering;
} // namespace caf
//==================================================================================================
/// The curtain of an intersection, described at a set of positions along it
//==================================================================================================
class RimIntersectionCurtain
{
public:
/// The polyline the curve is resampled along, in 3D. Kept apart from the pillars so the samples
/// are distributed along the intersection itself, also where the pillars are long.
std::vector<cvf::Vec3d> trace;
/// For each point of the trace, the segment the surface is looked up along, from the top to the
/// bottom of the curtain. Tilted for an intersection that follows the grid pillars, which is
/// what puts the curve on the curtain instead of on a vertical plane through it.
std::vector<std::pair<cvf::Vec3d, cvf::Vec3d>> pillars;
bool isValid() const { return trace.size() == pillars.size() && trace.size() > 1; }
};
class RimIntersection : public caf::PdmObject
{
CAF_PDM_HEADER_INIT;
@@ -47,6 +79,20 @@ public:
virtual const RivIntersectionGeometryGeneratorInterface* intersectionGeometryGenerator() const = 0;
RimSurfaceIntersectionCollection* surfaceIntersectionCollection() const;
std::vector<RimSurfaceIntersectionCurve*> surfaceIntersectionCurves() const;
std::vector<RimSurfaceIntersectionBand*> surfaceIntersectionBands() const;
RimSurfaceIntersectionCurve* addIntersectionCurve();
RimSurfaceIntersectionBand* addIntersectionBand();
/// True if the intersection is a vertical curtain, and therefore can display surface intersection
/// curves and bands. Horizontal sections would require contouring instead of a vertical ray cast.
virtual bool supportsSurfaceIntersectionCurves() const;
virtual RimIntersectionCurtain surfaceCurtain() const;
virtual void rebuildGeometryAndScheduleCreateDisplayModel();
protected:
virtual RimIntersectionResultsDefinitionCollection* findSeparateResultsCollection();
@@ -56,8 +102,25 @@ protected:
void defineSeparateDataSourceUi( QString uiConfigName, caf::PdmUiOrdering& uiOrdering );
void updateDefaultSeparateDataSource();
void appendSurfaceIntersectionsToTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering );
/// The context menu entries shared by all intersection types, ending with a separator so the
/// caller can add its own entries before the trailing copy-to-all-views command
void appendCommonMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const;
/// A curtain that is a vertical extrusion of the trace, spanning [bottomZ, topZ]
static RimIntersectionCurtain verticalCurtain( const std::vector<cvf::Vec3d>& trace, double topZ, double bottomZ );
/// Reaches past any reservoir depth, used when the curtain has no defined vertical extent
static double defaultCurtainExtent();
caf::PdmField<bool> m_isActive;
caf::PdmField<bool> m_showInactiveCells;
caf::PdmField<bool> m_useSeparateDataSource;
caf::PdmPtrField<RimIntersectionResultDefinition*> m_separateDataSource;
caf::PdmChildField<RimSurfaceIntersectionCollection*> m_surfaceIntersections;
private:
void onSurfaceIntersectionsChanged( const caf::SignalEmitter* emitter );
};
@@ -41,6 +41,7 @@
#include "RivExtrudedCurveIntersectionPartMgr.h"
#include "RivIjkIntersectionPartMgr.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafPdmUiCheckBoxEditor.h"
#include "cafPdmUiDoubleSliderEditor.h"
@@ -246,6 +247,20 @@ bool RimIntersectionCollection::hasAnyActiveSeparateResults()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIntersectionCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicPasteIntersectionsFeature";
menuBuilder.addSeparator();
menuBuilder << "RicAppendIntersectionFeature";
menuBuilder << "RicAppendIntersectionBoxFeature";
menuBuilder << "RicAppendIjkIntersectionFeature";
menuBuilder.addSeparator();
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
void RimIntersectionCollection::appendPartsToModel( Rim3dView& view, cvf::ModelBasicList* model, cvf::Transform* scaleTransform )
{
if ( !isActive() ) return;
@@ -291,9 +306,10 @@ void RimIntersectionCollection::appendDynamicPartsToModel( cvf::ModelBasicList*
{
if ( cs->isActive() )
{
cs->intersectionBoxPartMgr()->generatePartGeometry( visibleCells );
cs->intersectionBoxPartMgr()->generatePartGeometry( visibleCells, scaleTransform );
cs->intersectionBoxPartMgr()->appendNativeIntersectionFacesToModel( model, scaleTransform );
cs->intersectionBoxPartMgr()->appendMeshLinePartsToModel( model, scaleTransform );
cs->intersectionBoxPartMgr()->appendAnnotationPartsToModel( model, scaleTransform );
}
}
@@ -301,9 +317,10 @@ void RimIntersectionCollection::appendDynamicPartsToModel( cvf::ModelBasicList*
{
if ( cs->isActive() )
{
cs->intersectionPartMgr()->generatePartGeometry( visibleCells );
cs->intersectionPartMgr()->generatePartGeometry( visibleCells, scaleTransform );
cs->intersectionPartMgr()->appendNativeIntersectionFacesToModel( model, scaleTransform );
cs->intersectionPartMgr()->appendMeshLinePartsToModel( model, scaleTransform );
cs->intersectionPartMgr()->appendAnnotationPartsToModel( model, scaleTransform );
}
}
}
@@ -36,6 +36,11 @@ class RimEclipseCellColors;
class RimSimWellInView;
class RivTernaryScalarMapper;
namespace caf
{
class CmdFeatureMenuBuilder;
}
namespace cvf
{
class ModelBasicList;
@@ -93,6 +98,8 @@ public:
void onChildAdded( caf::PdmFieldHandle* containerForNewObject ) override;
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
bool isActive() const;
void setActive( bool active );
@@ -36,6 +36,7 @@
#include "RiuViewer.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafPdmUiTreeOrdering.h"
CAF_PDM_SOURCE_INIT( RimIntersectionResultDefinition, "IntersectionResultDefinition" );
@@ -88,6 +89,14 @@ RimIntersectionResultDefinition::~RimIntersectionResultDefinition()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIntersectionResultDefinition::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicAppendSeparateIntersectionResultFeature";
}
bool RimIntersectionResultDefinition::isActive() const
{
return m_isActive();
@@ -31,6 +31,11 @@ class RimRegularLegendConfig;
class RimTernaryLegendConfig;
class RiuViewer;
namespace caf
{
class CmdFeatureMenuBuilder;
}
class RimIntersectionResultDefinition : public caf::PdmObject
{
CAF_PDM_HEADER_INIT;
@@ -39,6 +44,8 @@ public:
RimIntersectionResultDefinition();
~RimIntersectionResultDefinition() override;
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
bool isActive() const;
bool isInAction() const;
QString autoName() const;
@@ -22,6 +22,7 @@
#include "RimGridView.h"
#include "RimIntersectionCollection.h"
#include "RimIntersectionResultDefinition.h"
#include "cafCmdFeatureMenuBuilder.h"
CAF_PDM_SOURCE_INIT( RimIntersectionResultsDefinitionCollection, "RimIntersectionResultsDefinitionCollection" );
@@ -50,6 +51,14 @@ RimIntersectionResultsDefinitionCollection::~RimIntersectionResultsDefinitionCol
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimIntersectionResultsDefinitionCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicAppendSeparateIntersectionResultFeature";
}
bool RimIntersectionResultsDefinitionCollection::isActive() const
{
return m_isActive();
@@ -22,6 +22,11 @@
class RimIntersectionResultDefinition;
namespace caf
{
class CmdFeatureMenuBuilder;
}
class RimIntersectionResultsDefinitionCollection : public caf::PdmObjectCollection<RimIntersectionResultDefinition>
{
CAF_PDM_HEADER_INIT;
@@ -30,6 +35,8 @@ public:
RimIntersectionResultsDefinitionCollection();
~RimIntersectionResultsDefinitionCollection() override;
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
bool isActive() const;
std::vector<RimIntersectionResultDefinition*> intersectionResultsDefinitions() const;
@@ -41,7 +41,6 @@
#include "RimAnnotationCollection.h"
#include "RimAnnotationGroupCollection.h"
#include "RimAnnotationInViewCollection.h"
#include "RimBoxIntersection.h"
#include "RimCalcScript.h"
#include "RimCaseCollection.h"
#include "RimCellEdgeColors.h"
@@ -68,7 +67,6 @@
#include "RimEnsembleCurveFilterCollection.h"
#include "RimEnsembleCurveSetCollection.h"
#include "RimEnsembleFractureStatisticsCollection.h"
#include "RimExtrudedCurveIntersection.h"
#include "RimFaultInView.h"
#include "RimFaultReactivationModel.h"
#include "RimFileWellPath.h"
@@ -90,10 +88,6 @@
#include "RimGridCrossPlotCollection.h"
#include "RimGridCrossPlotDataSet.h"
#include "RimIdenticalGridCaseGroup.h"
#include "RimIjkIntersection.h"
#include "RimIntersectionCollection.h"
#include "RimIntersectionResultDefinition.h"
#include "RimIntersectionResultsDefinitionCollection.h"
#include "RimModeledWellPath.h"
#include "RimMultiPlot.h"
#include "RimObservedSummaryData.h"
@@ -764,58 +758,6 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
{
menuBuilder << "RicAddWellLogToPlotFeature";
}
else if ( dynamic_cast<RimIntersectionCollection*>( firstUiItem ) )
{
menuBuilder << "RicPasteIntersectionsFeature";
menuBuilder.addSeparator();
menuBuilder << "RicAppendIntersectionFeature";
menuBuilder << "RicAppendIntersectionBoxFeature";
menuBuilder << "RicAppendIjkIntersectionFeature";
menuBuilder.addSeparator();
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
else if ( dynamic_cast<RimExtrudedCurveIntersection*>( firstUiItem ) )
{
menuBuilder << "RicCreateSurfaceIntersectionBandFeature";
menuBuilder << "RicCreateSurfaceIntersectionCurveFeature";
menuBuilder.addSeparator();
menuBuilder << "RicPasteIntersectionsFeature";
menuBuilder.addSeparator();
menuBuilder << "RicAppendIntersectionFeature";
menuBuilder << "RicAppendIntersectionBoxFeature";
menuBuilder << "RicAppendIjkIntersectionFeature";
menuBuilder.addSeparator();
menuBuilder << "RicSeismicSectionFromIntersectionFeature";
menuBuilder.addSeparator();
menuBuilder << "RicNewIntersectionViewFeature";
menuBuilder.addSeparator();
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
else if ( dynamic_cast<RimBoxIntersection*>( firstUiItem ) )
{
menuBuilder << "RicPasteIntersectionsFeature";
menuBuilder.addSeparator();
menuBuilder << "RicAppendIntersectionFeature";
menuBuilder << "RicAppendIntersectionBoxFeature";
menuBuilder << "RicAppendIjkIntersectionFeature";
menuBuilder.addSeparator();
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
else if ( dynamic_cast<RimIjkIntersection*>( firstUiItem ) )
{
menuBuilder << "RicPasteIntersectionsFeature";
menuBuilder.addSeparator();
menuBuilder << "RicAppendIntersectionFeature";
menuBuilder << "RicAppendIntersectionBoxFeature";
menuBuilder << "RicAppendIjkIntersectionFeature";
menuBuilder.addSeparator();
menuBuilder << "RicCopyIntersectionsToAllViewsInCaseFeature";
}
else if ( dynamic_cast<RimIntersectionResultsDefinitionCollection*>( firstUiItem ) ||
dynamic_cast<RimIntersectionResultDefinition*>( firstUiItem ) )
{
menuBuilder << "RicAppendSeparateIntersectionResultFeature";
}
else if ( dynamic_cast<RimSimWellInView*>( firstUiItem ) )
{
menuBuilder << "RicNewWellLogExtractionCurveFeature";