///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2018- 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 "RimWellPathGeometryDef.h" #include "WellPathCommands/PointTangentManipulator/RicWellPathGeometry3dEditor.h" #include "WellPathCommands/RicCreateWellTargetsPickEventHandler.h" #include "RiaFieldHandleTools.h" #include "RiaJCurveCalculator.h" #include "RiaLogging.h" #include "RiaOffshoreSphericalCoords.h" #include "RiaPolyArcLineSampler.h" #include "RiaSCurveCalculator.h" #include "RigWellPath.h" #include "RimModeledWellPath.h" #include "RimProject.h" #include "RimWellPathTarget.h" #include "RiuViewerCommands.h" #include "cafCmdFeatureMenuBuilder.h" #include "cafPdmFieldScriptingCapabilityCvfVec3d.h" #include "cafPdmObjectScriptingCapability.h" #include "cafPdmUiDoubleValueEditor.h" #include "cafPdmUiLineEditor.h" #include "cafPdmUiPushButtonEditor.h" #include "cafPdmUiTableViewEditor.h" #include "cafPdmUiTreeOrdering.h" #include "cvfGeometryTools.h" CAF_PDM_SOURCE_INIT( RimWellPathGeometryDef, "WellPathGeometryDef", "WellPathGeometry" ); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellPathGeometryDef::RimWellPathGeometryDef() : changed( this ) , m_pickTargetsEventHandler( new RicCreateWellTargetsPickEventHandler( this ) ) { CAF_PDM_InitScriptableObjectWithNameAndComment( "Well Targets", ":/WellTargets.png", "", "", "WellPathGeometry", "Class containing the geometry of a modeled Well Path" ); this->setUi3dEditorTypeName( RicWellPathGeometry3dEditor::uiEditorTypeName() ); CAF_PDM_InitScriptableFieldWithScriptKeyword( &m_referencePointUtmXyd, "ReferencePosUtmXyd", "ReferencePoint", cvf::Vec3d( 0, 0, 0 ), "UTM Reference Point", "", "", "" ); CAF_PDM_InitScriptableField( &m_airGap, "AirGap", 0.0, "Air Gap", "", "", "" ); m_airGap.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); CAF_PDM_InitScriptableField( &m_mdAtFirstTarget, "MdAtFirstTarget", 0.0, "MD at First Target", "", "", "" ); m_mdAtFirstTarget.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); CAF_PDM_InitScriptableFieldNoDefault( &m_wellTargets, "WellPathTargets", "Well Targets", "", "", "" ); m_wellTargets.uiCapability()->setUiEditorTypeName( caf::PdmUiTableViewEditor::uiEditorTypeName() ); m_wellTargets.uiCapability()->setUiTreeChildrenHidden( true ); m_wellTargets.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP ); m_wellTargets.uiCapability()->setCustomContextMenuEnabled( true ); CAF_PDM_InitScriptableField( &m_useAutoGeneratedTargetAtSeaLevel, "UseAutoGeneratedTargetAtSeaLevel", true, "Generate Target at Sea Level", "", "", "" ); CAF_PDM_InitScriptableFieldNoDefault( &m_autoTargetAtSeaLevel, "AutoGeneratedTarget", "Auto Generated Target", "", "", "" ); m_autoTargetAtSeaLevel = new RimWellPathTarget; m_autoTargetAtSeaLevel->setEnabled( false ); CAF_PDM_InitScriptableField( &m_isAttachedToParentWell, "AttachedToParentWell", false, "Attached to Parent Well", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_fixedWellPathPoints, "FixedWellPathPoints", "", "", "", "" ); CAF_PDM_InitFieldNoDefault( &m_fixedMeasuredDepths, "FixedMeasuredDepths", "", "", "", "" ); CAF_PDM_InitField( &m_pickPointsEnabled, "m_pickPointsEnabled", false, "", "", "", "" ); caf::PdmUiPushButtonEditor::configureEditorForField( &m_pickPointsEnabled ); CAF_PDM_InitScriptableField( &m_showSpheres, "ShowSpheres", false, "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", "", "", "" ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellPathGeometryDef::~RimWellPathGeometryDef() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::Vec3d RimWellPathGeometryDef::anchorPointXyz() const { cvf::Vec3d xyz( m_referencePointUtmXyd() ); xyz.z() = -xyz.z(); return xyz; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::setReferencePointXyz( const cvf::Vec3d& refPointXyz ) { cvf::Vec3d xyd( refPointXyz ); xyd.z() = -xyd.z(); m_referencePointUtmXyd = xyd; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RimWellPathGeometryDef::airGap() const { return m_airGap; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::setAirGap( double airGap ) { m_airGap = airGap; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RimWellPathGeometryDef::mdAtFirstTarget() const { return m_mdAtFirstTarget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::setMdAtFirstTarget( double md ) { m_mdAtFirstTarget = md; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::setIsAttachedToParentWell( bool isAttached ) { m_isAttachedToParentWell = isAttached; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::setFixedWellPathPoints( const std::vector& points ) { m_fixedWellPathPoints = points; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::setFixedMeasuredDepths( const std::vector& mds ) { m_fixedMeasuredDepths = mds; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimWellPathGeometryDef::createTargets( const std::vector& points ) { CAF_ASSERT( points.size() >= 2u ); std::vector appendedTargets; for ( size_t i = 0; i < points.size(); ++i ) { auto target = appendTarget(); target->setAsPointTargetXYZ( points[i] ); appendedTargets.push_back( target ); } return appendedTargets; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RimWellPathGeometryDef::createWellPathGeometry() { cvf::ref wellPathGeometry = new RigWellPath; if ( m_useAutoGeneratedTargetAtSeaLevel ) { updateTargetAtSeaLevel(); } std::vector wellPathPoints = m_fixedWellPathPoints; std::vector measuredDepths = m_fixedMeasuredDepths; RiaLineArcWellPathCalculator wellPathCalculator = lineArcWellPathCalculator(); if ( wellPathCalculator.lineArcEndpoints().size() >= 2 ) { 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(); for ( auto md : sampledMeasuredDepths ) { measuredDepths.push_back( md + startMD ); } } wellPathGeometry->setWellPathPoints( wellPathPoints ); wellPathGeometry->setMeasuredDepths( measuredDepths ); if ( m_airGap != 0.0 ) { wellPathGeometry->setDatumElevation( m_airGap ); } return wellPathGeometry; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimWellPathGeometryDef::wellPlan() const { RiaLineArcWellPathCalculator wellPathCalculator = lineArcWellPathCalculator(); RiaWellPlanCalculator wpCalc( wellPathCalculator.startTangent(), wellPathCalculator.lineArcEndpoints() ); return wpCalc.wellPlan(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimWellPathGeometryDef::showSpheres() const { return m_showSpheres(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::Color3f RimWellPathGeometryDef::sphereColor() const { return m_sphereColor(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RimWellPathGeometryDef::sphereRadiusFactor() const { return m_sphereRadiusFactor(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::pair RimWellPathGeometryDef::findActiveTargetsAroundInsertionPoint( const RimWellPathTarget* targetToInsertBefore ) { RimWellPathTarget* before = nullptr; RimWellPathTarget* after = nullptr; bool foundTarget = false; for ( const auto& wt : m_wellTargets ) { if ( wt == targetToInsertBefore ) { foundTarget = true; } if ( wt->isEnabled() && !after && foundTarget ) after = wt; if ( wt->isEnabled() && !foundTarget ) before = wt; } return { before, after }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::insertTarget( const RimWellPathTarget* targetToInsertBefore, RimWellPathTarget* targetToInsert ) { size_t index = m_wellTargets.index( targetToInsertBefore ); if ( index < m_wellTargets.size() ) m_wellTargets.insert( index, targetToInsert ); else m_wellTargets.push_back( targetToInsert ); targetToInsert->moved.connect( this, &RimWellPathGeometryDef::onTargetMoved ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::deleteTarget( RimWellPathTarget* targetTodelete ) { m_wellTargets.removeChildObject( targetTodelete ); delete targetTodelete; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::deleteAllTargets() { m_wellTargets.deleteAllChildObjects(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellPathTarget* RimWellPathGeometryDef::appendTarget() { RimWellPathTarget* wellPathTarget = nullptr; auto targets = m_wellTargets.childObjects(); if ( targets.empty() ) { wellPathTarget = new RimWellPathTarget; } else { wellPathTarget = dynamic_cast( targets.back()->xmlCapability()->copyByXmlSerialization( caf::PdmDefaultObjectFactory::instance() ) ); } if ( wellPathTarget ) { m_wellTargets.push_back( wellPathTarget ); } wellPathTarget->moved.connect( this, &RimWellPathGeometryDef::onTargetMoved ); return wellPathTarget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RimWellPathTarget* RimWellPathGeometryDef::firstActiveTarget() const { for ( const RimWellPathTarget* target : m_wellTargets ) { if ( target->isEnabled() ) { return target; } } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RimWellPathTarget* RimWellPathGeometryDef::lastActiveTarget() const { if ( !m_wellTargets.size() ) return nullptr; for ( int tIdx = static_cast( m_wellTargets.size() - 1 ); tIdx >= 0; --tIdx ) { if ( m_wellTargets[tIdx]->isEnabled() ) { return m_wellTargets[tIdx]; } } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::enableTargetPointPicking( bool isEnabling ) { m_pickPointsEnabled = isEnabling; this->updateConnectedEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::updateWellPathVisualization( bool fullUpdate ) { changed.send( fullUpdate ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList RimWellPathGeometryDef::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly ) { QList options; return options; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { if ( changedField == &m_pickPointsEnabled ) { this->updateConnectedEditors(); } changed.send( false ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { if ( !m_isAttachedToParentWell ) { uiOrdering.add( &m_referencePointUtmXyd ); uiOrdering.add( &m_airGap ); uiOrdering.add( &m_mdAtFirstTarget ); uiOrdering.add( &m_useAutoGeneratedTargetAtSeaLevel ); } auto group = uiOrdering.addNewGroup( "Well Target Appearance" ); group->add( &m_showSpheres ); group->add( &m_sphereColor ); group->add( &m_sphereRadiusFactor ); uiOrdering.add( &m_wellTargets ); uiOrdering.add( &m_pickPointsEnabled ); m_mdAtFirstTarget.uiCapability()->setUiReadOnly( m_useAutoGeneratedTargetAtSeaLevel() ); uiOrdering.skipRemainingFields( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) { uiTreeOrdering.skipRemainingChildren( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimWellPathGeometryDef::activeWellTargets() const { std::vector active; if ( m_useAutoGeneratedTargetAtSeaLevel && !m_wellTargets.empty() && m_autoTargetAtSeaLevel ) { active.push_back( m_autoTargetAtSeaLevel ); } for ( const auto& wt : m_wellTargets ) { if ( wt->isEnabled() ) { active.push_back( wt ); } } return active; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RiaLineArcWellPathCalculator RimWellPathGeometryDef::lineArcWellPathCalculator() const { std::vector targetDatas; std::vector activeTargets = activeWellTargets(); for ( auto wellTarget : activeTargets ) { targetDatas.push_back( wellTarget->wellTargetData() ); } RiaLineArcWellPathCalculator wellPathCalculator( anchorPointXyz(), targetDatas ); const std::vector& targetStatuses = wellPathCalculator.targetStatuses(); for ( size_t tIdx = 0; tIdx < activeTargets.size(); ++tIdx ) { activeTargets[tIdx]->flagRadius1AsIncorrect( targetStatuses[tIdx].isRadius1Editable, false, 0 ); activeTargets[tIdx]->flagRadius2AsIncorrect( targetStatuses[tIdx].isRadius2Editable, false, 0 ); if ( targetStatuses[tIdx].hasDerivedTangent ) { activeTargets[tIdx]->setDerivedTangent( targetStatuses[tIdx].resultAzimuth, targetStatuses[tIdx].resultInclination ); } if ( targetStatuses[tIdx].hasOverriddenRadius1 ) { activeTargets[tIdx]->flagRadius1AsIncorrect( targetStatuses[tIdx].isRadius1Editable, true, targetStatuses[tIdx].resultRadius1 ); } if ( targetStatuses[tIdx].hasOverriddenRadius2 ) { activeTargets[tIdx]->flagRadius2AsIncorrect( targetStatuses[tIdx].isRadius2Editable, true, targetStatuses[tIdx].resultRadius2 ); } } return wellPathCalculator; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::updateTargetAtSeaLevel() { if ( m_useAutoGeneratedTargetAtSeaLevel && !m_wellTargets.empty() ) { cvf::Vec3d newPos = cvf::Vec3d::ZERO; auto firstTarget = m_wellTargets[0]; cvf::Vec3d targetTangent = firstTarget->tangent(); double radius = firstTarget->radius1(); cvf::Vec3d tangentInHorizontalPlane = targetTangent; tangentInHorizontalPlane[2] = 0.0; tangentInHorizontalPlane.normalize(); RiaOffshoreSphericalCoords sphTangent( targetTangent ); double inc = sphTangent.inc(); double horizontalLengthFromTarget = radius - radius * cvf::Math::cos( inc ); newPos = firstTarget->targetPointXYZ() - horizontalLengthFromTarget * tangentInHorizontalPlane; newPos.z() = -anchorPointXyz().z(); m_autoTargetAtSeaLevel->setAsPointXYZAndTangentTarget( { newPos[0], newPos[1], newPos[2] }, 0, 0 ); m_autoTargetAtSeaLevel->setEnabled( true ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::defineCustomContextMenu( const caf::PdmFieldHandle* fieldNeedingMenu, QMenu* menu, QWidget* fieldEditorWidget ) { caf::CmdFeatureMenuBuilder menuBuilder; menuBuilder << "RicNewWellPathListTargetFeature"; menuBuilder << "Separator"; menuBuilder << "RicDeleteWellPathTargetFeature"; menuBuilder.appendToMenu( menu ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) { if ( field == &m_pickPointsEnabled ) { caf::PdmUiPushButtonEditorAttribute* pbAttribute = dynamic_cast( attribute ); if ( pbAttribute ) { if ( !m_pickPointsEnabled ) { pbAttribute->m_buttonText = "Start Picking Targets"; } else { pbAttribute->m_buttonText = "Stop Picking Targets"; } } } if ( field == &m_wellTargets ) { auto tvAttribute = dynamic_cast( attribute ); if ( tvAttribute ) { tvAttribute->resizePolicy = caf::PdmUiTableViewEditorAttribute::RESIZE_TO_FIT_CONTENT; if ( m_pickPointsEnabled ) { tvAttribute->baseColor.setRgb( 255, 220, 255 ); tvAttribute->alwaysEnforceResizePolicy = true; } } } if ( field == &m_referencePointUtmXyd ) { auto uiDisplayStringAttr = dynamic_cast( attribute ); if ( uiDisplayStringAttr ) { uiDisplayStringAttr->m_displayString = QString::number( m_referencePointUtmXyd()[0], 'f', 2 ) + " " + QString::number( m_referencePointUtmXyd()[1], 'f', 2 ) + " " + QString::number( m_referencePointUtmXyd()[2], 'f', 2 ); } } if ( field == &m_airGap ) { auto uiDoubleValueEditorAttr = dynamic_cast( attribute ); if ( uiDoubleValueEditorAttr ) { uiDoubleValueEditorAttr->m_decimals = 2; uiDoubleValueEditorAttr->m_validator = new QDoubleValidator( 0.0, std::numeric_limits::max(), 2 ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) { RicWellPathGeometry3dEditorAttribute* attrib = dynamic_cast( attribute ); if ( attrib ) { attrib->pickEventHandler = m_pickTargetsEventHandler; attrib->enablePicking = m_pickPointsEnabled; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::onTargetMoved( const caf::SignalEmitter* emitter, bool fullUpdate ) { updateWellPathVisualization( fullUpdate ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::initAfterRead() { if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2019.12.1" ) ) { m_useAutoGeneratedTargetAtSeaLevel = false; } for ( auto wt : m_wellTargets ) { if ( wt ) wt->moved.connect( this, &RimWellPathGeometryDef::onTargetMoved ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimWellPathGeometryDef::setUseAutoGeneratedTargetAtSeaLevel( bool autoGenerate ) { m_useAutoGeneratedTargetAtSeaLevel = autoGenerate; }