diff --git a/ApplicationLibCode/Application/RiaCompletionTypeCalculationScheduler.cpp b/ApplicationLibCode/Application/RiaCompletionTypeCalculationScheduler.cpp index 1cc4c0b9ed..3730f07273 100644 --- a/ApplicationLibCode/Application/RiaCompletionTypeCalculationScheduler.cpp +++ b/ApplicationLibCode/Application/RiaCompletionTypeCalculationScheduler.cpp @@ -114,12 +114,6 @@ void RiaCompletionTypeCalculationScheduler::slotRecalculateCompletionType() Rim3dView* activeView = RiaApplication::instance()->activeReservoirView(); - QModelIndex mi; - if ( RiuMainWindow::instance() ) - { - mi = RiuMainWindow::instance()->projectTreeView()->treeView()->currentIndex(); - } - for ( RimEclipseCase* eclipseCase : uniqueCases ) { if ( eclipseCase ) @@ -148,11 +142,6 @@ void RiaCompletionTypeCalculationScheduler::slotRecalculateCompletionType() RiuMainWindow::instance()->setActiveViewer( activeView->viewer()->layoutWidget() ); } } - - if ( mi.isValid() && RiuMainWindow::instance() ) - { - RiuMainWindow::instance()->projectTreeView()->treeView()->setCurrentIndex( mi ); - } } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Commands/WellPathCommands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/WellPathCommands/CMakeLists_files.cmake index cb8c3b62c6..c79c3c167f 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/WellPathCommands/CMakeLists_files.cmake @@ -7,6 +7,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicNewWellPathListTargetFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicNewWellPathAttributeFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicDeleteWellPathTargetFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicDeleteWellPathAttributeFeature.h +${CMAKE_CURRENT_LIST_DIR}/RicDeleteWellPathFeature.h ${CMAKE_CURRENT_LIST_DIR}/RicWellPathsUnitSystemSettingsImpl.h ${CMAKE_CURRENT_LIST_DIR}/RicWellPathsUnitSystemSettingsUi.h ${CMAKE_CURRENT_LIST_DIR}/RicWellPathPickEventHandler.h @@ -40,6 +41,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicNewWellPathListTargetFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicNewWellPathAttributeFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicDeleteWellPathTargetFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicDeleteWellPathAttributeFeature.cpp +${CMAKE_CURRENT_LIST_DIR}/RicDeleteWellPathFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicWellPathsUnitSystemSettingsImpl.cpp ${CMAKE_CURRENT_LIST_DIR}/RicWellPathsUnitSystemSettingsUi.cpp ${CMAKE_CURRENT_LIST_DIR}/RicWellPathPickEventHandler.cpp diff --git a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.cpp b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.cpp index a3a667a18e..e7f9325ba8 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.cpp @@ -79,6 +79,14 @@ void RicPointTangentManipulator::setHandleSize( double handleSize ) m_partManager->setHandleSize( handleSize ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicPointTangentManipulator::setPolyline( const std::vector& polyline ) +{ + m_partManager->setPolyline( polyline ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -94,7 +102,7 @@ bool RicPointTangentManipulator::eventFilter( QObject* obj, QEvent* inputEvent ) { if ( inputEvent->type() == QEvent::MouseButtonPress ) { - QMouseEvent* mouseEvent = static_cast( inputEvent ); + auto* mouseEvent = static_cast( inputEvent ); if ( mouseEvent->button() == Qt::LeftButton ) { @@ -119,7 +127,7 @@ bool RicPointTangentManipulator::eventFilter( QObject* obj, QEvent* inputEvent ) { if ( m_partManager->isManipulatorActive() ) { - QMouseEvent* mouseEvent = static_cast( inputEvent ); + auto* mouseEvent = static_cast( inputEvent ); cvf::ref rayIs = m_viewer->rayIntersectSpecFromWindowCoordinates( mouseEvent->pos().x(), diff --git a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.h b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.h index 4a23514af2..e7b2dd60b6 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.h +++ b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulator.h @@ -55,6 +55,7 @@ public: void setOrigin( const cvf::Vec3d& origin ); void setTangent( const cvf::Vec3d& tangent ); void setHandleSize( double handleSize ); + void setPolyline( const std::vector& polyline ); void appendPartsToModel( cvf::ModelBasicList* model ); diff --git a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.cpp b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.cpp index 732d30d8b3..a9fdaf08d4 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.cpp @@ -15,6 +15,7 @@ // for more details. // ///////////////////////////////////////////////////////////////////////////////// + #include "RicPointTangentManipulatorPartMgr.h" #include "RivPartPriority.h" @@ -36,17 +37,16 @@ #include "cvfPrimitiveSetIndexedUInt.h" #include "cvfRay.h" +#include "cvfGeometryTools.h" #include "cvfMath.h" -#include - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RicPointTangentManipulatorPartMgr::RicPointTangentManipulatorPartMgr() : m_tangentOnStartManipulation( cvf::Vec3d::UNDEFINED ) , m_originOnStartManipulation( cvf::Vec3d::UNDEFINED ) - , m_activeHandle( NONE ) + , m_activeHandle( HandleType::NONE ) , m_handleSize( 1.0 ) , m_isGeometryUpdateNeeded( true ) { @@ -104,12 +104,20 @@ void RicPointTangentManipulatorPartMgr::originAndTangent( cvf::Vec3d* origin, cv *tangent = m_tangent; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicPointTangentManipulatorPartMgr::setPolyline( const std::vector& polyline ) +{ + m_polyline = polyline; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RicPointTangentManipulatorPartMgr::isManipulatorActive() const { - return m_activeHandle != NONE; + return m_activeHandle != HandleType::NONE; } //-------------------------------------------------------------------------------------------------- @@ -172,7 +180,37 @@ void RicPointTangentManipulatorPartMgr::updateManipulatorFromRay( const cvf::Ray { if ( !isManipulatorActive() ) return; - if ( m_activeHandle == HORIZONTAL_PLANE ) + if ( m_activeHandle == HandleType::PRESCRIBED_POLYLINE ) + { + cvf::Plane plane; + plane.setFromPointAndNormal( m_origin, newMouseRay->direction() ); + cvf::Vec3d newIntersection; + newMouseRay->planeIntersect( plane, &newIntersection ); + + const cvf::Vec3d newOrigin = m_originOnStartManipulation + ( newIntersection - m_initialPickPoint ); + + double closestDistance = std::numeric_limits::max(); + cvf::Vec3d closestPoint; + + for ( size_t i = 1; i < m_polyline.size(); i++ ) + { + const auto& p1 = m_polyline[i]; + const auto& p2 = m_polyline[i - 1]; + + double normalizedIntersection; + const auto pointOnLine = cvf::GeometryTools::projectPointOnLine( p1, p2, newOrigin, &normalizedIntersection ); + + const double candidateDistance = pointOnLine.pointDistanceSquared( newOrigin ); + if ( candidateDistance < closestDistance ) + { + closestDistance = candidateDistance; + closestPoint = pointOnLine; + } + } + + m_origin = closestPoint; + } + else if ( m_activeHandle == HandleType::HORIZONTAL_PLANE ) { cvf::Plane plane; plane.setFromPointAndNormal( m_origin, cvf::Vec3d::Z_AXIS ); @@ -183,7 +221,7 @@ void RicPointTangentManipulatorPartMgr::updateManipulatorFromRay( const cvf::Ray m_origin = newOrigin; } - else if ( m_activeHandle == VERTICAL_AXIS ) + else if ( m_activeHandle == HandleType::VERTICAL_AXIS ) { cvf::Plane plane; cvf::Vec3d planeNormal = ( newMouseRay->direction() ^ cvf::Vec3d::Z_AXIS ) ^ cvf::Vec3d::Z_AXIS; @@ -201,7 +239,6 @@ void RicPointTangentManipulatorPartMgr::updateManipulatorFromRay( const cvf::Ray m_origin = newOrigin; } - // m_tangent = newTangent; m_isGeometryUpdateNeeded = true; } @@ -211,7 +248,7 @@ void RicPointTangentManipulatorPartMgr::updateManipulatorFromRay( const cvf::Ray //-------------------------------------------------------------------------------------------------- void RicPointTangentManipulatorPartMgr::endManipulator() { - m_activeHandle = NONE; + m_activeHandle = HandleType::NONE; } //-------------------------------------------------------------------------------------------------- @@ -219,8 +256,15 @@ void RicPointTangentManipulatorPartMgr::endManipulator() //-------------------------------------------------------------------------------------------------- void RicPointTangentManipulatorPartMgr::recreateAllGeometryAndParts() { - createHorizontalPlaneHandle(); - createVerticalAxisHandle(); + if ( m_polyline.empty() ) + { + createHorizontalPlaneHandle(); + createVerticalAxisHandle(); + } + else + { + createPolylineHandle(); + } } //-------------------------------------------------------------------------------------------------- @@ -228,8 +272,15 @@ void RicPointTangentManipulatorPartMgr::recreateAllGeometryAndParts() //-------------------------------------------------------------------------------------------------- void RicPointTangentManipulatorPartMgr::createGeometryOnly() { - m_handleParts[HORIZONTAL_PLANE]->setDrawable( createHorizontalPlaneGeo().p() ); - m_handleParts[VERTICAL_AXIS]->setDrawable( createVerticalAxisGeo().p() ); + if ( m_polyline.empty() ) + { + m_handleParts[HandleType::HORIZONTAL_PLANE]->setDrawable( createHorizontalPlaneGeo().p() ); + m_handleParts[HandleType::VERTICAL_AXIS]->setDrawable( createVerticalAxisGeo().p() ); + } + else + { + m_handleParts[HandleType::PRESCRIBED_POLYLINE]->setDrawable( createPolylineGeo().p() ); + } } //-------------------------------------------------------------------------------------------------- @@ -241,7 +292,7 @@ void RicPointTangentManipulatorPartMgr::createHorizontalPlaneHandle() ref geo = createHorizontalPlaneGeo(); - HandleType handleId = HORIZONTAL_PLANE; + HandleType handleId = HandleType::HORIZONTAL_PLANE; cvf::Color4f color = cvf::Color4f( 1.0f, 0.0f, 1.0f, 0.7f ); cvf::String partName( "PointTangentManipulator Horizontal Plane Handle" ); @@ -318,7 +369,7 @@ void RicPointTangentManipulatorPartMgr::createVerticalAxisHandle() using namespace cvf; cvf::ref geo = createVerticalAxisGeo(); - HandleType handleId = VERTICAL_AXIS; + HandleType handleId = HandleType::VERTICAL_AXIS; cvf::Color4f color = cvf::Color4f( 0.0f, 0.7f, 0.8f, 0.7f ); cvf::String partName( "PointTangentManipulator Vertical Axis Handle" ); @@ -362,6 +413,42 @@ cvf::ref RicPointTangentManipulatorPartMgr::createVerticalAxis return createIndexedTriangelDrawableGeo( vertexArray.p(), indexArray.p() ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicPointTangentManipulatorPartMgr::createPolylineHandle() +{ + cvf::ref geo = createPolylineGeo(); + + HandleType handleId = HandleType::PRESCRIBED_POLYLINE; + cvf::Color4f color = cvf::Color4f( 0.8f, 0.7f, 0.8f, 0.7f ); + cvf::String partName( "PointTangentManipulator Polyline Handle" ); + + addHandlePart( geo.p(), color, handleId, partName ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +cvf::ref RicPointTangentManipulatorPartMgr::createPolylineGeo() +{ + using namespace cvf; + + cvf::ref geomBuilder = new cvf::GeometryBuilderTriangles; + + double radius = m_handleSize * 0.3; + cvf::GeometryUtils::createSphere( radius, 10, 10, geomBuilder.p() ); + + Vec3f origin( m_origin ); + + geomBuilder->transformVertexRange( 0, geomBuilder->vertexCount() - 1, cvf::Mat4f::fromTranslation( origin ) ); + + cvf::ref vertexArray = geomBuilder->vertices(); + cvf::ref indexArray = geomBuilder->triangles(); + + return createIndexedTriangelDrawableGeo( vertexArray.p(), indexArray.p() ); +} + #if 0 //-------------------------------------------------------------------------------------------------- /// diff --git a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.h b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.h index d339749496..96379d9ddb 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.h +++ b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicPointTangentManipulatorPartMgr.h @@ -38,18 +38,19 @@ class String; template class Array; -typedef Array Vec3fArray; -typedef Array UIntArray; +using Vec3fArray = Array; +using UIntArray = Array; } // namespace cvf class RicPointTangentManipulatorPartMgr : public cvf::Object { public: - enum HandleType + enum class HandleType { HORIZONTAL_PLANE, VERTICAL_AXIS, + PRESCRIBED_POLYLINE, AZIMUTH, INCLINATION, NONE @@ -63,6 +64,7 @@ public: void setTangent( const cvf::Vec3d& tangent ); void setHandleSize( double handleSize ); void originAndTangent( cvf::Vec3d* origin, cvf::Vec3d* tangent ); + void setPolyline( const std::vector& polyline ); bool isManipulatorActive() const; void tryToActivateManipulator( const cvf::HitItem* hitItem ); @@ -81,6 +83,9 @@ private: void createVerticalAxisHandle(); cvf::ref createVerticalAxisGeo(); + void createPolylineHandle(); + cvf::ref createPolylineGeo(); + void addHandlePart( cvf::DrawableGeo* geo, const cvf::Color4f& color, HandleType handleId, const cvf::String& partName ); void addActiveModePart( cvf::DrawableGeo* geo, const cvf::Color4f& color, HandleType handleId, const cvf::String& partName ); @@ -99,6 +104,8 @@ private: double m_handleSize; bool m_isGeometryUpdateNeeded; + std::vector m_polyline; + HandleType m_activeHandle; cvf::Vec3d m_initialPickPoint; cvf::Vec3d m_tangentOnStartManipulation; diff --git a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.cpp b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.cpp index 972b9997aa..3580a1e1c0 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.cpp @@ -20,12 +20,15 @@ #include "RicPointTangentManipulator.h" +#include "RigWellPath.h" + #include "Rim3dView.h" #include "RimCase.h" #include "RimModeledWellPath.h" #include "RimWellPathGeometryDef.h" #include "RimWellPathGeometryDefTools.h" #include "RimWellPathTarget.h" +#include "RimWellPathTieIn.h" #include "RiuViewer.h" @@ -50,7 +53,7 @@ RicWellTarget3dEditor::RicWellTarget3dEditor() //-------------------------------------------------------------------------------------------------- RicWellTarget3dEditor::~RicWellTarget3dEditor() { - RiuViewer* ownerRiuViewer = dynamic_cast( ownerViewer() ); + auto* ownerRiuViewer = dynamic_cast( ownerViewer() ); if ( m_cvfModel.notNull() && ownerRiuViewer ) { @@ -68,9 +71,9 @@ RicWellTarget3dEditor::~RicWellTarget3dEditor() //-------------------------------------------------------------------------------------------------- void RicWellTarget3dEditor::configureAndUpdateUi( const QString& uiConfigName ) { - RimWellPathTarget* target = dynamic_cast( this->pdmObject() ); - RiuViewer* ownerRiuViewer = dynamic_cast( ownerViewer() ); - Rim3dView* view = mainOrComparisonView(); + auto* target = dynamic_cast( this->pdmObject() ); + auto* ownerRiuViewer = dynamic_cast( ownerViewer() ); + Rim3dView* view = mainOrComparisonView(); if ( !target || !target->isEnabled() || !view ) { @@ -110,6 +113,29 @@ void RicWellTarget3dEditor::configureAndUpdateUi( const QString& uiConfigName ) m_manipulator->setTangent( target->tangent() ); m_manipulator->setHandleSize( handleSize ); + { + RimWellPath* wellPath = nullptr; + target->firstAncestorOrThisOfType( wellPath ); + + if ( wellPath && !wellPath->isTopLevelWellPath() && geomDef->firstActiveTarget() == target ) + { + if ( auto parentWellPath = wellPath->wellPathTieIn()->parentWell() ) + { + auto geo = parentWellPath->wellPathGeometry(); + auto points = geo->wellPathPoints(); + + for ( auto& p : points ) + { + p = dispXf->transformToDisplayCoord( p ); + } + + // For the first target of a lateral, use the coordinates from the parent well as snap-to locations for + // the 3D manipulator sphere + m_manipulator->setPolyline( points ); + } + } + } + m_cvfModel->removeAllParts(); m_manipulator->appendPartsToModel( m_cvfModel.p() ); @@ -129,64 +155,167 @@ void RicWellTarget3dEditor::cleanupBeforeSettingPdmObject() //-------------------------------------------------------------------------------------------------- void RicWellTarget3dEditor::slotUpdated( const cvf::Vec3d& origin, const cvf::Vec3d& tangent ) { - RimWellPathTarget* target = dynamic_cast( this->pdmObject() ); - Rim3dView* view = mainOrComparisonView(); + auto* manipulatedTarget = dynamic_cast( this->pdmObject() ); + Rim3dView* view = mainOrComparisonView(); - if ( !target || !view ) + if ( !manipulatedTarget || !view ) { return; } RimWellPathGeometryDef* geomDef; - target->firstAncestorOrThisOfTypeAsserted( geomDef ); + manipulatedTarget->firstAncestorOrThisOfTypeAsserted( geomDef ); if ( !geomDef ) return; - if ( geomDef->useReferencePointFromTopLevelWell() ) + RimModeledWellPath* modeledWellPath = nullptr; + geomDef->firstAncestorOfType( modeledWellPath ); + + cvf::Vec3d domainCoordXYZ; // domain coordinate of the new location + cvf::Vec3d deltaManipulatorMovement; // delta change relative current location of target + cvf::Vec3d relativePositionXYZ; // position of well target relative to anchor point { - RimModeledWellPath* modeledWellPath = nullptr; - geomDef->firstAncestorOfType( modeledWellPath ); - if ( modeledWellPath ) + cvf::ref dispXf = view->displayCoordTransform(); + domainCoordXYZ = dispXf->transformToDomainCoord( origin ); + + relativePositionXYZ = domainCoordXYZ - geomDef->anchorPointXyz(); + deltaManipulatorMovement = manipulatedTarget->targetPointXYZ() - relativePositionXYZ; + } + + if ( geomDef->activeWellTargets().front() == manipulatedTarget ) + { + // The first well target of a lateral is the tie-in well target + + if ( modeledWellPath && modeledWellPath->wellPathTieIn() && modeledWellPath->wellPathTieIn()->parentWell() ) { - auto topLevelWellPath = dynamic_cast( modeledWellPath->topLevelWellPath() ); - if ( topLevelWellPath ) + auto parentWell = modeledWellPath->wellPathTieIn()->parentWell(); + auto wellPathGeo = parentWell->wellPathGeometry(); + auto closestMD = wellPathGeo->closestMeasuredDepth( domainCoordXYZ ); + + modeledWellPath->wellPathTieIn()->setTieInMeasuredDepth( closestMD ); + modeledWellPath->wellPathTieIn()->updateChildWellGeometry(); + } + + bool modifyAllTargetsOnAllWells = ( ( QApplication::keyboardModifiers() & Qt::ControlModifier ) && + ( QApplication::keyboardModifiers() & Qt::SHIFT ) ); + + if ( modifyAllTargetsOnAllWells ) + { + for ( auto wellLateral : modeledWellPath->wellPathLaterals() ) { - // Manipulate the reference point of top level well path - geomDef = topLevelWellPath->geometryDefinition(); + if ( auto modeledLateral = dynamic_cast( wellLateral ) ) + { + auto activeTargets = modeledLateral->geometryDefinition()->activeWellTargets(); + for ( auto t : activeTargets ) + { + if ( t == activeTargets.front() ) continue; + if ( t == manipulatedTarget ) continue; + + // Does not work very well + // Must update the tie-in MD also + updateTargetWithDeltaChange( t, deltaManipulatorMovement ); + } + } + } + } + + if ( QApplication::keyboardModifiers() & Qt::ControlModifier ) + { + for ( auto target : geomDef->activeWellTargets() ) + { + if ( target == geomDef->activeWellTargets().front() ) continue; + + updateTargetWithDeltaChange( target, deltaManipulatorMovement ); + } + } + + cvf::Vec3d relativePositionXYD = relativePositionXYZ; + relativePositionXYD.z() = -relativePositionXYD.z(); + + manipulatedTarget->updateFrom3DManipulator( relativePositionXYD ); + + return; + } + + if ( modeledWellPath && modeledWellPath->isTopLevelWellPath() ) + { + // Modification of top level well path + + bool modifyReferencePoint = ( ( QApplication::keyboardModifiers() & Qt::ControlModifier ) && + ( QApplication::keyboardModifiers() & Qt::SHIFT ) ); + if ( modifyReferencePoint ) + { + // Find all linked wells and update reference point with delta change + std::vector linkedWellPathGeoDefs; + if ( geomDef->isReferencePointUpdatesLinked() ) + { + linkedWellPathGeoDefs = RimWellPathGeometryDefTools::linkedDefinitions(); + } + else + { + linkedWellPathGeoDefs.push_back( geomDef ); + } + + RimWellPathGeometryDefTools::updateLinkedGeometryDefinitions( linkedWellPathGeoDefs, deltaManipulatorMovement ); + + return; + } + + bool modifyAllTargetOnWell = ( QApplication::keyboardModifiers() & Qt::ControlModifier ); + if ( modifyAllTargetOnWell ) + { + for ( auto t : geomDef->activeWellTargets() ) + { + if ( t == manipulatedTarget ) continue; + + updateTargetWithDeltaChange( t, deltaManipulatorMovement ); + } + } + } + else if ( modeledWellPath && !modeledWellPath->isTopLevelWellPath() ) + { + bool modifyAllTargetsOnAllWells = ( ( QApplication::keyboardModifiers() & Qt::ControlModifier ) && + ( QApplication::keyboardModifiers() & Qt::SHIFT ) ); + if ( modifyAllTargetsOnAllWells ) + { + // Update all well targets on all connected laterals + + for ( auto wellLateral : modeledWellPath->wellPathLaterals() ) + { + if ( auto modeledLateral = dynamic_cast( wellLateral ) ) + { + auto activeTargets = modeledLateral->geometryDefinition()->activeWellTargets(); + for ( auto t : activeTargets ) + { + if ( t == activeTargets.front() ) continue; + if ( t == manipulatedTarget ) continue; + + updateTargetWithDeltaChange( t, deltaManipulatorMovement ); + } + } + } + } + + bool modifyAllTargets = ( QApplication::keyboardModifiers() & Qt::ControlModifier ); + if ( modifyAllTargets ) + { + // Update all well targets on current well path + + for ( auto t : geomDef->activeWellTargets() ) + { + if ( t == geomDef->activeWellTargets().front() ) continue; + if ( t == manipulatedTarget ) continue; + + updateTargetWithDeltaChange( t, deltaManipulatorMovement ); } } } - cvf::ref dispXf = view->displayCoordTransform(); - - auto domainCoordXYZ = dispXf->transformToDomainCoord( origin ); - - // If CTRL is pressed, modify the reference point instead of the well path target - bool modifyReferencePoint = ( QApplication::keyboardModifiers() & Qt::ControlModifier ); - if ( modifyReferencePoint ) + // Modify a single well target { - auto relativePositionXYZ = domainCoordXYZ - geomDef->anchorPointXyz(); - auto delta = target->targetPointXYZ() - relativePositionXYZ; - - // Find all linked wells and update with delta change - - std::vector linkedWellPathGeoDefs; - if ( geomDef->isReferencePointUpdatesLinked() ) - { - linkedWellPathGeoDefs = RimWellPathGeometryDefTools::linkedDefinitions(); - } - else - { - linkedWellPathGeoDefs.push_back( geomDef ); - } - - RimWellPathGeometryDefTools::updateLinkedGeometryDefinitions( linkedWellPathGeoDefs, delta ); - } - else - { - cvf::Vec3d relativePositionXYD = domainCoordXYZ - geomDef->anchorPointXyz(); + cvf::Vec3d relativePositionXYD = relativePositionXYZ; relativePositionXYD.z() = -relativePositionXYD.z(); - target->updateFrom3DManipulator( relativePositionXYD ); + manipulatedTarget->updateFrom3DManipulator( relativePositionXYD ); } } @@ -195,7 +324,7 @@ void RicWellTarget3dEditor::slotUpdated( const cvf::Vec3d& origin, const cvf::Ve //-------------------------------------------------------------------------------------------------- void RicWellTarget3dEditor::slotSelectedIn3D() { - RimWellPathTarget* target = dynamic_cast( this->pdmObject() ); + auto* target = dynamic_cast( this->pdmObject() ); if ( !target ) { return; @@ -209,7 +338,7 @@ void RicWellTarget3dEditor::slotSelectedIn3D() //-------------------------------------------------------------------------------------------------- void RicWellTarget3dEditor::slotDragFinished() { - RimWellPathTarget* target = dynamic_cast( this->pdmObject() ); + auto* target = dynamic_cast( this->pdmObject() ); if ( !target ) { return; @@ -223,7 +352,7 @@ void RicWellTarget3dEditor::slotDragFinished() //-------------------------------------------------------------------------------------------------- void RicWellTarget3dEditor::removeAllFieldEditors() { - if ( RimWellPathTarget* oldTarget = dynamic_cast( this->pdmObject() ) ) + if ( auto* oldTarget = dynamic_cast( this->pdmObject() ) ) { for ( auto field : oldTarget->fieldsFor3dManipulator() ) { @@ -231,3 +360,13 @@ void RicWellTarget3dEditor::removeAllFieldEditors() } } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicWellTarget3dEditor::updateTargetWithDeltaChange( RimWellPathTarget* target, const cvf::Vec3d& delta ) +{ + auto coordXYZ = target->targetPointXYZ() - delta; + target->setPointXYZ( coordXYZ ); + target->updateConnectedEditors(); +} diff --git a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.h b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.h index 7f041829a9..3b46859cd3 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.h +++ b/ApplicationLibCode/Commands/WellPathCommands/PointTangentManipulator/RicWellTarget3dEditor.h @@ -26,6 +26,7 @@ #include class RicPointTangentManipulator; +class RimWellPathTarget; namespace cvf { @@ -54,6 +55,8 @@ private slots: private: void removeAllFieldEditors(); + static void updateTargetWithDeltaChange( RimWellPathTarget* target, const cvf::Vec3d& delta ); + private: QPointer m_manipulator; cvf::ref m_cvfModel; diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLaterals.cpp b/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLaterals.cpp index a208c220b6..2f95de8917 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLaterals.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLaterals.cpp @@ -66,21 +66,27 @@ void RicCreateMultipleWellPathLaterals::onActionTriggered( bool isChecked ) if ( selected ) { - m_ui->setSourceLateral( selected ); + m_ui->setTopLevelWellPath( selected->topLevelWellPath() ); + double startMD = 0.0; double endMD = 0.0; - if ( auto tieIn = selected->wellPathTieIn() ) + + auto sourceLateral = m_ui->sourceLateral(); + if ( sourceLateral ) { - startMD = selected->wellPathTieIn()->tieInMeasuredDepth() + 50.0; - endMD = startMD + 50.0; - - if ( auto parentWell = selected->wellPathTieIn()->parentWell() ) + if ( auto tieIn = sourceLateral->wellPathTieIn() ) { - if ( !parentWell->wellPathGeometry()->measuredDepths().empty() ) - { - double candidate = parentWell->wellPathGeometry()->measuredDepths().back() - 50.0; + startMD = sourceLateral->wellPathTieIn()->tieInMeasuredDepth() + 50.0; + endMD = startMD + 50.0; - if ( candidate > startMD ) endMD = candidate; + if ( auto parentWell = sourceLateral->wellPathTieIn()->parentWell() ) + { + if ( !parentWell->wellPathGeometry()->measuredDepths().empty() ) + { + double candidate = parentWell->wellPathGeometry()->measuredDepths().back() - 50.0; + + if ( candidate > startMD ) endMD = candidate; + } } } } diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.cpp b/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.cpp index cbcf78f434..3e216b7d0b 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.cpp @@ -47,6 +47,9 @@ RicCreateMultipleWellPathLateralsUi::RicCreateMultipleWellPathLateralsUi() { CAF_PDM_InitFieldNoDefault( &m_sourceLateral, "SourceLaterals", "Source Well Path Lateral", "", "", "" ); + CAF_PDM_InitFieldNoDefault( &m_topLevelWellPath, "TopLevelWellPath", "Top Level Well Path", "", "", "" ); + m_topLevelWellPath.uiCapability()->setUiHidden( true ); + CAF_PDM_InitFieldNoDefault( &m_locations, "Locations", "Locations", "", "", "" ); m_locations = new RimMultipleLocations; } @@ -54,9 +57,13 @@ RicCreateMultipleWellPathLateralsUi::RicCreateMultipleWellPathLateralsUi() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicCreateMultipleWellPathLateralsUi::setSourceLateral( RimModeledWellPath* lateral ) +void RicCreateMultipleWellPathLateralsUi::setTopLevelWellPath( RimWellPath* wellPath ) { - m_sourceLateral = lateral; + m_topLevelWellPath = wellPath; + + auto laterals = RimTools::wellPathCollection()->connectedWellPathLaterals( m_topLevelWellPath ); + + if ( !laterals.empty() ) m_sourceLateral = dynamic_cast( laterals.front() ); } //-------------------------------------------------------------------------------------------------- @@ -108,14 +115,15 @@ QList if ( fieldNeedingOptions == &m_sourceLateral ) { - if ( sourceLateral()->wellPathTieIn() && sourceLateral()->wellPathTieIn()->parentWell() ) + if ( m_topLevelWellPath ) { - auto parentWell = sourceLateral()->wellPathTieIn()->parentWell(); - auto laterals = RimTools::wellPathCollection()->connectedWellPathLaterals( parentWell ); + auto laterals = RimTools::wellPathCollection()->connectedWellPathLaterals( m_topLevelWellPath ); for ( auto lateral : laterals ) { - options.push_back( caf::PdmOptionItemInfo( lateral->name(), lateral ) ); + caf::IconProvider iconProvider = lateral->uiIconProvider(); + + options.push_back( caf::PdmOptionItemInfo( lateral->name(), lateral, false, iconProvider ) ); } } } diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.h b/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.h index 0c7af4671b..9378a756ca 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.h +++ b/ApplicationLibCode/Commands/WellPathCommands/RicCreateMultipleWellPathLateralsUi.h @@ -30,6 +30,7 @@ #include class RimModeledWellPath; +class RimWellPath; namespace caf { @@ -46,7 +47,7 @@ class RicCreateMultipleWellPathLateralsUi : public caf::PdmObject public: RicCreateMultipleWellPathLateralsUi(); - void setSourceLateral( RimModeledWellPath* lateral ); + void setTopLevelWellPath( RimWellPath* wellPath ); void setDefaultValues( double start, double end ); RimModeledWellPath* sourceLateral() const; @@ -60,6 +61,7 @@ private: private: caf::PdmPtrField m_sourceLateral; + caf::PdmPtrField m_topLevelWellPath; caf::PdmChildField m_locations; }; diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.cpp b/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.cpp index 558eb41e23..24a383985f 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.cpp @@ -27,6 +27,7 @@ #include "RigHexIntersectionTools.h" #include "RigMainGrid.h" #include "RigWellPath.h" +#include "RigWellPathGeometryTools.h" #include "Rim3dView.h" #include "RimEclipseView.h" @@ -111,8 +112,15 @@ bool RicCreateWellTargetsPickEventHandler::handle3dPickEvent( const Ric3dPickEve targetPointInDomain = wellPathSourceInfo->closestPointOnCenterLine( firstPickItem.faceIdx(), intersectionPointInDomain ); double md = wellPathSourceInfo->measuredDepth( firstPickItem.faceIdx(), intersectionPointInDomain ); - doSetAzimuthAndInclination = calculateAzimuthAndInclinationAtMd( md, wellPathGeometry, &azimuth, &inclination ); - double rkbDiff = wellPathGeometry->rkbDiff(); + + { + const auto [az, inc] = RigWellPathGeometryTools::calculateAzimuthAndInclinationAtMd( md, wellPathGeometry ); + azimuth = az; + inclination = inc; + doSetAzimuthAndInclination = true; + } + + double rkbDiff = wellPathGeometry->rkbDiff(); if ( m_geometryToAddTargetsTo->airGap() == 0.0 && rkbDiff != std::numeric_limits::infinity() ) { m_geometryToAddTargetsTo->setAirGap( rkbDiff ); @@ -190,59 +198,6 @@ bool RicCreateWellTargetsPickEventHandler::handle3dPickEvent( const Ric3dPickEve return false; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool RicCreateWellTargetsPickEventHandler::calculateAzimuthAndInclinationAtMd( double measuredDepth, - gsl::not_null wellPathGeometry, - double* azimuth, - double* inclination ) const -{ - int mdIndex = -1; - auto mdList = wellPathGeometry->measuredDepths(); - - for ( int i = 0; i < (int)mdList.size(); i++ ) - { - if ( mdList[i] > measuredDepth ) - { - mdIndex = i - 1; - break; - } - } - - auto ptList = wellPathGeometry->wellPathPoints(); - if ( mdIndex > 0 && mdIndex < (int)ptList.size() - 2 ) - { - auto v1 = cvf::Vec3d( ptList[mdIndex - 1] ); - auto v2 = cvf::Vec3d( ptList[mdIndex] ); - auto v3 = cvf::Vec3d( ptList[mdIndex + 1] ); - auto v4 = cvf::Vec3d( ptList[mdIndex + 2] ); - - auto v21 = v2 - v1; - auto v32 = v3 - v2; - auto v43 = v4 - v3; - - v21.normalize(); - v32.normalize(); - v43.normalize(); - - auto v13mean = ( v21 + v32 ) / 2; - auto v24mean = ( v32 + v43 ) / 2; - - double weight = ( measuredDepth - mdList[mdIndex] ) / ( mdList[mdIndex + 1] - mdList[mdIndex] ); - auto vTan = v13mean * weight + v24mean * ( 1 - weight ); - - RiaOffshoreSphericalCoords coords( vTan ); - *azimuth = coords.azi(); - *inclination = coords.inc(); - return true; - } - - *azimuth = 0.0; - *inclination = 0.0; - return false; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.h b/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.h index 1f81abd7ce..be0c8acd69 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.h +++ b/ApplicationLibCode/Commands/WellPathCommands/RicCreateWellTargetsPickEventHandler.h @@ -43,11 +43,6 @@ protected: void notifyUnregistered() override; private: - bool calculateAzimuthAndInclinationAtMd( double measuredDepth, - gsl::not_null wellPathGeometry, - double* azimuth, - double* inclination ) const; - static bool isGridSourceObject( const cvf::Object* object ); static cvf::Vec3d findHexElementIntersection( gsl::not_null view, const RiuPickItemInfo& pickItem, diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicDeleteWellPathFeature.cpp b/ApplicationLibCode/Commands/WellPathCommands/RicDeleteWellPathFeature.cpp new file mode 100644 index 0000000000..8adc133bd0 --- /dev/null +++ b/ApplicationLibCode/Commands/WellPathCommands/RicDeleteWellPathFeature.cpp @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RicDeleteWellPathFeature.h" + +#include "RimTools.h" +#include "RimWellPath.h" +#include "RimWellPathCollection.h" + +#include "cafSelectionManager.h" + +#include + +CAF_CMD_SOURCE_INIT( RicDeleteWellPathFeature, "RicDeleteWellPathFeature" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RicDeleteWellPathFeature::isCommandEnabled() +{ + std::vector objects; + caf::SelectionManager::instance()->objectsByType( &objects ); + + return !objects.empty(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicDeleteWellPathFeature::onActionTriggered( bool isChecked ) +{ + std::vector wellPaths; + caf::SelectionManager::instance()->objectsByType( &wellPaths ); + + if ( !wellPaths.empty() ) + { + auto wpc = RimTools::wellPathCollection(); + + for ( auto w : wellPaths ) + { + for ( auto wl : w->allWellPathLaterals() ) + { + wpc->deleteWell( wl ); + } + } + + wpc->rebuildWellPathNodes(); + wpc->scheduleRedrawAffectedViews(); + wpc->updateAllRequiredEditors(); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicDeleteWellPathFeature::setupActionLook( QAction* actionToSetup ) +{ + actionToSetup->setText( "Delete Well Path" ); + actionToSetup->setIcon( QIcon( ":/Erase.svg" ) ); +} diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicDeleteWellPathFeature.h b/ApplicationLibCode/Commands/WellPathCommands/RicDeleteWellPathFeature.h new file mode 100644 index 0000000000..77eb96346b --- /dev/null +++ b/ApplicationLibCode/Commands/WellPathCommands/RicDeleteWellPathFeature.h @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafCmdFeature.h" + +//================================================================================================== +/// +//================================================================================================== +class RicDeleteWellPathFeature : public caf::CmdFeature +{ + CAF_CMD_HEADER_INIT; + +protected: + bool isCommandEnabled() override; + void onActionTriggered( bool isChecked ) override; + void setupActionLook( QAction* actionToSetup ) override; +}; diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.cpp b/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.cpp index 587ea35eff..d956777868 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.cpp @@ -24,6 +24,7 @@ #include "Riu3dSelectionManager.h" #include "cafSelectionManager.h" +#include "cafSelectionManagerTools.h" #include @@ -34,7 +35,7 @@ CAF_CMD_SOURCE_INIT( RicLinkWellPathFeature, "RicLinkWellPathFeature" ); //-------------------------------------------------------------------------------------------------- bool RicLinkWellPathFeature::isCommandEnabled() { - return ( wellPathGeometryDef() != nullptr ); + return ( !wellPaths().empty() ); } //-------------------------------------------------------------------------------------------------- @@ -42,10 +43,14 @@ bool RicLinkWellPathFeature::isCommandEnabled() //-------------------------------------------------------------------------------------------------- void RicLinkWellPathFeature::onActionTriggered( bool isChecked ) { - if ( auto geoDef = wellPathGeometryDef() ) + for ( auto w : wellPaths() ) { - geoDef->enableLinkOfReferencePointUpdates( isChecked ); - geoDef->updateConnectedEditors(); + if ( auto modeledWell = dynamic_cast( w ) ) + { + auto geoDef = modeledWell->geometryDefinition(); + geoDef->enableLinkOfReferencePointUpdates( isChecked ); + geoDef->updateConnectedEditors(); + } } } @@ -58,6 +63,8 @@ void RicLinkWellPathFeature::setupActionLook( QAction* actionToSetup ) actionToSetup->setText( text ); actionToSetup->setCheckable( true ); actionToSetup->setChecked( isCommandChecked() ); + + actionToSetup->setIcon( QIcon( ":/chain.png" ) ); } //-------------------------------------------------------------------------------------------------- @@ -65,27 +72,39 @@ void RicLinkWellPathFeature::setupActionLook( QAction* actionToSetup ) //-------------------------------------------------------------------------------------------------- bool RicLinkWellPathFeature::isCommandChecked() { - if ( auto geoDef = wellPathGeometryDef() ) + if ( !wellPaths().empty() ) { - return geoDef->isReferencePointUpdatesLinked(); + auto firstWell = dynamic_cast( wellPaths().front() ); + if ( auto geoDef = firstWell->geometryDefinition() ) + { + return geoDef->isReferencePointUpdatesLinked(); + } } - return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimWellPathGeometryDef* RicLinkWellPathFeature::wellPathGeometryDef() +std::vector RicLinkWellPathFeature::wellPaths() { + std::vector wellPaths; + auto wellPathSelectionItem = RiuWellPathSelectionItem::wellPathSelectionItem(); if ( wellPathSelectionItem && wellPathSelectionItem->m_wellpath ) { - if ( auto modeledWellPath = dynamic_cast( wellPathSelectionItem->m_wellpath ) ) + if ( auto modeledWellPath = + dynamic_cast( wellPathSelectionItem->m_wellpath->topLevelWellPath() ) ) { - return modeledWellPath->geometryDefinition(); + wellPaths.push_back( modeledWellPath ); } } - return nullptr; + auto selectedWells = caf::selectedObjectsByTypeStrict(); + for ( auto w : selectedWells ) + { + wellPaths.push_back( w->topLevelWellPath() ); + } + + return wellPaths; } diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.h b/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.h index 006f942e84..5f5320aebd 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.h +++ b/ApplicationLibCode/Commands/WellPathCommands/RicLinkWellPathFeature.h @@ -20,7 +20,7 @@ #include "cafCmdFeature.h" -class RimWellPathGeometryDef; +class RimWellPath; //================================================================================================== /// @@ -36,5 +36,5 @@ public: bool isCommandChecked() override; private: - static RimWellPathGeometryDef* wellPathGeometryDef(); + static std::vector wellPaths(); }; diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.cpp b/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.cpp index 41d8dfdb5c..47f270c79c 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.cpp @@ -84,8 +84,8 @@ void RicNewWellPathLateralAtDepthFeature::setupActionLook( QAction* actionToSetu //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RimWellPath* RicNewWellPathLateralAtDepthFeature::createLateralAtMeasuredDepth( RimWellPath* parentWellPath, - double parentWellMD ) +RimModeledWellPath* RicNewWellPathLateralAtDepthFeature::createLateralAtMeasuredDepth( RimWellPath* parentWellPath, + double parentWellMD ) { RimProject* project = RimProject::current(); RimWellPathCollection* wellPathColl = RimTools::wellPathCollection(); diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.h b/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.h index 39709160ad..372ab372f1 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.h +++ b/ApplicationLibCode/Commands/WellPathCommands/RicNewWellPathLateralAtDepthFeature.h @@ -20,6 +20,7 @@ #include "cafCmdFeature.h" +class RimModeledWellPath; class RimWellPath; //================================================================================================== @@ -34,6 +35,6 @@ public: void onActionTriggered( bool isChecked ) override; void setupActionLook( QAction* actionToSetup ) override; - static RimWellPath* createLateralAtMeasuredDepth( RimWellPath* parentWellPath, double parentWellMD ); - static QString updateNameOfParentAndFindNameOfSideStep( RimWellPath* parentWellPath ); + static RimModeledWellPath* createLateralAtMeasuredDepth( RimWellPath* parentWellPath, double parentWellMD ); + static QString updateNameOfParentAndFindNameOfSideStep( RimWellPath* parentWellPath ); }; diff --git a/ApplicationLibCode/Commands/WellPathCommands/RicWellPathPickEventHandler.cpp b/ApplicationLibCode/Commands/WellPathCommands/RicWellPathPickEventHandler.cpp index fdb5c9c551..e14e5b59fe 100644 --- a/ApplicationLibCode/Commands/WellPathCommands/RicWellPathPickEventHandler.cpp +++ b/ApplicationLibCode/Commands/WellPathCommands/RicWellPathPickEventHandler.cpp @@ -31,14 +31,15 @@ #include "RimWellPath.h" #include "RimWellPathAttribute.h" #include "RimWellPathAttributeCollection.h" +#include "RimWellPathGeometryDef.h" #include "RimWellPathValve.h" #include "RiuMainWindow.h" +#include "RivExtrudedCurveIntersectionPartMgr.h" #include "RivObjectSourceInfo.h" #include "RivWellPathSourceInfo.h" -#include "RivExtrudedCurveIntersectionPartMgr.h" #include "cafDisplayCoordTransform.h" #include "cafSelectionManager.h" #include "cvfPart.h" @@ -179,6 +180,10 @@ bool RicWellPathPickEventHandler::handle3dPickEvent( const Ric3dPickEvent& event } } } + else if ( auto geoDef = dynamic_cast( sourceInfo->object() ) ) + { + RiuMainWindow::instance()->selectAsCurrentItem( geoDef ); + } } if ( dynamic_cast( firstPickedPart->sourceInfo() ) ) diff --git a/ApplicationLibCode/ModelVisualization/CMakeLists_files.cmake b/ApplicationLibCode/ModelVisualization/CMakeLists_files.cmake index 17d1b814a1..f1dc43549b 100644 --- a/ApplicationLibCode/ModelVisualization/CMakeLists_files.cmake +++ b/ApplicationLibCode/ModelVisualization/CMakeLists_files.cmake @@ -59,6 +59,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RivWellDiskPartMgr.h ${CMAKE_CURRENT_LIST_DIR}/RivElementVectorResultPartMgr.h ${CMAKE_CURRENT_LIST_DIR}/RivPolylinePartMgr.h ${CMAKE_CURRENT_LIST_DIR}/RivCellFilterPartMgr.h +${CMAKE_CURRENT_LIST_DIR}/RivDrawableSpheres.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -116,6 +117,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RivWellDiskPartMgr.cpp ${CMAKE_CURRENT_LIST_DIR}/RivElementVectorResultPartMgr.cpp ${CMAKE_CURRENT_LIST_DIR}/RivPolylinePartMgr.cpp ${CMAKE_CURRENT_LIST_DIR}/RivCellFilterPartMgr.cpp +${CMAKE_CURRENT_LIST_DIR}/RivDrawableSpheres.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationLibCode/ModelVisualization/RivDrawableSpheres.cpp b/ApplicationLibCode/ModelVisualization/RivDrawableSpheres.cpp new file mode 100644 index 0000000000..d6f9ee31dd --- /dev/null +++ b/ApplicationLibCode/ModelVisualization/RivDrawableSpheres.cpp @@ -0,0 +1,84 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 - 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RivDrawableSpheres.h" + +#include "cvfRay.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RivDrawableSpheres::RivDrawableSpheres() + : cvf::DrawableVectors() + , m_radius( 1.0 ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RivDrawableSpheres::RivDrawableSpheres( cvf::String vectorMatrixUniformName, cvf::String colorUniformName ) + : cvf::DrawableVectors( vectorMatrixUniformName, colorUniformName ) + , m_radius( 1.0 ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// Estimate the intersection of a sphere by the sphere inscribed in a bounding box +//-------------------------------------------------------------------------------------------------- +bool RivDrawableSpheres::rayIntersectCreateDetail( const cvf::Ray& ray, + cvf::Vec3d* intersectionPoint, + cvf::ref* hitDetail ) const +{ + if ( m_centerCoordArray.isNull() ) return false; + + for ( size_t i = 0; i < m_centerCoordArray->size(); i++ ) + { + cvf::BoundingBox bb; + + cvf::Vec3f center = m_centerCoordArray->get( i ); + cvf::Vec3f corner1 = cvf::Vec3f( center.x() + m_radius, center.y() + m_radius, center.z() + m_radius ); + cvf::Vec3f corner2 = cvf::Vec3f( center.x() - m_radius, center.y() - m_radius, center.z() - m_radius ); + + bb.add( corner1 ); + bb.add( corner2 ); + + if ( ray.boxIntersect( bb, intersectionPoint ) ) + { + return true; + } + } + + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivDrawableSpheres::setRadius( float radius ) +{ + m_radius = radius; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RivDrawableSpheres::setCenterCoords( cvf::Vec3fArray* vertexArray ) +{ + m_centerCoordArray = vertexArray; +} diff --git a/ApplicationLibCode/ModelVisualization/RivDrawableSpheres.h b/ApplicationLibCode/ModelVisualization/RivDrawableSpheres.h new file mode 100644 index 0000000000..6b4651d517 --- /dev/null +++ b/ApplicationLibCode/ModelVisualization/RivDrawableSpheres.h @@ -0,0 +1,39 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 - 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cvfDrawableVectors.h" + +class RivDrawableSpheres : public cvf::DrawableVectors +{ +public: + RivDrawableSpheres(); + RivDrawableSpheres( cvf::String vectorMatrixUniformName, cvf::String colorUniformName ); + + bool rayIntersectCreateDetail( const cvf::Ray& ray, + cvf::Vec3d* intersectionPoint, + cvf::ref* hitDetail ) const override; + + void setRadius( float radius ); + void setCenterCoords( cvf::Vec3fArray* vertexArray ); + +private: + cvf::ref m_centerCoordArray; // Coordinates for sphere center + float m_radius; // Sphere radius +}; diff --git a/ApplicationLibCode/ModelVisualization/RivWellPathPartMgr.cpp b/ApplicationLibCode/ModelVisualization/RivWellPathPartMgr.cpp index 0655793a95..166f2d2a64 100644 --- a/ApplicationLibCode/ModelVisualization/RivWellPathPartMgr.cpp +++ b/ApplicationLibCode/ModelVisualization/RivWellPathPartMgr.cpp @@ -53,6 +53,7 @@ #include "RimWellPathValve.h" #include "Riv3dWellLogPlanePartMgr.h" +#include "RivDrawableSpheres.h" #include "RivFishbonesSubsPartMgr.h" #include "RivObjectSourceInfo.h" #include "RivPartPriority.h" @@ -789,14 +790,14 @@ void RivWellPathPartMgr::buildWellPathParts( const caf::DisplayCoordTransform* d colors->add( sphereColor ); } - cvf::ref vectorDrawable; + cvf::ref vectorDrawable; if ( RiaGuiApplication::instance()->useShaders() ) { - vectorDrawable = new cvf::DrawableVectors( "u_transformationMatrix", "u_color" ); + vectorDrawable = new RivDrawableSpheres( "u_transformationMatrix", "u_color" ); } else { - vectorDrawable = new cvf::DrawableVectors(); + vectorDrawable = new RivDrawableSpheres(); } vectorDrawable->setVectors( vertices.p(), vecRes.p() ); @@ -814,12 +815,20 @@ void RivWellPathPartMgr::buildWellPathParts( const caf::DisplayCoordTransform* d cvf::GeometryUtils::createSphere( cellRadius, 15, 15, &builder ); vectorDrawable->setGlyph( builder.trianglesUShort().p(), builder.vertices().p() ); + { + vectorDrawable->setRadius( cellRadius ); + vectorDrawable->setCenterCoords( vertices.p() ); + } + cvf::ref part = new cvf::Part; part->setName( "RivWellPathPartMgr_WellTargetSpheres" ); part->setDrawable( vectorDrawable.p() ); part->setEffect( new cvf::Effect() ); + auto sourceInfo = new RivObjectSourceInfo( geoDef ); + part->setSourceInfo( sourceInfo ); + m_spherePart = part; } } diff --git a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp index 7a74171814..111a584a95 100644 --- a/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimContextCommandBuilder.cpp @@ -372,6 +372,9 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() { menuBuilder << "RicNewEditableWellPathFeature"; menuBuilder << "RicNewWellPathLateralFeature"; + menuBuilder << "RicLinkWellPathFeature"; + + menuBuilder.addSeparator(); menuBuilder << "RicNewWellPathIntersectionFeature"; appendCreateCompletions( menuBuilder ); @@ -399,12 +402,18 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() menuBuilder.subMenuEnd(); menuBuilder.addSeparator(); + menuBuilder << "RicDeleteWellPathFeature"; + menuBuilder.addSeparator(); - if ( dynamic_cast( firstUiItem ) ) + if ( auto modeledWellPath = dynamic_cast( firstUiItem ) ) { menuBuilder << "RicShowWellPlanFeature"; - menuBuilder << "RicCreateMultipleWellPathLaterals"; + + if ( modeledWellPath->isTopLevelWellPath() ) + { + menuBuilder << "RicCreateMultipleWellPathLaterals"; + } } } else if ( dynamic_cast( firstUiItem ) ) @@ -1068,6 +1077,10 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection() menuBuilder << "RicCutReferencesToClipboardFeature"; menuBuilder << "Separator"; + + menuBuilder << "RicDeleteWellPathFeature"; + menuBuilder << "RicLinkWellPathFeature"; + if ( dynamic_cast( firstUiItem ) || dynamic_cast( firstUiItem ) ) { menuBuilder << "RicCreatePlotFromSelectionFeature"; diff --git a/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.cpp b/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.cpp index ab497bdc82..add52a678a 100644 --- a/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.cpp @@ -60,12 +60,6 @@ RimMultipleLocations::RimMultipleLocations() CAF_PDM_InitFieldNoDefault( &m_rangeSpacing, "Spacing", "Spacing", "", "", "" ); m_rangeSpacing.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); - CAF_PDM_InitFieldNoDefault( &m_minimumMD, "MinimumMD", "Minimum MD", "", "", "" ); - m_minimumMD.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); - - CAF_PDM_InitFieldNoDefault( &m_maximumMD, "MaximumMD", "Maximum MD", "", "", "" ); - m_maximumMD.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); - CAF_PDM_InitField( &m_rangeCount, "RangeValveCount", 10, "Number of Items", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_locations, "Locations", "Measured Depths", "", "", "" ); @@ -77,9 +71,6 @@ RimMultipleLocations::RimMultipleLocations() //-------------------------------------------------------------------------------------------------- void RimMultipleLocations::setRange( double minimumMD, double maximumMD ) { - m_minimumMD = minimumMD; - m_maximumMD = maximumMD; - m_rangeStart = minimumMD; m_rangeEnd = maximumMD; } @@ -91,8 +82,6 @@ void RimMultipleLocations::updateRangesAndLocations() { double existingRangeStart = m_rangeStart(); double existingRangeEnd = m_rangeEnd(); - m_rangeStart = std::clamp( m_rangeStart(), minimumMD(), maximumMD() ); - m_rangeEnd = std::clamp( m_rangeEnd(), minimumMD(), maximumMD() ); if ( existingRangeStart != m_rangeStart() || existingRangeEnd != m_rangeEnd() ) { computeRangesAndLocations(); @@ -295,8 +284,6 @@ void RimMultipleLocations::fieldChangedByUi( const caf::PdmFieldHandle* changedF changedField == &m_rangeSpacing ) { recomputeLocations = true; - m_rangeStart = std::clamp( m_rangeStart(), minimumMD(), maximumMD() ); - m_rangeEnd = std::clamp( m_rangeEnd(), minimumMD(), maximumMD() ); } if ( changedField == &m_rangeSpacing ) @@ -337,22 +324,6 @@ double RimMultipleLocations::minimumSpacingMeters() const return 10.0; } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -double RimMultipleLocations::minimumMD() const -{ - return m_rangeStart(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -double RimMultipleLocations::maximumMD() const -{ - return m_rangeEnd(); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.h b/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.h index 6b9b85810b..ad2b7bff91 100644 --- a/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.h +++ b/ApplicationLibCode/ProjectDataModel/RimMultipleLocations.h @@ -64,8 +64,6 @@ protected: private: int rangeCountFromSpacing() const; double minimumSpacingMeters() const; - double minimumMD() const; - double maximumMD() const; static std::vector locationsFromStartSpacingAndCount( double start, double spacing, size_t count ); private: @@ -75,8 +73,5 @@ private: caf::PdmField m_rangeSpacing; caf::PdmField m_rangeCount; - caf::PdmField m_minimumMD; - caf::PdmField m_maximumMD; - caf::PdmField> m_locations; // Given in measured depth }; diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp index 3da249c091..a86ad9dbed 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp @@ -34,6 +34,7 @@ #include "RimWellPathTarget.h" #include "RimWellPathTieIn.h" +#include "RigWellPathGeometryTools.h" #include "cafPdmFieldScriptingCapability.h" #include "cafPdmUiDoubleValueEditor.h" #include "cafPdmUiTreeOrdering.h" @@ -87,7 +88,7 @@ void RimModeledWellPath::createWellPathGeometry() //-------------------------------------------------------------------------------------------------- void RimModeledWellPath::updateWellPathVisualization() { - this->setWellPathGeometry( m_geometryDefinition->createWellPathGeometry().p() ); + createWellPathGeometry(); std::vector refferingCurves; this->objectsWithReferringPtrFieldsOfType( refferingCurves ); @@ -288,7 +289,10 @@ void RimModeledWellPath::updateTieInLocationFromParentWell() cvf::Vec3d relativePointXYZ = lastPointXYZ - referencePointXYZ; auto firstTarget = targets.front(); - firstTarget->setPointXYZ( relativePointXYZ ); + const auto [azimuth, inclination] = + RigWellPathGeometryTools::calculateAzimuthAndInclinationAtMd( tieIn->tieInMeasuredDepth(), + parentWellPath->wellPathGeometry() ); + firstTarget->setAsPointXYZAndTangentTarget( relativePointXYZ, azimuth, inclination ); updateGeometry( true ); } diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp index cf1aa30291..7ee7c3bcda 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp @@ -137,8 +137,6 @@ RimWellPath::RimWellPath() CAF_PDM_InitFieldNoDefault( &m_wellPathTieIn, "WellPathTieIn", "well Path Tie-In", "", "", "" ); m_wellPathTieIn = new RimWellPathTieIn; m_wellPathTieIn->connectWellPaths( nullptr, this, 0.0 ); - - this->setDeletable( true ); } //-------------------------------------------------------------------------------------------------- @@ -1170,6 +1168,31 @@ std::vector RimWellPath::allWellPathLaterals() const return laterals; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimWellPath::wellPathLaterals() const +{ + std::vector laterals; + + std::vector referringObjects; + this->objectsWithReferringPtrFields( referringObjects ); + for ( auto obj : referringObjects ) + { + if ( auto tieIn = dynamic_cast( obj ) ) + { + auto tieInWellPath = tieIn->childWell(); + if ( tieInWellPath == this ) continue; + if ( tieInWellPath ) + { + laterals.push_back( tieInWellPath ); + } + } + } + + return laterals; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.h b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.h index 3ba5b0b45f..7c94e3d6cc 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.h +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.h @@ -163,6 +163,7 @@ public: RimWellPath* topLevelWellPath(); const RimWellPath* topLevelWellPath() const; std::vector allWellPathLaterals() const; + std::vector wellPathLaterals() const; RimWellPathTieIn* wellPathTieIn() const; void connectWellPaths( RimWellPath* childWell, double tieInMeasuredDepth ); diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp index 7cf65884cd..cd1157be7d 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp @@ -583,6 +583,15 @@ void RimWellPathCollection::deleteAllWellPaths() updateAllRequiredEditors(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellPathCollection::deleteWell( RimWellPath* wellPath ) +{ + m_wellPaths.removeChildObject( wellPath ); + delete wellPath; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.h b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.h index 4c3d4b9294..6e4a429594 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.h +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.h @@ -98,6 +98,7 @@ public: void removeWellPath( gsl::not_null wellPath ); void deleteAllWellPaths(); + void deleteWell( RimWellPath* wellPath ); void groupWellPaths( const std::vector& wellPaths ); void rebuildWellPathNodes(); diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.cpp index 6a800647ac..1ea4923911 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.cpp @@ -105,7 +105,7 @@ RimWellPathGeometryDef::RimWellPathGeometryDef() CAF_PDM_InitScriptableField( &m_linkReferencePointUpdates, "LinkReferencePointUpdates", false, - "Link Reference Point Updates", + "Link Reference Point", "", "", "" ); @@ -121,7 +121,7 @@ RimWellPathGeometryDef::RimWellPathGeometryDef() CAF_PDM_InitField( &m_pickPointsEnabled, "m_pickPointsEnabled", false, "", "", "", "" ); caf::PdmUiPushButtonEditor::configureEditorForField( &m_pickPointsEnabled ); - CAF_PDM_InitScriptableField( &m_showSpheres, "ShowSpheres", false, "Spheres", "", "", "" ); + CAF_PDM_InitScriptableField( &m_showSpheres, "ShowSpheres", true, "Spheres", "", "", "" ); CAF_PDM_InitField( &m_sphereColor, "SphereColor", cvf::Color3f( cvf::Color3f::CEETRON ), "Sphere Color", "", "", "" ); CAF_PDM_InitField( &m_sphereRadiusFactor, "SphereRadiusFactor", 0.15, "Sphere Radius Factor", "", "", "" ); } @@ -289,7 +289,17 @@ cvf::ref RimWellPathGeometryDef::createWellPathGeometry() RiaPolyArcLineSampler arcLineSampler( wellPathCalculator.startTangent(), wellPathCalculator.lineArcEndpoints() ); auto [sampledWellPathPoints, sampledMeasuredDepths] = arcLineSampler.sampledPointsAndMDs( 30, false ); wellPathPoints.insert( wellPathPoints.end(), sampledWellPathPoints.begin(), sampledWellPathPoints.end() ); - double startMD = measuredDepths.empty() ? 0.0 : measuredDepths.back(); + + double startMD = 0.0; + if ( !measuredDepths.empty() ) + { + startMD = measuredDepths.back(); + } + else if ( !m_useAutoGeneratedTargetAtSeaLevel ) + { + startMD = m_mdAtFirstTarget; + } + for ( auto md : sampledMeasuredDepths ) { measuredDepths.push_back( md + startMD ); @@ -523,6 +533,23 @@ void RimWellPathGeometryDef::fieldChangedByUi( const caf::PdmFieldHandle* change RimWellPathGeometryDefTools::updateLinkedGeometryDefinitions( linkedDefs, delta ); } } + else if ( changedField == &m_useAutoGeneratedTargetAtSeaLevel && !m_useAutoGeneratedTargetAtSeaLevel ) + { + auto firstTarget = firstActiveTarget(); + if ( firstTarget != nullptr ) + { + auto firstLocationXYZ = firstTarget->targetPointXYZ() + anchorPointXyz(); + + // Temporarily enable target at sea level to be able to create a complete geometry to find MD of first + // target of the complete geometry + m_useAutoGeneratedTargetAtSeaLevel = true; + auto wellPathGeo = createWellPathGeometry(); + m_useAutoGeneratedTargetAtSeaLevel = false; + + double mdAtFirstTarget = wellPathGeo->closestMeasuredDepth( firstLocationXYZ ); + m_mdAtFirstTarget = mdAtFirstTarget; + } + } changed.send( false ); } @@ -759,6 +786,14 @@ void RimWellPathGeometryDef::defineObjectEditorAttribute( QString uiConfigName, } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +caf::PdmFieldHandle* RimWellPathGeometryDef::objectToggleField() +{ + return &m_showSpheres; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.h b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.h index 573a843052..50b2a66802 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.h +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathGeometryDef.h @@ -102,6 +102,7 @@ protected: caf::PdmUiEditorAttribute* attribute ) override; void defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; + caf::PdmFieldHandle* objectToggleField() override; void onTargetMoved( const caf::SignalEmitter* emitter, bool fullUpdate ); diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.cpp index b8f1a1f193..9087b479a4 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.cpp @@ -43,7 +43,7 @@ void caf::AppEnum::setUp() { addItem( RimWellPathTarget::TargetTypeEnum::POINT_AND_TANGENT, "POINT_AND_TANGENT", "Point and Tangent" ); addItem( RimWellPathTarget::TargetTypeEnum::POINT, "POINT", "Point" ); - setDefault( RimWellPathTarget::TargetTypeEnum::POINT_AND_TANGENT ); + setDefault( RimWellPathTarget::TargetTypeEnum::POINT ); } } // namespace caf //-------------------------------------------------------------------------------------------------- @@ -51,7 +51,7 @@ void caf::AppEnum::setUp() //-------------------------------------------------------------------------------------------------- RimWellPathTarget::RimWellPathTarget() : moved( this ) - , m_targetType( TargetTypeEnum::POINT_AND_TANGENT ) + , m_targetType( TargetTypeEnum::POINT ) , m_targetPointXYD( cvf::Vec3d::ZERO ) , m_azimuth( 0.0 ) , m_inclination( 0.0 ) @@ -69,10 +69,13 @@ RimWellPathTarget::RimWellPathTarget() m_isLocked.uiCapability()->setUiHidden( true ); CAF_PDM_InitScriptableFieldNoDefault( &m_targetPointXYD, "TargetPoint", "Relative Coord", "", "", "" ); - CAF_PDM_InitScriptableFieldNoDefault( &m_targetPointForDisplay, "TargetPointForDisplay", "UTM Coord", "", "", "" ); + CAF_PDM_InitFieldNoDefault( &m_targetPointForDisplay, "TargetPointForDisplay", "UTM Coord", "", "", "" ); m_targetPointForDisplay.registerGetMethod( this, &RimWellPathTarget::targetPointForDisplayXYD ); m_targetPointForDisplay.registerSetMethod( this, &RimWellPathTarget::setTargetPointFromDisplayCoord ); + CAF_PDM_InitScriptableFieldNoDefault( &m_targetMeasuredDepth, "TargetMeasuredDepth", "MD", "", "", "" ); + m_targetMeasuredDepth.registerGetMethod( this, &RimWellPathTarget::measuredDepth ); + CAF_PDM_InitScriptableField( &m_dogleg1, "Dogleg1", 3.0, "DL in", "", "[deg/30m]", "" ); CAF_PDM_InitScriptableField( &m_dogleg2, "Dogleg2", 3.0, "DL out", "", "[deg/30m]", "" ); @@ -224,10 +227,8 @@ double RimWellPathTarget::azimuth() const { return cvf::Math::toRadians( m_azimuth ); } - else - { - return std::numeric_limits::infinity(); - } + + return std::numeric_limits::infinity(); } //-------------------------------------------------------------------------------------------------- @@ -239,10 +240,8 @@ double RimWellPathTarget::inclination() const { return cvf::Math::toRadians( m_inclination ); } - else - { - return std::numeric_limits::infinity(); - } + + return std::numeric_limits::infinity(); } //-------------------------------------------------------------------------------------------------- @@ -441,6 +440,28 @@ void RimWellPathTarget::setTargetPointFromDisplayCoord( const cvf::Vec3d& coordI m_targetPointXYD = newCoordInXYD; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RimWellPathTarget::measuredDepth() const +{ + RimWellPath* wellPath = nullptr; + this->firstAncestorOfType( wellPath ); + + auto geoDef = geometryDefinition(); + + if ( geoDef && wellPath && wellPath->wellPathGeometry() ) + { + auto offsetXYZ = geoDef->anchorPointXyz(); + auto coordXYZ = targetPointXYZ() + offsetXYZ; + + auto wellPathGeo = wellPath->wellPathGeometry(); + return wellPathGeo->closestMeasuredDepth( coordXYZ ); + } + + return 0.0; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.h b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.h index e21ea7e5a2..f383e91e73 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.h +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTarget.h @@ -85,6 +85,7 @@ private: cvf::Vec3d targetPointForDisplayXYD() const; void setTargetPointFromDisplayCoord( const cvf::Vec3d& coordInXYZ ); + double measuredDepth() const; RimWellPathGeometryDef* geometryDefinition() const; @@ -96,9 +97,11 @@ private: caf::PdmField> m_targetType; caf::PdmField m_targetPointXYD; caf::PdmProxyValueField m_targetPointForDisplay; - caf::PdmField m_azimuth; - caf::PdmField m_inclination; - caf::PdmField m_dogleg1; - caf::PdmField m_dogleg2; - caf::PdmField m_hasTangentConstraintUiField; + caf::PdmProxyValueField m_targetMeasuredDepth; + + caf::PdmField m_azimuth; + caf::PdmField m_inclination; + caf::PdmField m_dogleg1; + caf::PdmField m_dogleg2; + caf::PdmField m_hasTangentConstraintUiField; }; diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.cpp index b825359d27..cd9352efd3 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.cpp @@ -30,7 +30,8 @@ #include "RiuMainWindow.h" -#include "cafPdmUiDoubleValueEditor.h" +#include "RigWellPathGeometryTools.h" +#include "cafPdmUiDoubleSliderEditor.h" CAF_PDM_SOURCE_INIT( RimWellPathTieIn, "RimWellPathTieIn" ); @@ -44,7 +45,7 @@ RimWellPathTieIn::RimWellPathTieIn() CAF_PDM_InitFieldNoDefault( &m_parentWell, "ParentWellPath", "Parent Well Path", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_childWell, "ChildWellPath", "ChildWellPath", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_tieInMeasuredDepth, "TieInMeasuredDepth", "Tie In Measured Depth", "", "", "" ); - m_tieInMeasuredDepth.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); + m_tieInMeasuredDepth.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() ); CAF_PDM_InitField( &m_addValveAtConnection, "AddValveAtConnection", false, "Add Outlet Valve for Branches", "", "", "" ); @@ -227,3 +228,32 @@ QList RimWellPathTieIn::calculateValueOptions( const caf return options; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellPathTieIn::defineEditorAttribute( const caf::PdmFieldHandle* field, + QString uiConfigName, + caf::PdmUiEditorAttribute* attribute ) +{ + if ( field == &m_tieInMeasuredDepth ) + { + caf::PdmUiDoubleSliderEditorAttribute* myAttr = dynamic_cast( attribute ); + + if ( myAttr && parentWell() ) + { + double minimumValue = 0.0, maximumValue = 0.0; + + auto wellPathGeo = parentWell()->wellPathGeometry(); + + if ( wellPathGeo ) + { + minimumValue = wellPathGeo->measuredDepths().front(); + maximumValue = wellPathGeo->measuredDepths().back(); + + myAttr->m_minimum = minimumValue; + myAttr->m_maximum = maximumValue; + } + } + } +} diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.h b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.h index 3405e94aa2..77ddbf05f1 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.h +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathTieIn.h @@ -53,6 +53,10 @@ private: QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly ) override; + void defineEditorAttribute( const caf::PdmFieldHandle* field, + QString uiConfigName, + caf::PdmUiEditorAttribute* attribute ) override; + private: caf::PdmPtrField m_parentWell; caf::PdmPtrField m_childWell; diff --git a/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake b/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake index a8d59fb3d1..546396ba21 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake +++ b/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake @@ -14,8 +14,10 @@ ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerDouble.h ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerString.h ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerTime.h ${CMAKE_CURRENT_LIST_DIR}/RimcWellLogPlotCollection.h -${CMAKE_CURRENT_LIST_DIR}/RimcWellLogPlot.cpp -${CMAKE_CURRENT_LIST_DIR}/RimcWellLogTrack.cpp +${CMAKE_CURRENT_LIST_DIR}/RimcWellLogPlot.h +${CMAKE_CURRENT_LIST_DIR}/RimcWellLogTrack.h +${CMAKE_CURRENT_LIST_DIR}/RimcWellPathGeometryDef.h +${CMAKE_CURRENT_LIST_DIR}/RimcModeledWellPath.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -35,6 +37,8 @@ ${CMAKE_CURRENT_LIST_DIR}/RimcDataContainerTime.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcWellLogPlotCollection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcWellLogPlot.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcWellLogTrack.cpp +${CMAKE_CURRENT_LIST_DIR}/RimcWellPathGeometryDef.cpp +${CMAKE_CURRENT_LIST_DIR}/RimcModeledWellPath.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcModeledWellPath.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcModeledWellPath.cpp new file mode 100644 index 0000000000..ab73cf2d9b --- /dev/null +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcModeledWellPath.cpp @@ -0,0 +1,133 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimcModeledWellPath.h" + +#include "RimModeledWellPath.h" +#include "RimPerforationCollection.h" +#include "RimPerforationInterval.h" +#include "RimTools.h" +#include "RimWellPathCollection.h" +#include "RimWellPathGeometryDef.h" +#include "RimWellPathTarget.h" + +#include "WellPathCommands/RicNewWellPathLateralAtDepthFeature.h" + +#include "cafPdmAbstractFieldScriptingCapability.h" +#include "cafPdmFieldScriptingCapability.h" + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimModeledWellPath, RimcModeledWellPath_appendLateral, "AppendLateral" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimcModeledWellPath_appendLateral::RimcModeledWellPath_appendLateral( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self ) +{ + CAF_PDM_InitObject( "Append Well Path Lateral", "", "", "Append Well Path Lateral" ); + CAF_PDM_InitScriptableField( &m_tieInDepth, "TieInDepth", 0.0, "", "", "", "Measured Depth on the Parent Well Path" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_lateralName, "LateralName", "", "", "", "Lateral Name" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +caf::PdmObjectHandle* RimcModeledWellPath_appendLateral::execute() +{ + auto parentWellPath = self(); + + auto lateral = RicNewWellPathLateralAtDepthFeature::createLateralAtMeasuredDepth( parentWellPath, m_tieInDepth ); + if ( !m_lateralName().isEmpty() ) + { + lateral->setName( m_lateralName ); + } + lateral->geometryDefinition()->enableTargetPointPicking( false ); + + return lateral; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimcModeledWellPath_appendLateral::resultIsPersistent() const +{ + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr RimcModeledWellPath_appendLateral::defaultResult() const +{ + return std::unique_ptr( new RimModeledWellPath ); +} + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimModeledWellPath, + RimcModeledWellPath_appendPerforationInterval, + "AppendPerforationInterval" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimcModeledWellPath_appendPerforationInterval::RimcModeledWellPath_appendPerforationInterval( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self ) +{ + CAF_PDM_InitObject( "Append Perforation Interval", "", "", "Append Perforation Interval" ); + CAF_PDM_InitScriptableField( &m_startMD, "StartMd", 0.0, "", "", "", "Start Measured Depth" ); + CAF_PDM_InitScriptableField( &m_endMD, "EndMd", 0.0, "", "", "", "End Measured Depth" ); + CAF_PDM_InitScriptableField( &m_diameter, "Diameter", 0.0, "", "", "", "Diameter" ); + CAF_PDM_InitScriptableField( &m_skinFactor, "SkinFactor", 0.0, "", "", "", "Skin Factor" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +caf::PdmObjectHandle* RimcModeledWellPath_appendPerforationInterval::execute() +{ + auto wellPath = self(); + + auto perforationInterval = new RimPerforationInterval; + perforationInterval->setStartAndEndMD( m_startMD, m_endMD ); + perforationInterval->setSkinFactor( m_skinFactor ); + perforationInterval->setDiameter( m_diameter ); + + wellPath->perforationIntervalCollection()->appendPerforation( perforationInterval ); + + auto* wellPathCollection = RimTools::wellPathCollection(); + + wellPathCollection->uiCapability()->updateConnectedEditors(); + wellPathCollection->scheduleRedrawAffectedViews(); + + return perforationInterval; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimcModeledWellPath_appendPerforationInterval::resultIsPersistent() const +{ + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr RimcModeledWellPath_appendPerforationInterval::defaultResult() const +{ + return std::unique_ptr( new RimPerforationInterval ); +} diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcModeledWellPath.h b/ApplicationLibCode/ProjectDataModelCommands/RimcModeledWellPath.h new file mode 100644 index 0000000000..a6725c69dc --- /dev/null +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcModeledWellPath.h @@ -0,0 +1,67 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafPdmField.h" +#include "cafPdmObjectHandle.h" +#include "cafPdmObjectMethod.h" + +#include "cvfVector3.h" + +#include + +//================================================================================================== +/// +//================================================================================================== +class RimcModeledWellPath_appendLateral : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimcModeledWellPath_appendLateral( caf::PdmObjectHandle* self ); + + caf::PdmObjectHandle* execute() override; + bool resultIsPersistent() const override; + std::unique_ptr defaultResult() const override; + +private: + caf::PdmField m_tieInDepth; + caf::PdmField m_lateralName; +}; + +//================================================================================================== +/// +//================================================================================================== +class RimcModeledWellPath_appendPerforationInterval : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimcModeledWellPath_appendPerforationInterval( caf::PdmObjectHandle* self ); + + caf::PdmObjectHandle* execute() override; + bool resultIsPersistent() const override; + std::unique_ptr defaultResult() const override; + +private: + caf::PdmField m_startMD; + caf::PdmField m_endMD; + caf::PdmField m_diameter; + caf::PdmField m_skinFactor; +}; diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathGeometryDef.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathGeometryDef.cpp new file mode 100644 index 0000000000..98db4c9822 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathGeometryDef.cpp @@ -0,0 +1,85 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimcWellPathGeometryDef.h" + +#include "RimModeledWellPath.h" +#include "RimWellPathCollection.h" +#include "RimWellPathGeometryDef.h" +#include "RimWellPathTarget.h" + +#include "cafPdmAbstractFieldScriptingCapability.h" +#include "cafPdmFieldScriptingCapability.h" +#include "cafPdmFieldScriptingCapabilityCvfVec3d.h" + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellPathGeometryDef, + RimcRimWellPathGeometryDef_appendNewWellTarget, + "AppendWellTarget" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimcRimWellPathGeometryDef_appendNewWellTarget::RimcRimWellPathGeometryDef_appendNewWellTarget( caf::PdmObjectHandle* self ) + : caf::PdmObjectMethod( self ) +{ + CAF_PDM_InitObject( "Create and Add New Well Target", "", "", "Create and Add New Well Target" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_coordinate, "Coordinate", "", "", "", "Coordinate" ); + CAF_PDM_InitScriptableField( &m_isAbsolute, "Absolute", false, "", "", "", "Relative or Absolute Coordinate" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +caf::PdmObjectHandle* RimcRimWellPathGeometryDef_appendNewWellTarget::execute() +{ + auto geoDef = self(); + + cvf::Vec3d relativeTargetPoint = m_coordinate(); + relativeTargetPoint.z() = -relativeTargetPoint.z(); + if ( m_isAbsolute ) + { + cvf::Vec3d referencePoint = geoDef->anchorPointXyz(); + relativeTargetPoint -= referencePoint; + } + + auto newTarget = new RimWellPathTarget; + newTarget->setAsPointTargetXYD( + cvf::Vec3d( relativeTargetPoint.x(), relativeTargetPoint.y(), -relativeTargetPoint.z() ) ); + geoDef->insertTarget( nullptr, newTarget ); + + geoDef->updateConnectedEditors(); + geoDef->updateWellPathVisualization( false ); + + return newTarget; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimcRimWellPathGeometryDef_appendNewWellTarget::resultIsPersistent() const +{ + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr RimcRimWellPathGeometryDef_appendNewWellTarget::defaultResult() const +{ + return std::unique_ptr( new RimWellPathTarget ); +} diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathGeometryDef.h b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathGeometryDef.h new file mode 100644 index 0000000000..b22ad820e0 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathGeometryDef.h @@ -0,0 +1,46 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021- 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafPdmField.h" +#include "cafPdmObjectHandle.h" +#include "cafPdmObjectMethod.h" + +#include "cvfVector3.h" + +#include + +//================================================================================================== +/// +//================================================================================================== +class RimcRimWellPathGeometryDef_appendNewWellTarget : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimcRimWellPathGeometryDef_appendNewWellTarget( caf::PdmObjectHandle* self ); + + caf::PdmObjectHandle* execute() override; + bool resultIsPersistent() const override; + std::unique_ptr defaultResult() const override; + +private: + caf::PdmField m_coordinate; + caf::PdmField m_isAbsolute; +}; diff --git a/ApplicationLibCode/ReservoirDataModel/RigWellPath.cpp b/ApplicationLibCode/ReservoirDataModel/RigWellPath.cpp index 783ac6c09e..3819f47366 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWellPath.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigWellPath.cpp @@ -315,41 +315,15 @@ cvf::Vec3d RigWellPath::tangentAlongWellPath( double measuredDepth ) const //-------------------------------------------------------------------------------------------------- double RigWellPath::wellPathAzimuthAngle( const cvf::Vec3d& position ) const { - size_t closestIndex = cvf::UNDEFINED_SIZE_T; - double closestDistance = cvf::UNDEFINED_DOUBLE; - - for ( size_t i = 1; i < m_wellPathPoints.size(); i++ ) - { - cvf::Vec3d p1 = m_wellPathPoints[i - 1]; - cvf::Vec3d p2 = m_wellPathPoints[i - 0]; - - double candidateDistance = cvf::GeometryTools::linePointSquareDist( p1, p2, position ); - if ( candidateDistance < closestDistance ) - { - closestDistance = candidateDistance; - closestIndex = i; - } - } - // For vertical well (x-component of direction = 0) returned angle will be 90. double azimuthAngleDegrees = 90.0; - if ( closestIndex != cvf::UNDEFINED_DOUBLE ) + cvf::Vec3d p1 = cvf::Vec3d::UNDEFINED; + cvf::Vec3d p2 = cvf::Vec3d::UNDEFINED; + twoClosestPoints( position, &p1, &p2 ); + + if ( !p1.isUndefined() ) { - cvf::Vec3d p1; - cvf::Vec3d p2; - - if ( closestIndex > 0 ) - { - p1 = m_wellPathPoints[closestIndex - 1]; - p2 = m_wellPathPoints[closestIndex - 0]; - } - else - { - p1 = m_wellPathPoints[closestIndex + 1]; - p2 = m_wellPathPoints[closestIndex + 0]; - } - cvf::Vec3d direction = p2 - p1; if ( fabs( direction.y() ) > 1e-5 ) @@ -370,34 +344,11 @@ void RigWellPath::twoClosestPoints( const cvf::Vec3d& position, cvf::Vec3d* p1, { CVF_ASSERT( p1 && p2 ); - size_t closestIndex = cvf::UNDEFINED_SIZE_T; - double closestDistance = cvf::UNDEFINED_DOUBLE; - - for ( size_t i = 1; i < m_wellPathPoints.size(); i++ ) + auto closeIndices = closestIndices( position ); + if ( closeIndices.first != cvf::UNDEFINED_SIZE_T ) { - cvf::Vec3d point1 = m_wellPathPoints[i - 1]; - cvf::Vec3d point2 = m_wellPathPoints[i - 0]; - - double candidateDistance = cvf::GeometryTools::linePointSquareDist( point1, point2, position ); - if ( candidateDistance < closestDistance ) - { - closestDistance = candidateDistance; - closestIndex = i; - } - } - - if ( closestIndex != cvf::UNDEFINED_SIZE_T ) - { - if ( closestIndex > 0 ) - { - *p1 = m_wellPathPoints[closestIndex - 1]; - *p2 = m_wellPathPoints[closestIndex - 0]; - } - else - { - *p1 = m_wellPathPoints[closestIndex + 1]; - *p2 = m_wellPathPoints[closestIndex + 0]; - } + *p1 = m_wellPathPoints[closeIndices.first]; + *p2 = m_wellPathPoints[closeIndices.second]; } } @@ -429,6 +380,32 @@ double RigWellPath::identicalTubeLength( const RigWellPath& other ) const return identicalLength; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +double RigWellPath::closestMeasuredDepth( const cvf::Vec3d& position ) const +{ + auto [firstIndex, secondIndex] = closestIndices( position ); + if ( firstIndex != cvf::UNDEFINED_SIZE_T ) + { + cvf::Vec3d p1 = m_wellPathPoints[firstIndex]; + cvf::Vec3d p2 = m_wellPathPoints[secondIndex]; + + double diffP1 = ( p1 - position ).lengthSquared(); + double diffP2 = ( p2 - position ).lengthSquared(); + + double weigth1 = diffP2 / ( diffP1 + diffP2 ); + + double measureDepth1 = m_measuredDepths[firstIndex]; + double measureDepth2 = m_measuredDepths[secondIndex]; + + double interpolatedValue = measureDepth1 * weigth1 + measureDepth2 * ( 1.0 - weigth1 ); + return interpolatedValue; + } + + return -1.0; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -681,3 +658,40 @@ std::vector RigWellPath::clipPolylineStartAboveZ( const std::vector< return clippedPolyLine; } + +//-------------------------------------------------------------------------------------------------- +// Returns the closes indices with smallest index first +// If not found, cvf::UNDEFINED_SIZE_T is returned for both +//-------------------------------------------------------------------------------------------------- +std::pair RigWellPath::closestIndices( const cvf::Vec3d& position ) const +{ + size_t closestIndex = cvf::UNDEFINED_SIZE_T; + double closestDistance = cvf::UNDEFINED_DOUBLE; + + for ( size_t i = 1; i < m_wellPathPoints.size(); i++ ) + { + cvf::Vec3d point1 = m_wellPathPoints[i - 1]; + cvf::Vec3d point2 = m_wellPathPoints[i - 0]; + + double candidateDistance = cvf::GeometryTools::linePointSquareDist( point1, point2, position ); + if ( candidateDistance < closestDistance ) + { + closestDistance = candidateDistance; + closestIndex = i; + } + } + + if ( closestIndex != cvf::UNDEFINED_SIZE_T ) + { + if ( closestIndex > 0 ) + { + return { closestIndex - 1, closestIndex }; + } + else + { + return { closestIndex, closestIndex + 1 }; + } + } + + return { cvf::UNDEFINED_SIZE_T, cvf::UNDEFINED_SIZE_T }; +} diff --git a/ApplicationLibCode/ReservoirDataModel/RigWellPath.h b/ApplicationLibCode/ReservoirDataModel/RigWellPath.h index 86d7df76d1..754e1e0599 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWellPath.h +++ b/ApplicationLibCode/ReservoirDataModel/RigWellPath.h @@ -70,6 +70,7 @@ public: double wellPathAzimuthAngle( const cvf::Vec3d& position ) const; void twoClosestPoints( const cvf::Vec3d& position, cvf::Vec3d* p1, cvf::Vec3d* p2 ) const; double identicalTubeLength( const RigWellPath& otherWellPathGeometry ) const; + double closestMeasuredDepth( const cvf::Vec3d& position ) const; static cvf::ref commonGeometry( const std::vector& allGeometries ); void setUniqueStartAndEndIndex( size_t uniqueStartIndex, size_t uniqueEndIndex ); @@ -90,6 +91,9 @@ public: double* horizontalLengthAlongWellToClipPoint, size_t* indexToFirstVisibleSegment ); +private: + std::pair closestIndices( const cvf::Vec3d& position ) const; + private: std::vector m_wellPathPoints; std::vector m_measuredDepths; diff --git a/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryExporter.cpp b/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryExporter.cpp index e9788c64ca..83f0edfe3e 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryExporter.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryExporter.cpp @@ -53,10 +53,6 @@ void RigWellPathGeometryExporter::exportWellPathGeometry( gsl::not_nullgeometryDefinition()->airGap(); } - else - { - rkbOffset = modeledWellPath->geometryDefinition()->mdAtFirstTarget(); - } } } exportWellPathGeometry( *wellPathGeom, mdStepSize, rkbOffset, xValues, yValues, tvdValues, mdValues ); diff --git a/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.cpp b/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.cpp index 141c614b84..e849e2b0c4 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.cpp @@ -23,6 +23,7 @@ #include "cvfMath.h" #include "cvfMatrix3.h" +#include "RiaOffshoreSphericalCoords.h" #include #include @@ -133,6 +134,61 @@ std::vector RigWellPathGeometryTools::interpolateMdFromTvd( const std::v return interpolatedMdValues; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::pair + RigWellPathGeometryTools::calculateAzimuthAndInclinationAtMd( double measuredDepth, + gsl::not_null wellPathGeometry ) +{ + int mdIndex = -1; + auto mdList = wellPathGeometry->measuredDepths(); + + for ( int i = 0; i < (int)mdList.size(); i++ ) + { + if ( mdList[i] > measuredDepth ) + { + mdIndex = i - 1; + break; + } + } + + auto ptList = wellPathGeometry->wellPathPoints(); + if ( mdIndex >= 0 && mdIndex < (int)ptList.size() - 1 ) + { + const auto& v2 = cvf::Vec3d( ptList[mdIndex] ); + const auto& v3 = cvf::Vec3d( ptList[mdIndex + 1] ); + + auto v32 = ( v3 - v2 ).getNormalized(); + + auto v13mean = v32; + + if ( mdIndex > 0 ) + { + const auto& v1 = cvf::Vec3d( ptList[mdIndex - 1] ); + auto v21 = ( v2 - v1 ).getNormalized(); + v13mean = ( v21 + v32 ) / 2; + } + + auto v24mean = v32; + if ( mdIndex < (int)ptList.size() - 2 ) + { + const auto& v4 = cvf::Vec3d( ptList[mdIndex + 2] ); + auto v43 = ( v4 - v3 ).getNormalized(); + v24mean = ( v32 + v43 ) / 2; + } + + double weight = ( measuredDepth - mdList[mdIndex] ) / ( mdList[mdIndex + 1] - mdList[mdIndex] ); + auto vTan = v13mean * ( 1.0 - weight ) + v24mean * ( weight ); + + RiaOffshoreSphericalCoords coords( vTan ); + + return { coords.azi(), coords.inc() }; + } + + return { 0.0, 0.0 }; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.h b/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.h index 56874d5c08..94fbfef20e 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.h +++ b/ApplicationLibCode/ReservoirDataModel/RigWellPathGeometryTools.h @@ -25,6 +25,8 @@ #include +#include + class RigWellPath; //================================================================================================== @@ -45,6 +47,9 @@ public: const std::vector& originalTvdValues, const std::vector& tvdValuesToInterpolateFrom ); + static std::pair + calculateAzimuthAndInclinationAtMd( double measuredDepth, gsl::not_null wellPathGeometry ); + private: static std::vector interpolateUndefinedNormals( const cvf::Vec3d& planeNormal, const std::vector& normals, diff --git a/GrpcInterface/Python/rips/PythonExamples/modeled_well_path.py b/GrpcInterface/Python/rips/PythonExamples/modeled_well_path.py index 757bf34001..134a34f81c 100644 --- a/GrpcInterface/Python/rips/PythonExamples/modeled_well_path.py +++ b/GrpcInterface/Python/rips/PythonExamples/modeled_well_path.py @@ -3,15 +3,32 @@ import rips # Connect to ResInsight instance resinsight = rips.Instance.find() -# Example code -print("ResInsight version: " + resinsight.version_string()) -modeled_well_paths = resinsight.project.descendants(rips.ModeledWellPath) +# Create a modeled well path and add well path targets +# The coordinates are based on the Norne case -for wellpath in modeled_well_paths: - geometry = wellpath.well_path_geometry() - geometry.print_object_info() - reference_point = geometry.reference_point - reference_point[0] += 100 - geometry.update() - geometry.print_object_info() +well_path_coll = resinsight.project.descendants(rips.WellPathCollection)[0] +well_path = well_path_coll.add_new_object(rips.ModeledWellPath) +well_path.name = "Test Well-1" +well_path.update() + +geometry = well_path.well_path_geometry() + +reference_point = geometry.reference_point +reference_point[0] = 457196 +reference_point[1] = 7322270 +reference_point[2] = 2742 +geometry.update() # Commit updates back to ResInsight + +# Create the first well target at the reference point +coord = [0, 0, 0] +geometry.append_well_target(coord) + +# Append new well targets relative the the reference point +coord = [454.28, 250, -10] +target = geometry.append_well_target(coord) + +coord = [1054.28, 250, -50] +target = geometry.append_well_target(coord) + +well_path.append_perforation_interval(3300, 3350, 0.2, 0.76) diff --git a/GrpcInterface/Python/rips/PythonExamples/modeled_well_path_lateral.py b/GrpcInterface/Python/rips/PythonExamples/modeled_well_path_lateral.py new file mode 100644 index 0000000000..3200a2156c --- /dev/null +++ b/GrpcInterface/Python/rips/PythonExamples/modeled_well_path_lateral.py @@ -0,0 +1,59 @@ +# Load ResInsight Processing Server Client Library +import rips +import time + +# Connect to ResInsight instance +resinsight = rips.Instance.find() + +# Create a modeled well path and add well path targets +# The coordinates are based on the Norne case +# Add a lateral to the main well path + +well_path_coll = resinsight.project.descendants(rips.WellPathCollection)[0] +well_path = well_path_coll.add_new_object(rips.ModeledWellPath) +well_path.name = "Test Well-1" +well_path.update() + +geometry = well_path.well_path_geometry() + +reference_point = geometry.reference_point +reference_point[0] = 457196 +reference_point[1] = 7322270 +reference_point[2] = 2742 +geometry.update() # Commit updates back to ResInsight + +# Create the first well target at the reference point +coord = [0, 0, 0] +geometry.append_well_target(coord) + +# Append new well targets relative the the reference point +coord = [454.28, 250, -10] +target = geometry.append_well_target(coord) + +coord = [1054.28, 250, -50] +target = geometry.append_well_target(coord) + +# Create a lateral at specified location on parent well +measured_depth = 3600 +lateral = well_path.append_lateral(measured_depth) +geometry = lateral.well_path_geometry() + +coord = [770, 280, 50] +target = geometry.append_well_target(coord) + +coord = [1054.28, -100, 50] +target = geometry.append_well_target(coord) + +coord = [2054.28, -100, 45] +target = geometry.append_well_target(coord) + + +# Wait 2 second +print("Wait 2 seconds ...") +time.sleep(2) +print("Move reference point of parent well") + +geometry = well_path.well_path_geometry() +reference_point = geometry.reference_point +reference_point[2] += 50 +geometry.update() # Commit updates back to ResInsight