#11971 Add cell selection tool

Select cell in 3D by entering IJK
Select cell in 3D by entering coordinate E N Depth
This commit is contained in:
Magne Sjaastad
2025-01-28 11:20:32 +01:00
committed by GitHub
parent 41aed338ab
commit 86f8da37bc
9 changed files with 438 additions and 8 deletions

View File

@@ -110,6 +110,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RiuMenuBarBuildTools.h
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotRectAnnotation.h
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotZoomerMultiAxes.h
${CMAKE_CURRENT_LIST_DIR}/RiuCellSelectionTool.h
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -221,6 +222,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RiuMenuBarBuildTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotRectAnnotation.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQwtPlotZoomerMultiAxes.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuCellSelectionTool.cpp
)
if(RESINSIGHT_USE_QT_CHARTS)

View File

@@ -20,6 +20,7 @@
#include "Riu3dSelectionManager.h"
#include "Rim2dIntersectionView.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseResultDefinition.h"
#include "RimEclipseView.h"
#include "RimGeoMechResultDefinition.h"
@@ -178,6 +179,24 @@ RiuEclipseSelectionItem::RiuEclipseSelectionItem( RimGridView*
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuEclipseSelectionItem::RiuEclipseSelectionItem( RimEclipseView* view, size_t gridIndex, size_t gridLocalCellIndex )
{
m_view = view;
m_resultDefinition = view ? view->cellResult() : nullptr;
m_timestepIdx = view ? view->currentTimeStep() : 0;
m_gridIndex = gridIndex;
m_gridLocalCellIndex = gridLocalCellIndex;
m_nncIndex = cvf::UNDEFINED_SIZE_T;
m_color = cvf::Color3f( 1.0f, 0.0f, 0.0f );
m_face = cvf::StructGridInterface::NO_FACE;
m_localIntersectionPointInDisplay = cvf::Vec3d::UNDEFINED;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@@ -148,6 +148,8 @@ public:
cvf::StructGridInterface::FaceType face,
const cvf::Vec3d& localIntersectionPointInDisplay );
explicit RiuEclipseSelectionItem( RimEclipseView* view, size_t gridIndex, size_t gridLocalCellIndex );
~RiuEclipseSelectionItem() override{};
RiuSelectionType type() const override { return ECLIPSE_SELECTION_OBJECT; }

View File

@@ -0,0 +1,313 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 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 "RiuCellSelectionTool.h"
#include "RiaApplication.h"
#include "Rim3dView.h"
#include "RimEclipseView.h"
#include "RigMainGrid.h"
#include "Riu3dSelectionManager.h"
#include "RiuViewer.h"
#include <QAction>
#include <QButtonGroup>
#include <QDialog>
#include <QDoubleValidator>
#include <QHBoxLayout>
#include <QIntValidator>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
#include <QVBoxLayout>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuCellSelectionTool::RiuCellSelectionTool( QWidget* parent /*= nullptr */ )
: QDialog( parent )
{
setupUI();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuCellSelectionTool::setupUI()
{
QVBoxLayout* mainLayout = new QVBoxLayout( this );
QButtonGroup* inputTypeGroup = new QButtonGroup( this );
m_xyzRadio = new QRadioButton( "XYZ Coordinates" );
m_ijkRadio = new QRadioButton( "IJK Cell Indices" );
inputTypeGroup->addButton( m_xyzRadio );
inputTypeGroup->addButton( m_ijkRadio );
m_xyzRadio->setChecked( true );
mainLayout->addWidget( m_xyzRadio );
mainLayout->addWidget( m_ijkRadio );
m_coordinateLabel = new QLabel( "Coordinates:" );
m_coordinateEdit = new QLineEdit();
m_coordinateEdit->setPlaceholderText( "Enter coordinates (space or comma separated), two or three coordinates." );
m_coordinateEdit->setToolTip( "Three doubles: Find cell for point. Two doubles, find top cell for EN coordinate." );
m_cellLabel = new QLabel( "IJK:" );
m_cellEdit = new QLineEdit();
m_cellEdit->setPlaceholderText( "Enter IJK (space or comma separated)" );
QGridLayout* labelLayout = new QGridLayout();
labelLayout->addWidget( m_coordinateLabel, 0, 0 );
labelLayout->addWidget( m_coordinateEdit, 0, 1 );
labelLayout->addWidget( m_cellLabel, 1, 0 );
labelLayout->addWidget( m_cellEdit, 1, 1 );
mainLayout->addLayout( labelLayout );
m_submitButton = new QPushButton( "Select Cell" );
mainLayout->addWidget( m_submitButton );
m_appendButton = new QPushButton( "Append Cell" );
mainLayout->addWidget( m_appendButton );
mainLayout->addStretch();
connect( m_submitButton, &QPushButton::clicked, this, &RiuCellSelectionTool::validateAndSelect );
connect( m_appendButton, &QPushButton::clicked, this, &RiuCellSelectionTool::validateAndAppend );
connect( m_xyzRadio, &QRadioButton::toggled, this, &RiuCellSelectionTool::updateVisibleUiItems );
updateVisibleUiItems();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RiuCellSelectionTool::findTopCellIndex( double easting, double northing, const RigMainGrid* mainGrid )
{
if ( !mainGrid ) return cvf::UNDEFINED_SIZE_T;
auto mainGridBoundingBox = mainGrid->boundingBox();
cvf::BoundingBox pointBBox;
pointBBox.add( cvf::Vec3d( easting, northing, mainGridBoundingBox.min().z() ) );
pointBBox.add( cvf::Vec3d( easting, northing, mainGridBoundingBox.max().z() ) );
std::vector<size_t> cellIndices = mainGrid->findIntersectingCells( pointBBox );
double topDepth = mainGridBoundingBox.min().z();
size_t topCellIndex = cvf::UNDEFINED_SIZE_T;
for ( size_t cellIndex : cellIndices )
{
auto cell = mainGrid->cell( cellIndex );
auto center = cell.center();
// The domain coordinates use negative z-values to represent depth
if ( center.z() > topDepth )
{
topDepth = center.z();
topCellIndex = cellIndex;
}
}
return topCellIndex;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RiuCellSelectionTool::findCellForPoint( const cvf::Vec3d& source, double distance, const RigMainGrid* mainGrid )
{
if ( !mainGrid ) return cvf::UNDEFINED_SIZE_T;
std::vector<cvf::Vec3d> points = { source,
source + cvf::Vec3d( 0.0, 0.0, distance ),
source + cvf::Vec3d( distance, 0.0, 0.0 ),
source + cvf::Vec3d( -distance, 0.0, 0.0 ),
source + cvf::Vec3d( 0.0, distance, 0.0 ),
source + cvf::Vec3d( 0.0, -distance, 0.0 ),
source + cvf::Vec3d( 0.0, 0.0, -distance ) };
// Check if the point is inside a cell
for ( const auto& p : points )
{
auto candidate = mainGrid->findReservoirCellIndexFromPoint( p );
if ( candidate != cvf::UNDEFINED_SIZE_T ) return candidate;
}
// If the point is not inside a cell, check if it is close to a cell
cvf::BoundingBox pointBBox;
pointBBox.add( source );
std::vector<size_t> cellIndices = mainGrid->findIntersectingCells( pointBBox );
if ( !cellIndices.empty() )
{
return cellIndices.front();
}
return cvf::UNDEFINED_SIZE_T;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuCellSelectionTool::validateAndSelect()
{
if ( auto activeView = RiaApplication::instance()->activeReservoirView() )
{
if ( auto selItem = createSelectionItemFromInput() )
{
Riu3dSelectionManager::instance()->setSelectedItem( selItem );
activeView->updateDisplayModelForCurrentTimeStepAndRedraw();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuCellSelectionTool::validateAndAppend()
{
if ( auto activeView = RiaApplication::instance()->activeReservoirView() )
{
if ( auto selItem = createSelectionItemFromInput() )
{
Riu3dSelectionManager::instance()->appendItemToSelection( selItem );
activeView->updateDisplayModelForCurrentTimeStepAndRedraw();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QVector<double> RiuCellSelectionTool::parseDoubleValues( const QString& input )
{
// Flexible regex to extract three double numbers with any surrounding text
QRegularExpression coordRegex( R"([-+]?\d+\.?\d*)" );
QRegularExpressionMatchIterator it = coordRegex.globalMatch( input );
QVector<double> coords;
while ( it.hasNext() && coords.size() < 3 )
{
QRegularExpressionMatch match = it.next();
coords.append( match.captured().toDouble() );
}
return coords;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuEclipseSelectionItem* RiuCellSelectionTool::createSelectionItemFromInput()
{
auto eclipseView = dynamic_cast<RimEclipseView*>( RiaApplication::instance()->activeReservoirView() );
if ( !eclipseView || !eclipseView->mainGrid() )
{
return nullptr;
}
size_t cellIndex = cvf::UNDEFINED_SIZE_T;
auto mainGrid = eclipseView->mainGrid();
if ( m_xyzRadio->isChecked() )
{
QVector<double> coords = parseDoubleValues( m_coordinateEdit->text().trimmed() );
if ( coords.size() < 2 )
{
return nullptr;
}
m_cellEdit->clear();
double iSize, jSize, kSize;
mainGrid->characteristicCellSizes( &iSize, &jSize, &kSize );
auto distance = std::min( { iSize, jSize, kSize } ) * 0.4;
if ( coords.size() == 3 )
{
cellIndex = findCellForPoint( cvf::Vec3d( coords[0], coords[1], -coords[2] ), distance, mainGrid );
}
if ( cellIndex == cvf::UNDEFINED_SIZE_T )
{
cellIndex = findTopCellIndex( coords[0], coords[1], mainGrid );
}
if ( cellIndex == cvf::UNDEFINED_SIZE_T )
{
QString coordStr = coords.size() == 3 ? QString( "E:%1 N:%2 D:%3" ).arg( coords[0] ).arg( coords[1] ).arg( coords[2] )
: QString( "E:%1 N:%2" ).arg( coords[0] ).arg( coords[1] );
QMessageBox::information( this, "Cell Selection", "No cell found for : " + coordStr );
return nullptr;
}
if ( auto ijk = mainGrid->ijkFromCellIndex( cellIndex ) )
{
// 1-based for user input
m_cellEdit->setText( QString( "%1 %2 %3" ).arg( ijk->i() + 1 ).arg( ijk->j() + 1 ).arg( ijk->k() + 1 ) );
}
}
else
{
QVector<double> ijkValues = parseDoubleValues( m_cellEdit->text().trimmed() );
if ( ijkValues.size() != 3 )
{
return nullptr;
}
// 0-based for internal use
int i = ijkValues[0] - 1;
int j = ijkValues[1] - 1;
int k = ijkValues[2] - 1;
if ( mainGrid->isCellValid( i, j, k ) )
{
cellIndex = mainGrid->cellIndexFromIJK( i, j, k );
}
}
if ( cellIndex != cvf::UNDEFINED_SIZE_T )
{
size_t gridIndex = 0;
return new RiuEclipseSelectionItem( eclipseView, gridIndex, cellIndex );
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuCellSelectionTool::updateVisibleUiItems()
{
bool isXYZ = m_xyzRadio->isChecked();
m_coordinateEdit->setVisible( isXYZ );
m_coordinateLabel->setVisible( isXYZ );
m_cellEdit->setReadOnly( isXYZ );
}

View File

@@ -0,0 +1,74 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 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 "cvfVector3.h"
#include <QDialog>
class QRadioButton;
class QLineEdit;
class QPushButton;
class QLabel;
namespace caf
{
class VecIjk;
}
class RiuEclipseSelectionItem;
class RigMainGrid;
//==================================================================================================
//
//
//
//==================================================================================================
class RiuCellSelectionTool : public QDialog
{
Q_OBJECT
public:
RiuCellSelectionTool( QWidget* parent = nullptr );
private slots:
void validateAndSelect();
void validateAndAppend();
private:
static QVector<double> parseDoubleValues( const QString& input );
RiuEclipseSelectionItem* createSelectionItemFromInput();
void updateVisibleUiItems();
void setupUI();
static size_t findTopCellIndex( double easting, double northing, const RigMainGrid* mainGrid );
static size_t findCellForPoint( const cvf::Vec3d& source, double distance, const RigMainGrid* mainGrid );
private:
QRadioButton* m_xyzRadio;
QRadioButton* m_ijkRadio;
QLabel* m_coordinateLabel;
QLineEdit* m_coordinateEdit;
QLabel* m_cellLabel;
QLineEdit* m_cellEdit;
QPushButton* m_submitButton;
QPushButton* m_appendButton;
};

View File

@@ -147,6 +147,14 @@ QString RiuDockWidgetTools::mainWindowQuickAccessName()
return "dockQuickAccess_mainWindow";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiuDockWidgetTools::mainWindowCellSelectionToolName()
{
return "dockCellSelectionTool_mainWindow";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@@ -59,6 +59,7 @@ public:
static QString mainWindowMohrsCirclePlotName();
static QString mainWindowUndoStackName();
static QString mainWindowQuickAccessName();
static QString mainWindowCellSelectionToolName();
static QString mainWindowProjectTreeName();
static QString mainWindowDataSourceTreeName();

View File

@@ -50,6 +50,7 @@
#include "RimViewLinkerCollection.h"
#include "RimViewWindow.h"
#include "RiuCellSelectionTool.h"
#include "RiuDepthQwtPlot.h"
#include "RiuDockWidgetTools.h"
#include "RiuMdiArea.h"
@@ -872,7 +873,6 @@ void RiuMainWindow::createDockPanels()
dockManager()->addDockWidgetTabToArea( dockWidget, bottomArea );
}
// result info
{
auto dockWidget = RiuDockWidgetTools::createDockWidget( "Result Info", RiuDockWidgetTools::mainWindowResultInfoName(), dockManager() );
@@ -881,8 +881,16 @@ void RiuMainWindow::createDockPanels()
dockManager()->addDockWidget( ads::DockWidgetArea::LeftDockWidgetArea, dockWidget, bottomArea );
}
{
auto dockWidget =
RiuDockWidgetTools::createDockWidget( "Cell Selection Tool", RiuDockWidgetTools::mainWindowCellSelectionToolName(), dockManager() );
m_cellSelectionTool = new RiuCellSelectionTool( dockWidget );
dockWidget->setWidget( m_cellSelectionTool );
dockManager()->addDockWidget( ads::DockWidgetArea::LeftDockWidgetArea, dockWidget, bottomArea );
}
ads::CDockAreaWidget* procAndMsgTabs = nullptr;
// process monitor
{
auto dockWidget =
RiuDockWidgetTools::createDockWidget( "Process Monitor", RiuDockWidgetTools::mainWindowProcessMonitorName(), dockManager() );

View File

@@ -55,6 +55,7 @@ class RiuPvtPlotPanel;
class RiuMohrsCirclePlot;
class RiuMdiArea;
class RiuSeismicHistogramPanel;
class RiuCellSelectionTool;
class RicGridCalculatorDialog;
@@ -182,12 +183,14 @@ private:
RiuProcessMonitor* m_processMonitor;
QPointer<RiuMessagePanel> m_messagePanel;
RiuResultQwtPlot* m_resultQwtPlot;
RiuDepthQwtPlot* m_depthQwtPlot;
RiuMohrsCirclePlot* m_mohrsCirclePlot;
RiuRelativePermeabilityPlotPanel* m_relPermPlotPanel;
RiuSeismicHistogramPanel* m_seismicHistogramPanel;
RiuPvtPlotPanel* m_pvtPlotPanel;
RiuResultQwtPlot* m_resultQwtPlot;
RiuDepthQwtPlot* m_depthQwtPlot;
RiuMohrsCirclePlot* m_mohrsCirclePlot;
RiuRelativePermeabilityPlotPanel* m_relPermPlotPanel;
RiuSeismicHistogramPanel* m_seismicHistogramPanel;
RiuPvtPlotPanel* m_pvtPlotPanel;
RiuCellSelectionTool* m_cellSelectionTool;
std::unique_ptr<RicGridCalculatorDialog> m_gridCalculatorDialog;
QLabel* m_memoryCriticalWarning;