///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2026 Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimRftCorrelationReportPlot.h" #include "RimColorLegend.h" #include "RimColorLegendItem.h" #include "RimDepthTrackPlot.h" #include "RimParameterRftCrossPlot.h" #include "RimRftCrossPlotTools.h" #include "RimRftTornadoPlot.h" #include "RimWellLogTrack.h" #include "RimWellPlotTools.h" #include "RimWellRftEnsembleCurveSet.h" #include "RimWellRftPlot.h" #include "Formations/RimWellFormationsFile.h" #include "RiaWellLogUnitTools.h" #include "RiuInterfaceToViewWindow.h" #include "RiuPlotWidget.h" #include "RiuQwtPlotWidget.h" #include "RiuRftCorrelationPlotTools.h" #include "DockAreaTitleBar.h" #include "DockAreaWidget.h" #include "DockManager.h" #include "DockWidget.h" #include "cafPdmOptionItemInfo.h" #include "cafPdmPointer.h" #include "cafPdmUiCheckBoxEditor.h" #include "cafPdmUiTreeOrdering.h" #include "cafSelectionManager.h" #include #include #include #include #include #include #include #include #include #include #include static const char* RFT_DOCK_LAYOUT_REGISTRY_KEY = "RftCorrelationReportPlot/defaultDockLayout"; //-------------------------------------------------------------------------------------------------- /// Thin wrapper that implements RiuInterfaceToViewWindow for the dock manager frame. //-------------------------------------------------------------------------------------------------- class RiuRftCorrelationReportPlotWidget : public QFrame, public RiuInterfaceToViewWindow { public: RiuRftCorrelationReportPlotWidget( RimViewWindow* viewWindow, QWidget* parent = nullptr ) : QFrame( parent ) , m_viewWindow( viewWindow ) { setLayout( new QVBoxLayout ); layout()->setContentsMargins( 0, 0, 0, 0 ); layout()->setSpacing( 0 ); } RimViewWindow* ownerViewWindow() const override { return m_viewWindow; } private: RimViewWindow* m_viewWindow; }; //-------------------------------------------------------------------------------------------------- /// Sets the report plot as the CAF-selected item whenever a context menu fires /// anywhere inside the dock manager. //-------------------------------------------------------------------------------------------------- class RftSelectionFixerOnContextMenu : public QObject { public: RftSelectionFixerOnContextMenu( caf::PdmObject* item, QObject* parent ) : QObject( parent ) , m_item( item ) { } bool eventFilter( QObject*, QEvent* event ) override { if ( event->type() == QEvent::ContextMenu ) caf::SelectionManager::instance()->setSelectedItem( m_item ); return false; } private: caf::PdmObject* m_item; }; //================================================================================================== // // // //================================================================================================== CAF_PDM_SOURCE_INIT( RimRftCorrelationReportPlot, "RftCorrelationReportPlot" ); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimRftCorrelationReportPlot::RimRftCorrelationReportPlot() { CAF_PDM_InitObject( "RFT Correlation Report Plot", ":/CorrelationReportPlot16x16.png" ); setDeletable( true ); CAF_PDM_InitFieldNoDefault( &m_name, "PlotWindowTitle", "Title" ); m_name.registerGetMethod( this, &RimRftCorrelationReportPlot::createDescription ); CAF_PDM_InitFieldNoDefault( &m_wellRftPlot, "WellRftPlot", "RFT Plot" ); CAF_PDM_InitFieldNoDefault( &m_parameterRftCrossPlot, "ParameterRftCrossPlot", "Cross Plot" ); CAF_PDM_InitFieldNoDefault( &m_tornadoPlot, "TornadoPlot", "Tornado Plot" ); CAF_PDM_InitField( &m_depthType, "DepthType", caf::AppEnum( RiaDefines::DepthType::TRUE_VERTICAL_DEPTH ), "Depth Unit" ); CAF_PDM_InitField( &m_showDockTitleBars, "ShowDockTitleBars", false, "Show Title Bars" ); caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_showDockTitleBars ); CAF_PDM_InitField( &m_dockState, "DockState", QString(), "Dock State" ); m_dockState.uiCapability()->setUiHidden( true ); dockAsPlotWindow(); m_showWindow = true; m_showPlotLegends = false; m_wellRftPlot = new RimWellRftPlot; m_wellRftPlot->detachFromDockPermanently(); m_wellRftPlot->setShowWindow( true ); m_parameterRftCrossPlot = new RimParameterRftCrossPlot; m_tornadoPlot = new RimRftTornadoPlot; m_tornadoPlot->setParameterSelectedCallback( [this]( const QString& paramName ) { onTornadoParameterSelected( paramName ); } ); applyDepthTypeToSubPlots(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimRftCorrelationReportPlot::~RimRftCorrelationReportPlot() { removeWindowFromDock(); cleanupBeforeClose(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QWidget* RimRftCorrelationReportPlot::viewWidget() { return m_viewWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimRftCorrelationReportPlot::description() const { return createDescription(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmFieldHandle* RimRftCorrelationReportPlot::userDescriptionField() { return &m_name; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimWellRftPlot* RimRftCorrelationReportPlot::wellRftPlot() const { return m_wellRftPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimParameterRftCrossPlot* RimRftCorrelationReportPlot::crossPlot() const { return m_parameterRftCrossPlot(); } //-------------------------------------------------------------------------------------------------- /// Initialize the owned RimWellRftPlot from the source plot's selected data sources. /// We keep the fresh (curve-free) child plot and call initializeDataSources(source) so /// syncCurvesFromUiSelection builds curves from scratch without touching unresolved copies. /// Without a source plot, the first well with ensemble RFT data is used with all available data sources. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::initializeFromSourcePlot( RimWellRftPlot* source ) { applyDepthTypeToSubPlots(); m_wellRftPlot->setSimWellOrWellPathName( source ? source->simWellOrWellPathName() : RimWellPlotTools::firstWellNameWithEnsembleRftData() ); // A fresh RimWellRftPlot has no tracks; syncCurvesFromUiSelection exits early without one. // Guard against duplicate track creation if this is called more than once. if ( m_wellRftPlot->plotCount() == 0 ) { auto* track = new RimWellLogTrack(); m_wellRftPlot->addPlot( track ); track->setDescription( QString( "Track %1" ).arg( m_wellRftPlot->plotCount() ) ); } m_wellRftPlot->initializeDataSources( source ); // The correlation report operates on a single time step; prefer the time step selected in the // source plot, and trim any extras that initializeDataSources may have preselected. auto selectedTimeSteps = m_wellRftPlot->selectedTimeSteps(); if ( source && !source->selectedTimeSteps().empty() ) { m_wellRftPlot->setSelectedTimeSteps( { source->selectedTimeSteps().front() } ); } else if ( selectedTimeSteps.size() > 1 ) { m_wellRftPlot->setSelectedTimeSteps( { selectedTimeSteps.front() } ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimRftCorrelationReportPlot::createDescription() const { if ( m_parameterRftCrossPlot() ) { const QString wellName = m_parameterRftCrossPlot()->wellName(); const QString param = m_parameterRftCrossPlot()->ensembleParameter(); if ( !wellName.isEmpty() && !param.isEmpty() ) { return QString( "RFT Correlation: %1 vs %2" ).arg( param ).arg( wellName ); } } return "RFT Correlation Report"; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::recreatePlotWidgets() { CAF_ASSERT( m_dockManager ); m_wellRftPlot->createPlotWidget( m_dockManager ); m_tornadoPlot->createPlotWidget( m_dockManager ); m_parameterRftCrossPlot->createPlotWidget( m_dockManager ); // Context menu fixer — ensures this report is selected in CAF on any right-click delete m_contextMenuFilter; m_contextMenuFilter = new RftSelectionFixerOnContextMenu( this, this ); if ( auto* w = m_wellRftPlot->viewWidget() ) w->installEventFilter( m_contextMenuFilter ); if ( auto* w = m_tornadoPlot->viewer() ) w->installEventFilter( m_contextMenuFilter ); if ( auto* w = m_parameterRftCrossPlot->viewer() ) w->installEventFilter( m_contextMenuFilter ); installTrackClickFilters(); auto makeDockWidget = [&]( const QString& title, RimPlotWindow* plot, QWidget* widget ) -> ads::CDockWidget* { auto* dock = new ads::CDockWidget( title, m_dockManager ); dock->setWidget( widget, ads::CDockWidget::ForceNoScrollArea ); connect( dock, &ads::CDockWidget::closed, this, [this, plot]() { plot->setShowWindow( false ); updateConnectedEditors(); } ); return dock; }; m_rftDockWidget = makeDockWidget( "RFT Plot", m_wellRftPlot(), m_wellRftPlot->viewWidget() ); m_correlationDockWidget = makeDockWidget( "Tornado Plot", m_tornadoPlot(), m_tornadoPlot->viewer() ); m_crossPlotDockWidget = makeDockWidget( "Cross Plot", m_parameterRftCrossPlot(), m_parameterRftCrossPlot->viewer() ); // Restore saved dock state or apply hard-coded default layout QByteArray stateToRestore; if ( !m_dockState().isEmpty() ) { stateToRestore = QByteArray::fromBase64( m_dockState().toLatin1() ); } else { QSettings settings; QVariant v = settings.value( RFT_DOCK_LAYOUT_REGISTRY_KEY ); if ( v.isValid() ) stateToRestore = v.toByteArray(); } if ( !stateToRestore.isEmpty() ) { m_dockManager->addDockWidget( ads::LeftDockWidgetArea, m_rftDockWidget ); auto* rightArea = m_dockManager->addDockWidget( ads::RightDockWidgetArea, m_correlationDockWidget ); m_dockManager->addDockWidget( ads::BottomDockWidgetArea, m_crossPlotDockWidget, rightArea ); m_dockManager->restoreState( stateToRestore, 1 ); } else { // Default: RFT plot on the left, tornado top-right, cross plot bottom-right m_dockManager->addDockWidget( ads::LeftDockWidgetArea, m_rftDockWidget ); auto* rightArea = m_dockManager->addDockWidget( ads::RightDockWidgetArea, m_correlationDockWidget ); m_dockManager->addDockWidget( ads::BottomDockWidgetArea, m_crossPlotDockWidget, rightArea ); } m_rftDockWidget->toggleView( m_wellRftPlot->showWindow() ); m_correlationDockWidget->toggleView( m_tornadoPlot->showWindow() ); m_crossPlotDockWidget->toggleView( m_parameterRftCrossPlot->showWindow() ); updateDockTitleBarsVisibility(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::cleanupBeforeClose() { // Detach and delete legend curves before the QwtPlot inside the track widget is destroyed. // QwtPlot has autoDelete=true, so any curves still attached when it is deleted are freed by QWT // — leaving m_legendPlotCurves with dangling pointers on the next loadDataAndUpdate(). if ( m_wellRftPlot() ) m_wellRftPlot->cleanupLegendCurves(); if ( m_tornadoPlot() ) m_tornadoPlot->detachAllCurves(); if ( m_parameterRftCrossPlot() ) m_parameterRftCrossPlot->detachAllCurves(); m_rftDockWidget = nullptr; m_correlationDockWidget = nullptr; m_crossPlotDockWidget = nullptr; if ( m_dockManager ) { delete m_dockManager; m_dockManager = nullptr; } if ( m_viewWidget ) { m_viewWidget->setParent( nullptr ); delete m_viewWidget; m_viewWidget = nullptr; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::setupBeforeSave() { if ( m_dockManager ) { m_dockState = QString::fromLatin1( m_dockManager->saveState( 1 ).toBase64() ); } } //-------------------------------------------------------------------------------------------------- /// Re-apply dock detachment after project load, since the PDM factory bypasses the constructor. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::initAfterRead() { if ( m_wellRftPlot() ) { m_wellRftPlot->detachFromDockPermanently(); m_wellRftPlot->setShowWindow( true ); } applyDepthTypeToSubPlots(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::doRenderWindowContent( QPaintDevice* paintDevice ) { if ( m_viewWidget ) m_viewWidget->render( paintDevice ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QWidget* RimRftCorrelationReportPlot::createViewWidget( QWidget* mainWindowParent ) { auto* wrapper = new RiuRftCorrelationReportPlotWidget( this, mainWindowParent ); m_viewWidget = wrapper; m_dockManager = new ads::CDockManager( wrapper ); m_dockManager->setStyleSheet( "ads--CDockSplitter::handle { width: 2px; height: 2px; background-color: #a0a0a0; }" ); wrapper->layout()->addWidget( m_dockManager ); recreatePlotWidgets(); return m_viewWidget; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::deleteViewWidget() { cleanupBeforeClose(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::onLoadDataAndUpdate() { updateDockWindowVisibility(); if ( m_showWindow ) { m_wellRftPlot->loadDataAndUpdate(); installTrackClickFilters(); updateSelectedZoneHighlight(); syncZoneColorsToCrossPlot(); syncTornadoInputsFromCrossPlot(); m_tornadoPlot->loadDataAndUpdate(); m_parameterRftCrossPlot->loadDataAndUpdate(); } updateLayout(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { uiOrdering.add( &m_depthType ); // Delegate cross-plot settings (ensemble, well, depth range, parameter) to the cross plot m_parameterRftCrossPlot->appendDataAndFilterUiOrdering( uiOrdering ); auto* layoutGroup = uiOrdering.addNewGroup( "Dock Layout" ); layoutGroup->setCollapsedByDefault(); layoutGroup->add( &m_showDockTitleBars ); uiOrdering.skipRemainingFields( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString /*uiConfigName*/ ) { uiTreeOrdering.add( m_wellRftPlot() ); uiTreeOrdering.add( m_tornadoPlot() ); uiTreeOrdering.add( m_parameterRftCrossPlot() ); uiTreeOrdering.skipRemainingChildren(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) { if ( changedField == &m_showDockTitleBars ) { updateDockTitleBarsVisibility(); return; } if ( changedField == &m_depthType ) { applyDepthTypeToSubPlots(); } loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::childFieldChangedByUi( const caf::PdmFieldHandle* changedChildField ) { if ( m_rftDockWidget && changedChildField == &m_wellRftPlot ) m_rftDockWidget->toggleView( m_wellRftPlot->showWindow() ); else if ( m_correlationDockWidget && changedChildField == &m_tornadoPlot ) m_correlationDockWidget->toggleView( m_tornadoPlot->showWindow() ); else if ( m_crossPlotDockWidget && changedChildField == &m_parameterRftCrossPlot ) { m_crossPlotDockWidget->toggleView( m_parameterRftCrossPlot->showWindow() ); syncCrossPlotSelectionToRftPlot(); } updateDockTitleBarsVisibility(); loadDataAndUpdate(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::syncCrossPlotSelectionToRftPlot() { if ( !m_wellRftPlot() || !m_parameterRftCrossPlot() ) return; const QString wellName = m_parameterRftCrossPlot->wellName(); const QDateTime timeStep = m_parameterRftCrossPlot->selectedTimeStep(); if ( !wellName.isEmpty() ) m_wellRftPlot->setSimWellOrWellPathName( wellName ); if ( timeStep.isValid() ) m_wellRftPlot->setSelectedTimeSteps( { timeStep } ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimRftTornadoPlot* RimRftCorrelationReportPlot::tornadoPlot() const { return m_tornadoPlot(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimRftCorrelationReportPlot::childPlotsForTextExport() const { return { crossPlot(), tornadoPlot() }; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::onTornadoParameterSelected( const QString& paramName ) { if ( m_tornadoPlot() ) { m_tornadoPlot->setSelectedParameter( paramName ); m_tornadoPlot->loadDataAndUpdate(); } if ( m_parameterRftCrossPlot() ) { m_parameterRftCrossPlot->setEnsembleParameter( paramName ); m_parameterRftCrossPlot->loadDataAndUpdate(); syncEnsembleParameterToRftCurves(); } updateConnectedEditors(); } //-------------------------------------------------------------------------------------------------- /// Makes RFT curves colored by ensemble parameter use the cross plot's ensemble parameter. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::syncEnsembleParameterToRftCurves() { if ( !m_wellRftPlot() || !m_parameterRftCrossPlot() ) return; auto* curveSet = m_wellRftPlot->findEnsembleCurveSet( m_parameterRftCrossPlot->ensemble() ); if ( curveSet && curveSet->syncEnsembleParameter( m_parameterRftCrossPlot->ensembleParameter() ) ) { m_wellRftPlot->rebuildCurves(); curveSet->updateConnectedEditors(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::syncTornadoInputsFromCrossPlot() { if ( !m_tornadoPlot() || !m_parameterRftCrossPlot() ) return; applyWellFormationsToSubPlots(); m_tornadoPlot->setEnsemble( m_parameterRftCrossPlot->ensemble() ); m_tornadoPlot->setWellName( m_parameterRftCrossPlot->wellName() ); m_tornadoPlot->setTimeStep( m_parameterRftCrossPlot->selectedTimeStep() ); m_tornadoPlot->setSelectedParameter( m_parameterRftCrossPlot->ensembleParameter() ); m_tornadoPlot->setEclipseCase( m_parameterRftCrossPlot->eclipseCase() ); m_tornadoPlot->setDepthRange( m_parameterRftCrossPlot->depthRangeMin(), m_parameterRftCrossPlot->depthRangeMax() ); m_tornadoPlot->setDepthType( m_parameterRftCrossPlot->depthType() ); m_tornadoPlot->setFilterMode( m_parameterRftCrossPlot->filterMode() ); m_tornadoPlot->setSelectedZones( m_parameterRftCrossPlot->selectedZones() ); } //-------------------------------------------------------------------------------------------------- /// Makes a click on a formation in the RFT plot tracks select that formation as the zone filter /// used by the cross plot and tornado plot. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::installTrackClickFilters() { if ( !m_wellRftPlot() ) return; if ( m_trackClickFilter ) m_trackClickFilter->deleteLater(); m_trackClickFilter = new QObject( this ); const auto orientation = m_wellRftPlot->depthOrientation(); const bool isVertical = orientation == RiaDefines::Orientation::VERTICAL; for ( size_t i = 0; i < m_wellRftPlot->plotCount(); ++i ) { auto* track = dynamic_cast( m_wellRftPlot->plotByIndex( i ) ); if ( !track ) continue; RiuQwtPlotWidget* widget = track->viewer(); if ( !widget ) continue; caf::PdmPointer trackPtr( track ); RiuRftCorrelationPlotTools::installDepthClickFilter( widget, RimDepthTrackPlot::depthAxis( orientation ), isVertical, [this, trackPtr]( double depth ) { if ( trackPtr ) onRftTrackDepthClicked( trackPtr, depth ); }, m_trackClickFilter ); } } //-------------------------------------------------------------------------------------------------- /// Marks the depth interval of each selected zone in the RFT plot tracks with a thin bar along the /// depth axis side. Stale bars are found by type and removed before new ones are attached. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::updateSelectedZoneHighlight() { if ( !m_wellRftPlot() || !m_parameterRftCrossPlot() ) return; const auto filterMode = m_parameterRftCrossPlot->filterMode(); const bool isVertical = m_wellRftPlot->depthOrientation() == RiaDefines::Orientation::VERTICAL; // Bars are drawn in plot coordinates, so the depth range is converted to the plot's depth unit std::vector depthRangeBars; if ( filterMode == RimRftCrossPlotTools::DepthFilterMode::DEPTH_RANGE ) { const auto fromDepthUnit = m_wellRftPlot->caseDepthUnit(); const auto toDepthUnit = m_wellRftPlot->depthUnit(); depthRangeBars.push_back( { RiaWellLogUnitTools::convertDepth( m_parameterRftCrossPlot->depthRangeMin(), fromDepthUnit, toDepthUnit ), RiaWellLogUnitTools::convertDepth( m_parameterRftCrossPlot->depthRangeMax(), fromDepthUnit, toDepthUnit ) } ); } const std::vector selectedZones = m_parameterRftCrossPlot->selectedZones(); for ( size_t i = 0; i < m_wellRftPlot->plotCount(); ++i ) { auto* track = dynamic_cast( m_wellRftPlot->plotByIndex( i ) ); if ( !track || !track->viewer() ) continue; std::vector barIntervals = depthRangeBars; if ( filterMode == RimRftCrossPlotTools::DepthFilterMode::ZONES ) { // Use the zones as shaded on this track so the bars line up with them for ( const auto& [name, top, base] : track->formationZoneDisplayRanges() ) { if ( std::find( selectedZones.begin(), selectedZones.end(), name ) != selectedZones.end() ) barIntervals.push_back( { top, base } ); } } RiuRftCorrelationPlotTools::setDepthIntervalBars( track->viewer(), barIntervals, isVertical ); } } //-------------------------------------------------------------------------------------------------- /// Gives the cross plot the formation colors used by the RFT plot tracks, looked up by formation name. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::syncZoneColorsToCrossPlot() { if ( !m_wellRftPlot() || !m_parameterRftCrossPlot() ) return; std::map zoneColors; for ( size_t i = 0; i < m_wellRftPlot->plotCount() && zoneColors.empty(); ++i ) { auto* track = dynamic_cast( m_wellRftPlot->plotByIndex( i ) ); if ( !track ) continue; for ( const auto& [name, color] : track->formationZoneColors() ) { // The alpha carries the track's shading transparency, used for the observed pressure band zoneColors[name] = QColor( color.r(), color.g(), color.b(), track->formationShadingAlpha() ); } } m_parameterRftCrossPlot->setZoneColors( zoneColors ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::onRftTrackDepthClicked( RimWellLogTrack* track, double depth ) { if ( !m_parameterRftCrossPlot() ) return; // The track's zones are in plot coordinates and don't overlap, so the hit is the zone shaded at the click for ( const auto& [name, top, base] : track->formationZoneDisplayRanges() ) { if ( depth < std::min( top, base ) || depth > std::max( top, base ) ) continue; std::vector zones = m_parameterRftCrossPlot->selectedZones(); auto it = std::find( zones.begin(), zones.end(), name ); if ( it != zones.end() ) zones.erase( it ); // clicking a selected zone again deselects it else zones.push_back( name ); m_parameterRftCrossPlot->setFilterMode( RimRftCrossPlotTools::DepthFilterMode::ZONES ); m_parameterRftCrossPlot->setSelectedZones( zones ); // The zone filter does not affect the RFT curves; skip reloading the RFT plot to keep its zoom updateSelectedZoneHighlight(); syncTornadoInputsFromCrossPlot(); m_tornadoPlot->loadDataAndUpdate(); m_parameterRftCrossPlot->loadDataAndUpdate(); updateConnectedEditors(); return; } } //-------------------------------------------------------------------------------------------------- /// Pushes the user's manual "Well Formations File" selection in the cross plot down to the sub /// plots so their "Filter By: Zones" option becomes available. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::applyWellFormationsToSubPlots() { if ( !m_parameterRftCrossPlot() ) return; RimWellFormationsFile* wellFormationsFile = m_parameterRftCrossPlot->wellFormationsFile(); if ( m_tornadoPlot() ) m_tornadoPlot->setWellFormations( wellFormationsFile ); } //-------------------------------------------------------------------------------------------------- /// Pushes the selected depth unit (MD/TVD) down to the sub plots that present a depth axis or /// filter samples by depth. //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::applyDepthTypeToSubPlots() { if ( m_wellRftPlot() ) { m_wellRftPlot->setAvailableDepthTypes( { RiaDefines::DepthType::MEASURED_DEPTH, RiaDefines::DepthType::TRUE_VERTICAL_DEPTH } ); m_wellRftPlot->setDepthType( m_depthType() ); } if ( m_parameterRftCrossPlot() ) m_parameterRftCrossPlot->setDepthType( m_depthType() ); if ( m_tornadoPlot() ) m_tornadoPlot->setDepthType( m_depthType() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QList RimRftCorrelationReportPlot::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) { QList options; if ( fieldNeedingOptions == &m_depthType ) { using DepthAppEnum = caf::AppEnum; for ( auto depthType : { RiaDefines::DepthType::MEASURED_DEPTH, RiaDefines::DepthType::TRUE_VERTICAL_DEPTH } ) { options.push_back( caf::PdmOptionItemInfo( DepthAppEnum::uiText( depthType ), depthType ) ); } } return options; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimRftCorrelationReportPlot::updateDockTitleBarsVisibility() { if ( !m_dockManager ) return; for ( auto* area : m_dockManager->openedDockAreas() ) area->titleBar()->setVisible( m_showDockTitleBars() ); }