#include "nmPebiResultSceneController.h" #include "nmPebiResultPropertyCatalog.h" #include "nmPebiResultPropertyDisplayText.h" #include "nmPebiResultSnapshot.h" #include "nmPebiResultSnapshotRenderAdapter.h" #include "nmPebiResultWellAnnotationLayer.h" #include "nmVTKColorMapCatalog.h" #include "nmVTKMeshLayer.h" #include "nmVTKPlanarScaleGridLayer.h" #include "nmVTKScalarBarLayer.h" #include "nmVTKScene.h" #include "nmWxVTKRenderContainerWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { const char* const RESULT_MESH_LAYER_ID = "pebi.result.mesh"; const char* const RESULT_SCALAR_BAR_LAYER_ID = "pebi.result.scalar_bar"; const char* const RESULT_SCALE_GRID_LAYER_ID = "pebi.result.scale_grid"; const char* const RESULT_WELLS_LAYER_ID = "pebi.result.wells"; /* Qt 4.8 的 lupdate 无法从变量参数中推导翻译原文,必须显式登记。 */ static const char* const s_pResultSceneTranslationMarkers[] = { QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The result snapshot is invalid."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The result snapshot has no renderable property."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Unable to initialize the result render adapter."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The result render data failed validation."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Unable to create the result render layers."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Unable to commit the result render data."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The requested result property or frame is invalid."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The result property range is invalid."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Unable to apply the result property or frame."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The color settings no longer match the current result."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The color settings are invalid."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The selected color map is unavailable."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "The manual range must contain two finite increasing values."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Logarithmic scale requires positive property and display ranges."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Unable to construct the color mapping."), QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Unable to apply the color settings.") }; bool isResultSceneGuiThread() { QCoreApplication* pApplication = QCoreApplication::instance(); return pApplication == nullptr || pApplication->thread() == QThread::currentThread(); } QString controllerText(const char* pText) { Q_UNUSED(s_pResultSceneTranslationMarkers); return QCoreApplication::translate( "nmPebiResultSceneController", pText); } void copyBounds(const double dSource[6], double dTarget[6]) { for(int nIndex = 0; nIndex < 6; ++nIndex) { dTarget[nIndex] = dSource[nIndex]; } } bool getFiniteBounds(vtkDataSet* pDataSet, double dBounds[6]) { if(pDataSet == nullptr || dBounds == nullptr) { return false; } pDataSet->GetBounds(dBounds); if(!vtkMath::AreBoundsInitialized(dBounds)) { return false; } for(int nIndex = 0; nIndex < 6; ++nIndex) { if(!vtkMath::IsFinite(dBounds[nIndex])) { return false; } } return dBounds[0] < dBounds[1] && dBounds[2] < dBounds[3] && dBounds[4] <= dBounds[5]; } bool getInternalDisplayRange(const double dVisibleRange[2], bool bLogarithmic, bool bSingleValueMode, double dInternalRange[2]) { if(dVisibleRange == nullptr || dInternalRange == nullptr || !vtkMath::IsFinite(dVisibleRange[0]) || !vtkMath::IsFinite(dVisibleRange[1]) || dVisibleRange[0] > dVisibleRange[1] || (bLogarithmic && dVisibleRange[0] <= 0.0)) { return false; } if(dVisibleRange[0] < dVisibleRange[1]) { dInternalRange[0] = dVisibleRange[0]; dInternalRange[1] = dVisibleRange[1]; return true; } if(!bSingleValueMode || dVisibleRange[0] != dVisibleRange[1]) { return false; } // VTK 不接受零宽范围。单值模式从真实值单向扩展,使 c 位于 LUT 最小端。 const double dValue = dVisibleRange[0]; double dMaximum = 0.0; if(bLogarithmic) { dMaximum = dValue * (1.0 + 1.0e-6); if(!vtkMath::IsFinite(dMaximum) || dMaximum <= dValue) { dMaximum = dValue * 2.0; } } else { const double dEpsilon = qMax(1.0, fabs(dValue)) * 1.0e-6; dMaximum = dValue + dEpsilon; } if(!vtkMath::IsFinite(dMaximum) || dValue >= dMaximum || (bLogarithmic && dValue <= 0.0)) { return false; } dInternalRange[0] = dValue; dInternalRange[1] = dMaximum; return true; } bool fillLookupTableWithFirstColor(vtkLookupTable* pLookupTable) { if(pLookupTable == nullptr || pLookupTable->GetNumberOfTableValues() <= 0) { return false; } // 保留用户选择的色带语义,但让单值网格和色标统一使用第 0 个普通表项。 double dFirstColor[4] = { 0.0, 0.0, 0.0, 1.0 }; pLookupTable->GetTableValue(0, dFirstColor); for(int nComponent = 0; nComponent < 4; ++nComponent) { if(!vtkMath::IsFinite(dFirstColor[nComponent])) { return false; } } const int nTableValueCount = static_cast( pLookupTable->GetNumberOfTableValues()); for(int nIndex = 0; nIndex < nTableValueCount; ++nIndex) { pLookupTable->SetTableValue(nIndex, dFirstColor); } return true; } struct ResultDisplayCandidate { ResultDisplayCandidate() : m_pLookupTable(nullptr), m_bSingleValueMode(false) { m_dBusinessRange[0] = m_dBusinessRange[1] = 0.0; m_dVisibleRange[0] = m_dVisibleRange[1] = 0.0; m_dInternalRange[0] = 0.0; m_dInternalRange[1] = 1.0; } nmPebiResultDisplaySettings m_oSettings; double m_dBusinessRange[2]; double m_dVisibleRange[2]; double m_dInternalRange[2]; vtkSmartPointer m_pLookupTable; QString m_sScalarBarTitle; bool m_bSingleValueMode; }; bool prepareDisplayCandidate( const QSharedPointer& pSnapshot, const QString& sPropertyId, const nmPebiResultDisplaySettings& oRequestedSettings, ResultDisplayCandidate& oCandidate, QString* pError) { nmPebiResultDisplaySettings oSettings = oRequestedSettings.normalized(); if(pSnapshot.isNull() || !pSnapshot->hasProperty(sPropertyId) || !oSettings.isStructurallyValid()) { if(pError != nullptr) { *pError = controllerText("The color settings are invalid."); } return false; } if(!nmVTKColorMapCatalog::contains(oSettings.m_sColorMapId)) { if(pError != nullptr) { *pError = controllerText("The selected color map is unavailable."); } return false; } double dBusinessRange[2] = { 0.0, 0.0 }; if(!pSnapshot->getPropertyRange(sPropertyId, dBusinessRange) || !vtkMath::IsFinite(dBusinessRange[0]) || !vtkMath::IsFinite(dBusinessRange[1]) || dBusinessRange[0] > dBusinessRange[1]) { if(pError != nullptr) { *pError = controllerText("The result property range is invalid."); } return false; } double dVisibleRange[2] = { dBusinessRange[0], dBusinessRange[1] }; if(oSettings.m_bHasManualRange) { if(!vtkMath::IsFinite(oSettings.m_dManualMinimum) || !vtkMath::IsFinite(oSettings.m_dManualMaximum) || oSettings.m_dManualMinimum >= oSettings.m_dManualMaximum) { if(pError != nullptr) { *pError = controllerText( "The manual range must contain two finite increasing values."); } return false; } dVisibleRange[0] = oSettings.m_dManualMinimum; dVisibleRange[1] = oSettings.m_dManualMaximum; } const bool bSingleValueMode = !oSettings.m_bHasManualRange && dBusinessRange[0] == dBusinessRange[1]; const bool bLogarithmic = oSettings.m_eScaleMode == NM_PebiColorScale_Logarithmic; if(bLogarithmic && (dBusinessRange[0] <= 0.0 || dVisibleRange[0] <= 0.0)) { if(pError != nullptr) { *pError = controllerText( "Logarithmic scale requires positive property and display ranges."); } return false; } double dInternalRange[2] = { 0.0, 1.0 }; vtkSmartPointer pLookupTable = nmVTKColorMapCatalog::createLookupTable( oSettings.m_sColorMapId); if(pLookupTable == nullptr || !getInternalDisplayRange(dVisibleRange, bLogarithmic, bSingleValueMode, dInternalRange)) { if(pError != nullptr) { *pError = controllerText("Unable to construct the color mapping."); } return false; } pLookupTable->SetTableRange(dInternalRange); if(bLogarithmic) { pLookupTable->SetScaleToLog10(); } else { pLookupTable->SetScaleToLinear(); } if(bSingleValueMode && !fillLookupTableWithFirstColor(pLookupTable)) { if(pError != nullptr) { *pError = controllerText("Unable to construct the color mapping."); } return false; } const QColor oBelowColor = oSettings.m_oBelowRangeColor; const QColor oAboveColor = oSettings.m_oAboveRangeColor; pLookupTable->SetBelowRangeColor( oBelowColor.redF(), oBelowColor.greenF(), oBelowColor.blueF(), 1.0); pLookupTable->SetAboveRangeColor( oAboveColor.redF(), oAboveColor.greenF(), oAboveColor.blueF(), 1.0); pLookupTable->SetUseBelowRangeColor( oSettings.m_bUseBelowRangeColor ? 1 : 0); pLookupTable->SetUseAboveRangeColor( oSettings.m_bUseAboveRangeColor ? 1 : 0); // VTK 7.1 的上下越界颜色位于特殊表项中,Setter 后必须显式同步。 // 不能使用 ForceBuild,否则会覆盖色带目录手工写入的 256 个颜色表项。 pLookupTable->BuildSpecialColors(); pLookupTable->IndexedLookupOff(); pLookupTable->ResetAnnotations(); const QString sTitle = nmPebiResultPropertyDisplayText::getScalarBarTitle( sPropertyId, bLogarithmic); oCandidate.m_oSettings = oSettings; oCandidate.m_dBusinessRange[0] = dBusinessRange[0]; oCandidate.m_dBusinessRange[1] = dBusinessRange[1]; oCandidate.m_dVisibleRange[0] = dVisibleRange[0]; oCandidate.m_dVisibleRange[1] = dVisibleRange[1]; oCandidate.m_dInternalRange[0] = dInternalRange[0]; oCandidate.m_dInternalRange[1] = dInternalRange[1]; oCandidate.m_pLookupTable = pLookupTable; oCandidate.m_sScalarBarTitle = sTitle; oCandidate.m_bSingleValueMode = bSingleValueMode; return true; } QString chooseInitialProperty( const QSharedPointer& pSnapshot) { const QString sPressure = QString::fromLatin1("pressure"); if(!pSnapshot.isNull() && pSnapshot->hasProperty(sPressure) && pSnapshot->getPropertyFrameCount(sPressure) > 0) { return sPressure; } // 无压力仅是防御分支;按 Catalog 顺序选择第一个可用静态属性。 const QList listDescriptors = nmPebiResultPropertyCatalog::descriptors(); for(int nIndex = 0; nIndex < listDescriptors.size(); ++nIndex) { const nmPebiResultPropertyDescriptor& oDescriptor = listDescriptors[nIndex]; if(oDescriptor.m_eTimeType == NM_PebiResultProperty_Static && pSnapshot->hasProperty(oDescriptor.m_sPropertyId)) { return oDescriptor.m_sPropertyId; } } return QString(); } bool validateAdapterOutput( nmPebiResultSnapshotRenderAdapter* pAdapter, vtkIdType nExpectedCellCount, double dBounds[6]) { if(pAdapter == nullptr || dBounds == nullptr || pAdapter->getCellCount() != nExpectedCellCount || nExpectedCellCount <= 0) { return false; } vtkAlgorithmOutput* pOutputPort = pAdapter->getOutputPort(); if(pOutputPort == nullptr || pOutputPort->GetProducer() == nullptr || pOutputPort->GetIndex() < 0) { return false; } vtkAlgorithm* pProducer = pOutputPort->GetProducer(); pProducer->Update(pOutputPort->GetIndex()); vtkDataSet* pDataSet = vtkDataSet::SafeDownCast( pProducer->GetOutputDataObject(pOutputPort->GetIndex())); if(pDataSet == nullptr || pDataSet->GetNumberOfCells() != nExpectedCellCount || !getFiniteBounds(pDataSet, dBounds)) { return false; } const QByteArray aScalarName = pAdapter->getCurrentScalarArrayName().toUtf8(); vtkDataArray* pScalars = aScalarName.isEmpty() ? nullptr : pDataSet->GetCellData()->GetArray(aScalarName.constData()); if(pScalars == nullptr || pScalars->GetNumberOfComponents() != 1 || pScalars->GetNumberOfTuples() != nExpectedCellCount) { return false; } double dActualRange[2] = { 0.0, 0.0 }; pScalars->GetRange(dActualRange); return vtkMath::IsFinite(dActualRange[0]) && vtkMath::IsFinite(dActualRange[1]) && dActualRange[0] <= dActualRange[1]; } } class nmPebiResultSceneControllerPrivate { public: explicit nmPebiResultSceneControllerPrivate( nmWxVTKRenderContainerWidget* pContainer) : m_pContainer(pContainer), m_pMeshLayer(nullptr), m_pScalarBarLayer(nullptr), m_pScaleGridLayer(nullptr), m_pWellLayer(nullptr), m_pAdapter(nullptr), m_nCurrentFrameIndex(-1), m_nSnapshotBindingRevision(0), m_nViewportWidth(0), m_nViewportHeight(0), m_bHasPropertyRange(false), m_bHasBounds(false), m_bCurrentSingleValueMode(false), m_bScalarBarVisible(true), m_bScaleGridVisible(false), m_bWellNamesVisible(true) { m_dBusinessRange[0] = 0.0; m_dBusinessRange[1] = 0.0; m_dDisplayRange[0] = 0.0; m_dDisplayRange[1] = 1.0; for(int nIndex = 0; nIndex < 6; ++nIndex) { m_dBounds[nIndex] = 0.0; } } nmVTKScene* getScene() const { return m_pContainer != nullptr ? m_pContainer->getScene() : nullptr; } bool synchronizeLayerPointers() { nmVTKScene* pScene = getScene(); bool bPointersIntact = pScene != nullptr; if(pScene == nullptr) { bPointersIntact = m_pMeshLayer == nullptr && m_pScalarBarLayer == nullptr && m_pScaleGridLayer == nullptr && m_pWellLayer == nullptr; m_pMeshLayer = nullptr; m_pScalarBarLayer = nullptr; m_pScaleGridLayer = nullptr; m_pWellLayer = nullptr; return bPointersIntact; } // Scene 是图层唯一所有者。这里只比较指针身份,不解引用可能已经由 // 外部 Scene::clear() 删除的旧地址;不一致时立即丢弃观察指针。 if(m_pMeshLayer != nullptr && pScene->getLayer(QString::fromLatin1(RESULT_MESH_LAYER_ID)) != m_pMeshLayer) { m_pMeshLayer = nullptr; bPointersIntact = false; } if(m_pScalarBarLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) != m_pScalarBarLayer) { m_pScalarBarLayer = nullptr; bPointersIntact = false; } if(m_pScaleGridLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) != m_pScaleGridLayer) { m_pScaleGridLayer = nullptr; bPointersIntact = false; } if(m_pWellLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_WELLS_LAYER_ID)) != m_pWellLayer) { m_pWellLayer = nullptr; bPointersIntact = false; } return bPointersIntact; } bool hasOwnedBaseLayers() const { if(!isResultSceneGuiThread()) { return false; } nmVTKScene* pScene = getScene(); return pScene != nullptr && m_pMeshLayer != nullptr && m_pScalarBarLayer != nullptr && m_pScaleGridLayer != nullptr && m_pWellLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_MESH_LAYER_ID)) == m_pMeshLayer && pScene->getLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) == m_pScalarBarLayer && pScene->getLayer( QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) == m_pScaleGridLayer && pScene->getLayer( QString::fromLatin1(RESULT_WELLS_LAYER_ID)) == m_pWellLayer; } void discardActiveDataIfLayersLost() { const bool bPointersIntact = synchronizeLayerPointers(); const bool bHasActiveState = m_pAdapter != nullptr || !m_pSnapshot.isNull(); if(bHasActiveState && (!bPointersIntact || !hasOwnedBaseLayers())) { // 外部已经清理业务 Scene 时无法继续兑现“当前快照可见”的状态, // 因此采用 fail-closed:释放逻辑状态,但绝不访问已删除的 Layer。 releaseActiveData(); } } bool ensureBaseLayers(bool& bCreatedMesh, bool& bCreatedScalarBar, bool& bCreatedScaleGrid, bool& bCreatedWells) { bCreatedMesh = false; bCreatedScalarBar = false; bCreatedScaleGrid = false; bCreatedWells = false; const bool bPointersIntact = synchronizeLayerPointers(); nmVTKScene* pScene = getScene(); vtkRenderer* pRenderer = m_pContainer != nullptr ? m_pContainer->getRenderer() : nullptr; if(pScene == nullptr || pRenderer == nullptr || (!bPointersIntact && m_pAdapter != nullptr)) { return false; } if(m_pMeshLayer == nullptr) { if(pScene->getLayer( QString::fromLatin1(RESULT_MESH_LAYER_ID)) != nullptr) { return false; } nmVTKMeshLayer* pLayer = new nmVTKMeshLayer( QString::fromLatin1(RESULT_MESH_LAYER_ID)); pLayer->setVisible(false); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pMeshLayer = pLayer; bCreatedMesh = true; m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor( 0.0, 0.0, 0.0); m_pMeshLayer->getActor()->GetProperty()->LightingOff(); } if(m_pScalarBarLayer == nullptr) { if(pScene->getLayer(QString::fromLatin1( RESULT_SCALAR_BAR_LAYER_ID)) != nullptr) { rollbackCreatedLayers( bCreatedMesh, false, false, false); return false; } nmVTKScalarBarLayer* pLayer = new nmVTKScalarBarLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)); pLayer->setVisible(false); if(m_nViewportWidth > 0 && m_nViewportHeight > 0 && !pLayer->setViewportSize( m_nViewportWidth, m_nViewportHeight)) { delete pLayer; rollbackCreatedLayers( bCreatedMesh, false, false, false); return false; } if(!pScene->addLayer(pLayer)) { delete pLayer; rollbackCreatedLayers( bCreatedMesh, false, false, false); return false; } m_pScalarBarLayer = pLayer; bCreatedScalarBar = true; } if(m_pScaleGridLayer == nullptr) { if(pScene->getLayer(QString::fromLatin1( RESULT_SCALE_GRID_LAYER_ID)) != nullptr) { rollbackCreatedLayers( bCreatedMesh, bCreatedScalarBar, false, false); return false; } nmVTKPlanarScaleGridLayer* pLayer = new nmVTKPlanarScaleGridLayer( QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)); pLayer->setVisible(false); if(!pScene->addLayer(pLayer)) { delete pLayer; rollbackCreatedLayers( bCreatedMesh, bCreatedScalarBar, false, false); return false; } m_pScaleGridLayer = pLayer; bCreatedScaleGrid = true; } if(m_pWellLayer == nullptr) { if(pScene->getLayer(QString::fromLatin1( RESULT_WELLS_LAYER_ID)) != nullptr) { rollbackCreatedLayers( bCreatedMesh, bCreatedScalarBar, bCreatedScaleGrid, false); return false; } nmPebiResultWellAnnotationLayer* pLayer = new nmPebiResultWellAnnotationLayer( QString::fromLatin1(RESULT_WELLS_LAYER_ID)); pLayer->setVisible(false); if(!pScene->addLayer(pLayer)) { delete pLayer; rollbackCreatedLayers( bCreatedMesh, bCreatedScalarBar, bCreatedScaleGrid, false); return false; } m_pWellLayer = pLayer; bCreatedWells = true; } return true; } void rollbackCreatedLayers(bool bCreatedMesh, bool bCreatedScalarBar, bool bCreatedScaleGrid, bool bCreatedWells) { synchronizeLayerPointers(); nmVTKScene* pScene = getScene(); if(pScene == nullptr) { return; } if(bCreatedWells && m_pWellLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_WELLS_LAYER_ID)) == m_pWellLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_WELLS_LAYER_ID)); } if(bCreatedWells) { m_pWellLayer = nullptr; } if(bCreatedScaleGrid && m_pScaleGridLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) == m_pScaleGridLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)); } if(bCreatedScaleGrid) { m_pScaleGridLayer = nullptr; } if(bCreatedScalarBar && m_pScalarBarLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) == m_pScalarBarLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)); } if(bCreatedScalarBar) { m_pScalarBarLayer = nullptr; } if(bCreatedMesh && m_pMeshLayer != nullptr && pScene->getLayer(QString::fromLatin1(RESULT_MESH_LAYER_ID)) == m_pMeshLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_MESH_LAYER_ID)); } if(bCreatedMesh) { m_pMeshLayer = nullptr; } } bool bindMeshAdapter(nmPebiResultSnapshotRenderAdapter* pAdapter, vtkLookupTable* pLookupTable) { if(m_pMeshLayer == nullptr || pAdapter == nullptr || pLookupTable == nullptr) { return false; } // MeshLayer 在触碰旧 Mapper 前完成全部校验,失败不会破坏旧画面。 return m_pMeshLayer->setInputConnection( pAdapter->getOutputPort(), pAdapter->getCurrentScalarArrayName(), nmVTKMeshLayer::ScalarAssociation_CellData, pLookupTable); } void applyScalarBarLayout() { if(m_pScalarBarLayer == nullptr) { return; } if(m_nViewportWidth > 0 && m_nViewportHeight > 0) { m_pScalarBarLayer->setViewportSize( m_nViewportWidth, m_nViewportHeight); } const int nLabelCount = m_bCurrentSingleValueMode ? 0 : m_pScalarBarLayer->getResponsiveLabelCount(); m_pScalarBarLayer->setNumberOfLabels(nLabelCount); } void applyScalarBarState(const ResultDisplayCandidate& oDisplay) { if(m_pScalarBarLayer == nullptr) { return; } m_pScalarBarLayer->setLookupTable(oDisplay.m_pLookupTable); m_pScalarBarLayer->setTitle(oDisplay.m_sScalarBarTitle); m_pScalarBarLayer->setLabelFormat(QString::fromLatin1("%.4g")); m_bCurrentSingleValueMode = oDisplay.m_bSingleValueMode; m_pScalarBarLayer->setConstantValueLabel( oDisplay.m_bSingleValueMode, oDisplay.m_bSingleValueMode ? QString::number(oDisplay.m_dBusinessRange[0], 'g', 6) : QString()); applyScalarBarLayout(); } void applyLayerVisibility(bool bHasData) { synchronizeLayerPointers(); if(m_pMeshLayer != nullptr) { // 结果网格固定显示彩色表面和黑色单元边线,不保存可关闭的偏好。 m_pMeshLayer->setDisplayMode( nmVTKMeshLayer::DisplayMode_SurfaceWithEdges); m_pMeshLayer->setScalarVisibility(bHasData); // 彩色结果网格是有效快照的主内容,不提供独立隐藏状态。 m_pMeshLayer->setVisible(bHasData); } if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setVisible( bHasData && m_bScalarBarVisible); } if(m_pScaleGridLayer != nullptr) { m_pScaleGridLayer->setVisible( bHasData && m_bScaleGridVisible); } if(m_pWellLayer != nullptr) { // 井图层只包含文字和引线,由 SceneController 统一控制顶层 Prop。 m_pWellLayer->setVisible( bHasData && m_bWellNamesVisible); } if(m_pContainer != nullptr) { m_pContainer->setRotationCenterPickTarget( bHasData && m_pMeshLayer != nullptr ? m_pMeshLayer->getActor() : nullptr); } } bool restorePreviousScene() { synchronizeLayerPointers(); if(!hasOwnedBaseLayers() || m_pContainer == nullptr) { return false; } if(m_pAdapter == nullptr || m_pSnapshot.isNull()) { m_pMeshLayer->clearInput(); m_pContainer->clearPrimaryViewBounds(); if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setLookupTable(nullptr); m_pScalarBarLayer->setTitle(QString()); m_pScalarBarLayer->setConstantValueLabel(false, QString()); m_bCurrentSingleValueMode = false; applyScalarBarLayout(); } applyLayerVisibility(false); return true; } const bool bMeshRestored = m_pMeshLayer->getLookupTable() != nullptr && bindMeshAdapter(m_pAdapter, m_pMeshLayer->getLookupTable()); bool bGridRestored = !m_bHasBounds; bool bBoundsRestored = !m_bHasBounds; if(m_bHasBounds && m_pScaleGridLayer != nullptr) { bGridRestored = m_pScaleGridLayer->setBounds( m_dBounds[0], m_dBounds[1], m_dBounds[2], m_dBounds[3], m_dBounds[4]); bBoundsRestored = m_pContainer->setPrimaryViewBounds(m_dBounds); } if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setLookupTable( m_pMeshLayer->getLookupTable()); } applyLayerVisibility(true); return bMeshRestored && bGridRestored && bBoundsRestored; } void releaseActiveData() { // Scene 可能已由外部清空;先按稳定 ID 解除所有悬空观察指针。 synchronizeLayerPointers(); if(m_pContainer != nullptr) { m_pContainer->setRotationCenterPickTarget(nullptr); m_pContainer->clearPrimaryViewBounds(); } if(m_pMeshLayer != nullptr) { m_pMeshLayer->setScalarVisibility(false); m_pMeshLayer->clearInput(); m_pMeshLayer->setVisible(false); } if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setVisible(false); m_pScalarBarLayer->setLookupTable(nullptr); m_pScalarBarLayer->setTitle(QString()); m_pScalarBarLayer->setConstantValueLabel(false, QString()); } m_bCurrentSingleValueMode = false; applyScalarBarLayout(); if(m_pScaleGridLayer != nullptr) { m_pScaleGridLayer->setVisible(false); } if(m_pWellLayer != nullptr) { m_pWellLayer->setVisible(false); m_pWellLayer->clear(); } delete m_pAdapter; m_pAdapter = nullptr; m_pSnapshot.clear(); m_listAvailablePropertyIds.clear(); m_sCurrentPropertyId.clear(); m_nCurrentFrameIndex = -1; m_bHasPropertyRange = false; m_bHasBounds = false; // clear()保留窗口样式,但任何活动快照相关的手工范围都必须失效。 QMap::iterator oIterator = m_mapDisplaySettings.begin(); for(; oIterator != m_mapDisplaySettings.end(); ++oIterator) { oIterator.value().m_bHasManualRange = false; oIterator.value().m_dManualMinimum = 0.0; oIterator.value().m_dManualMaximum = 0.0; } } void removeOwnedLayers() { synchronizeLayerPointers(); nmVTKScene* pScene = getScene(); if(pScene == nullptr) { return; } // 只删除仍与本控制器记录身份一致的图层,绝不误删同 ID 的外部替代层。 if(m_pWellLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_WELLS_LAYER_ID)) == m_pWellLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_WELLS_LAYER_ID)); } m_pWellLayer = nullptr; if(m_pScaleGridLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) == m_pScaleGridLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)); } m_pScaleGridLayer = nullptr; if(m_pScalarBarLayer != nullptr && pScene->getLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) == m_pScalarBarLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)); } m_pScalarBarLayer = nullptr; if(m_pMeshLayer != nullptr && pScene->getLayer(QString::fromLatin1(RESULT_MESH_LAYER_ID)) == m_pMeshLayer) { pScene->removeLayer( QString::fromLatin1(RESULT_MESH_LAYER_ID)); } m_pMeshLayer = nullptr; } nmWxVTKRenderContainerWidget* m_pContainer; ///< 非所有权指针。 nmVTKMeshLayer* m_pMeshLayer; ///< Scene 所有。 nmVTKScalarBarLayer* m_pScalarBarLayer; ///< Scene 所有。 nmVTKPlanarScaleGridLayer* m_pScaleGridLayer; ///< Scene 所有。 nmPebiResultWellAnnotationLayer* m_pWellLayer; ///< Scene 所有。 nmPebiResultSnapshotRenderAdapter* m_pAdapter; ///< 控制器独占。 QSharedPointer m_pSnapshot; QStringList m_listAvailablePropertyIds; QString m_sCurrentPropertyId; /** @brief 按稳定属性 ID 保存的窗口级样式;clear 不删除。 */ QMap m_mapDisplaySettings; int m_nCurrentFrameIndex; /** @brief 不同快照对象完整提交成功后的单调序号,clear 和失败均不回退。 */ quint64 m_nSnapshotBindingRevision; int m_nViewportWidth; ///< 最近一次有效 QVTK 视口像素宽度。 int m_nViewportHeight; ///< 最近一次有效 QVTK 视口像素高度。 double m_dBusinessRange[2]; double m_dDisplayRange[2]; double m_dBounds[6]; bool m_bHasPropertyRange; bool m_bHasBounds; bool m_bCurrentSingleValueMode; ///< 当前是否为自动范围下的单值纯色模式。 bool m_bScalarBarVisible; bool m_bScaleGridVisible; bool m_bWellNamesVisible; }; nmPebiResultSceneController::nmPebiResultSceneController( nmWxVTKRenderContainerWidget* pContainer) : m_pPrivate(new nmPebiResultSceneControllerPrivate(pContainer)) { Q_ASSERT(isResultSceneGuiThread()); } nmPebiResultSceneController::~nmPebiResultSceneController() { Q_ASSERT(isResultSceneGuiThread()); if(m_pPrivate != nullptr) { m_pPrivate->releaseActiveData(); // Controller 结束后立即移除自己的业务图层,使同一容器可安全创建新实例。 m_pPrivate->removeOwnedLayers(); m_pPrivate->m_pContainer = nullptr; delete m_pPrivate; m_pPrivate = nullptr; } } bool nmPebiResultSceneController::setSnapshot( const QSharedPointer& pSnapshot, QString* pError) { Q_ASSERT(isResultSceneGuiThread()); if(pError != nullptr) { pError->clear(); } if(!isResultSceneGuiThread() || m_pPrivate == nullptr || m_pPrivate->m_pContainer == nullptr || pSnapshot.isNull() || !pSnapshot->isComplete()) { if(pError != nullptr) { *pError = controllerText("The result snapshot is invalid."); } return false; } m_pPrivate->discardActiveDataIfLayersLost(); if(m_pPrivate->m_pSnapshot == pSnapshot && m_pPrivate->m_pSnapshot->getSnapshotId() == pSnapshot->getSnapshotId()) { return true; } const QString sInitialProperty = chooseInitialProperty(pSnapshot); nmPebiResultPropertyDescriptor oInitialDescriptor; if(sInitialProperty.isEmpty() || !nmPebiResultPropertyCatalog::findDescriptor( sInitialProperty, oInitialDescriptor) || oInitialDescriptor.m_eAssociation != NM_PebiResultPropertyAssociation_CellData) { if(pError != nullptr) { *pError = controllerText( "The result snapshot has no renderable property."); } return false; } nmPebiResultSnapshotRenderAdapter* pCandidateAdapter = new nmPebiResultSnapshotRenderAdapter(); if(!pCandidateAdapter->initialize( pSnapshot, nmPebiResultSnapshotRenderAdapter::SharedPropertyArray) || !pCandidateAdapter->setPropertyFrame(sInitialProperty, 0)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "Unable to initialize the result render adapter."); } return false; } // 新快照只继承样式;所有属性的手工范围都在候选表中先清除。 QMap mapCandidateSettings = m_pPrivate->m_mapDisplaySettings; QMap::iterator oSettingsIterator = mapCandidateSettings.begin(); for(; oSettingsIterator != mapCandidateSettings.end(); ++oSettingsIterator) { oSettingsIterator.value().m_bHasManualRange = false; oSettingsIterator.value().m_dManualMinimum = 0.0; oSettingsIterator.value().m_dManualMaximum = 0.0; oSettingsIterator.value() = oSettingsIterator.value().normalized(); } const QStringList listCandidatePropertyIds = pSnapshot->getAvailablePropertyIds(); QString sDisplayError; for(int nPropertyIndex = 0; nPropertyIndex < listCandidatePropertyIds.size(); ++nPropertyIndex) { const QString& sPropertyId = listCandidatePropertyIds[nPropertyIndex]; nmPebiResultDisplaySettings oSettings = mapCandidateSettings.value( sPropertyId, nmPebiResultDisplaySettings()).normalized(); double dPropertyRange[2] = { 0.0, 0.0 }; if(!pSnapshot->getPropertyRange(sPropertyId, dPropertyRange)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "The result property range is invalid."); } return false; } if(oSettings.m_eScaleMode == NM_PebiColorScale_Logarithmic && dPropertyRange[0] <= 0.0) { // 当前快照不支持原 Log 时永久降级,避免保留不可见的隐藏偏好。 oSettings.m_eScaleMode = NM_PebiColorScale_Linear; } mapCandidateSettings.insert(sPropertyId, oSettings); // 在触碰活动 Scene 前预检每个可用属性,避免隐藏的无效偏好延迟到切换时。 ResultDisplayCandidate oPropertyDisplayCandidate; if(!prepareDisplayCandidate( pSnapshot, sPropertyId, oSettings, oPropertyDisplayCandidate, &sDisplayError)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = sDisplayError; } return false; } } ResultDisplayCandidate oDisplayCandidate; if(!prepareDisplayCandidate( pSnapshot, sInitialProperty, mapCandidateSettings.value(sInitialProperty), oDisplayCandidate, &sDisplayError)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = sDisplayError; } return false; } double dBounds[6]; if(pCandidateAdapter->getCurrentScalarArrayName().isEmpty() || !validateAdapterOutput(pCandidateAdapter, pSnapshot->getResultCellCount(), dBounds)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "The result render data failed validation."); } return false; } bool bCreatedMesh = false; bool bCreatedScalarBar = false; bool bCreatedScaleGrid = false; bool bCreatedWells = false; if(!m_pPrivate->ensureBaseLayers( bCreatedMesh, bCreatedScalarBar, bCreatedScaleGrid, bCreatedWells)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "Unable to create the result render layers."); } return false; } if(!m_pPrivate->m_pWellLayer->prepareCandidate(pSnapshot)) { m_pPrivate->rollbackCreatedLayers( bCreatedMesh, bCreatedScalarBar, bCreatedScaleGrid, bCreatedWells); delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "The result render data failed validation."); } return false; } bool bCommitted = m_pPrivate->m_pScaleGridLayer->setBounds( dBounds[0], dBounds[1], dBounds[2], dBounds[3], dBounds[4]) && m_pPrivate->m_pContainer->setPrimaryViewBounds(dBounds) && m_pPrivate->bindMeshAdapter( pCandidateAdapter, oDisplayCandidate.m_pLookupTable); if(!bCommitted) { m_pPrivate->m_pWellLayer->discardCandidate(); const bool bRestored = m_pPrivate->restorePreviousScene(); if(!bRestored) { // 不允许 Release 构建留下“逻辑有快照、实际无输入”的半恢复状态。 m_pPrivate->releaseActiveData(); } m_pPrivate->rollbackCreatedLayers( bCreatedMesh, bCreatedScalarBar, bCreatedScaleGrid, bCreatedWells); delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "Unable to commit the result render data."); } return false; } // prepareCandidate 成功后,commitCandidate 仅交换 Assembly 子项且没有失败分支。 m_pPrivate->m_pWellLayer->commitCandidate(); nmPebiResultSnapshotRenderAdapter* pPreviousAdapter = m_pPrivate->m_pAdapter; m_pPrivate->m_pAdapter = pCandidateAdapter; m_pPrivate->m_pSnapshot = pSnapshot; m_pPrivate->m_listAvailablePropertyIds = listCandidatePropertyIds; m_pPrivate->m_sCurrentPropertyId = sInitialProperty; m_pPrivate->m_mapDisplaySettings = mapCandidateSettings; m_pPrivate->m_nCurrentFrameIndex = 0; m_pPrivate->m_dBusinessRange[0] = oDisplayCandidate.m_dBusinessRange[0]; m_pPrivate->m_dBusinessRange[1] = oDisplayCandidate.m_dBusinessRange[1]; m_pPrivate->m_dDisplayRange[0] = oDisplayCandidate.m_dVisibleRange[0]; m_pPrivate->m_dDisplayRange[1] = oDisplayCandidate.m_dVisibleRange[1]; copyBounds(dBounds, m_pPrivate->m_dBounds); m_pPrivate->m_bHasPropertyRange = true; m_pPrivate->m_bHasBounds = true; m_pPrivate->applyScalarBarState(oDisplayCandidate); m_pPrivate->applyLayerVisibility(true); // Mapper 已不再引用旧 Producer,此时才允许释放旧 Adapter。 delete pPreviousAdapter; const bool bViewFitted = m_pPrivate->m_pContainer->fitTopViewWithoutRender(); const bool bCenterReset = m_pPrivate->m_pContainer->resetRotationCenterWithoutRender(); Q_ASSERT(bViewFitted && bCenterReset); Q_UNUSED(bViewFitted); Q_UNUSED(bCenterReset); // showReadyState 是本提交路径唯一的 Render 入口。 m_pPrivate->m_pContainer->showReadyState(); // 候选管线、相机和首次 Render 已完整提交,最后才发布新绑定序号。 ++m_pPrivate->m_nSnapshotBindingRevision; return true; } void nmPebiResultSceneController::clear() { Q_ASSERT(isResultSceneGuiThread()); if(isResultSceneGuiThread() && m_pPrivate != nullptr) { m_pPrivate->releaseActiveData(); } } void nmPebiResultSceneController::setViewportSize( int nWidth, int nHeight) { Q_ASSERT(isResultSceneGuiThread()); if(!isResultSceneGuiThread() || m_pPrivate == nullptr || nWidth <= 0 || nHeight <= 0) { return; } m_pPrivate->discardActiveDataIfLayersLost(); if(m_pPrivate->m_nViewportWidth == nWidth && m_pPrivate->m_nViewportHeight == nHeight) { return; } m_pPrivate->m_nViewportWidth = nWidth; m_pPrivate->m_nViewportHeight = nHeight; // QVTKWidget 的 Resize 会负责绘制,这里只同步二维布局和刻度数量。 m_pPrivate->applyScalarBarLayout(); } bool nmPebiResultSceneController::applyPropertyFrame( const QString& sPropertyId, int nFrameIndex, QString* pError) { Q_ASSERT(isResultSceneGuiThread()); if(pError != nullptr) { pError->clear(); } if(isResultSceneGuiThread() && m_pPrivate != nullptr) { m_pPrivate->discardActiveDataIfLayersLost(); } if(!isResultSceneGuiThread() || m_pPrivate == nullptr || m_pPrivate->m_pAdapter == nullptr || m_pPrivate->m_pSnapshot.isNull() || !m_pPrivate->hasOwnedBaseLayers() || !m_pPrivate->m_pSnapshot->hasProperty(sPropertyId) || nFrameIndex < 0 || nFrameIndex >= m_pPrivate->m_pSnapshot->getPropertyFrameCount( sPropertyId)) { if(pError != nullptr) { *pError = controllerText( "The requested result property or frame is invalid."); } return false; } nmPebiResultPropertyDescriptor oDescriptor; if(!nmPebiResultPropertyCatalog::findDescriptor( sPropertyId, oDescriptor) || oDescriptor.m_eAssociation != NM_PebiResultPropertyAssociation_CellData || (oDescriptor.m_eTimeType == NM_PebiResultProperty_Static && nFrameIndex != 0)) { if(pError != nullptr) { *pError = controllerText( "The requested result property or frame is invalid."); } return false; } if(sPropertyId == m_pPrivate->m_sCurrentPropertyId && nFrameIndex == m_pPrivate->m_nCurrentFrameIndex) { return true; } const nmPebiResultDisplaySettings oTargetSettings = m_pPrivate->m_mapDisplaySettings.value( sPropertyId, nmPebiResultDisplaySettings()); ResultDisplayCandidate oDisplayCandidate; if(!prepareDisplayCandidate( m_pPrivate->m_pSnapshot, sPropertyId, oTargetSettings, oDisplayCandidate, pError)) { return false; } const QString sPreviousProperty = m_pPrivate->m_sCurrentPropertyId; const int nPreviousFrame = m_pPrivate->m_nCurrentFrameIndex; bool bApplied = m_pPrivate->m_pAdapter->setPropertyFrame( sPropertyId, nFrameIndex); double dCurrentBounds[6]; if(bApplied) { bApplied = validateAdapterOutput( m_pPrivate->m_pAdapter, m_pPrivate->m_pSnapshot->getResultCellCount(), dCurrentBounds) && m_pPrivate->m_pMeshLayer->setScalarArray( m_pPrivate->m_pAdapter->getCurrentScalarArrayName(), nmVTKMeshLayer::ScalarAssociation_CellData, oDisplayCandidate.m_pLookupTable); } if(!bApplied) { const bool bAdapterRestored = m_pPrivate->m_pAdapter->setPropertyFrame( sPreviousProperty, nPreviousFrame); if(!bAdapterRestored) { // 极端恢复失败时清空逻辑和 VTK 状态,保证查询结果与实际画面一致。 m_pPrivate->releaseActiveData(); } if(pError != nullptr) { *pError = controllerText( "Unable to apply the result property or frame."); } return false; } m_pPrivate->m_sCurrentPropertyId = sPropertyId; m_pPrivate->m_nCurrentFrameIndex = nFrameIndex; m_pPrivate->m_mapDisplaySettings.insert( sPropertyId, oDisplayCandidate.m_oSettings); m_pPrivate->m_dBusinessRange[0] = oDisplayCandidate.m_dBusinessRange[0]; m_pPrivate->m_dBusinessRange[1] = oDisplayCandidate.m_dBusinessRange[1]; m_pPrivate->m_dDisplayRange[0] = oDisplayCandidate.m_dVisibleRange[0]; m_pPrivate->m_dDisplayRange[1] = oDisplayCandidate.m_dVisibleRange[1]; m_pPrivate->m_bHasPropertyRange = true; m_pPrivate->applyScalarBarState(oDisplayCandidate); m_pPrivate->m_pContainer->renderView(); return true; } bool nmPebiResultSceneController::applyCurrentDisplaySettings( quint64 nExpectedBindingRevision, const QString& sExpectedSnapshotId, const QString& sExpectedPropertyId, const nmPebiResultDisplaySettings& oSettings, QString* pError) { Q_ASSERT(isResultSceneGuiThread()); if(pError != nullptr) { pError->clear(); } if(isResultSceneGuiThread() && m_pPrivate != nullptr) { m_pPrivate->discardActiveDataIfLayersLost(); } // 身份核对和实际提交处在同一个 GUI 线程调用中,旧对话框不能污染新快照。 if(!isResultSceneGuiThread() || !hasSnapshot() || nExpectedBindingRevision == 0 || nExpectedBindingRevision != getSnapshotBindingRevision() || sExpectedSnapshotId.isEmpty() || sExpectedPropertyId.isEmpty() || sExpectedSnapshotId != getCurrentSnapshotId() || sExpectedPropertyId != m_pPrivate->m_sCurrentPropertyId) { if(pError != nullptr) { *pError = controllerText( "The color settings no longer match the current result."); } return false; } ResultDisplayCandidate oDisplayCandidate; if(!prepareDisplayCandidate( m_pPrivate->m_pSnapshot, sExpectedPropertyId, oSettings, oDisplayCandidate, pError)) { return false; } const nmPebiResultDisplaySettings oCurrentSettings = m_pPrivate->m_mapDisplaySettings.value( sExpectedPropertyId, nmPebiResultDisplaySettings()); if(oDisplayCandidate.m_oSettings == oCurrentSettings.normalized()) { return true; } // MeshLayer 先完整验证候选 LUT;失败时旧 Mapper 和 ScalarBar 仍共享旧 LUT。 if(!m_pPrivate->m_pMeshLayer->setLookupTable( oDisplayCandidate.m_pLookupTable)) { if(pError != nullptr) { *pError = controllerText("Unable to apply the color settings."); } return false; } m_pPrivate->m_mapDisplaySettings.insert( sExpectedPropertyId, oDisplayCandidate.m_oSettings); m_pPrivate->m_dBusinessRange[0] = oDisplayCandidate.m_dBusinessRange[0]; m_pPrivate->m_dBusinessRange[1] = oDisplayCandidate.m_dBusinessRange[1]; m_pPrivate->m_dDisplayRange[0] = oDisplayCandidate.m_dVisibleRange[0]; m_pPrivate->m_dDisplayRange[1] = oDisplayCandidate.m_dVisibleRange[1]; m_pPrivate->m_bHasPropertyRange = true; m_pPrivate->applyScalarBarState(oDisplayCandidate); m_pPrivate->m_pContainer->renderView(); return true; } void nmPebiResultSceneController::setScalarBarVisible(bool bVisible) { Q_ASSERT(isResultSceneGuiThread()); if(!isResultSceneGuiThread() || m_pPrivate == nullptr) { return; } m_pPrivate->discardActiveDataIfLayersLost(); if(m_pPrivate->m_bScalarBarVisible == bVisible) { return; } m_pPrivate->m_bScalarBarVisible = bVisible; const bool bHasData = hasSnapshot(); m_pPrivate->applyLayerVisibility(bHasData); if(bHasData) { m_pPrivate->m_pContainer->renderView(); } } void nmPebiResultSceneController::setScaleGridVisible(bool bVisible) { Q_ASSERT(isResultSceneGuiThread()); if(!isResultSceneGuiThread() || m_pPrivate == nullptr) { return; } m_pPrivate->discardActiveDataIfLayersLost(); if(m_pPrivate->m_bScaleGridVisible == bVisible) { return; } m_pPrivate->m_bScaleGridVisible = bVisible; const bool bHasData = hasSnapshot(); m_pPrivate->applyLayerVisibility(bHasData); if(bHasData) { m_pPrivate->m_pContainer->renderView(); } } void nmPebiResultSceneController::setWellNamesVisible(bool bVisible) { Q_ASSERT(isResultSceneGuiThread()); if(!isResultSceneGuiThread() || m_pPrivate == nullptr) { return; } m_pPrivate->discardActiveDataIfLayersLost(); if(m_pPrivate->m_bWellNamesVisible == bVisible) { return; } m_pPrivate->m_bWellNamesVisible = bVisible; const bool bHasData = hasSnapshot(); m_pPrivate->applyLayerVisibility(bHasData); if(bHasData) { m_pPrivate->m_pContainer->renderView(); } } bool nmPebiResultSceneController::hasSnapshot() const { return isResultSceneGuiThread() && m_pPrivate != nullptr && m_pPrivate->m_pAdapter != nullptr && !m_pPrivate->m_pSnapshot.isNull() && m_pPrivate->hasOwnedBaseLayers() && m_pPrivate->m_pWellLayer->hasActiveAnnotations(); } quint64 nmPebiResultSceneController::getSnapshotBindingRevision() const { return m_pPrivate != nullptr ? m_pPrivate->m_nSnapshotBindingRevision : 0; } QString nmPebiResultSceneController::getCurrentSnapshotId() const { return hasSnapshot() ? m_pPrivate->m_pSnapshot->getSnapshotId() : QString(); } QStringList nmPebiResultSceneController::getAvailablePropertyIds() const { return hasSnapshot() ? m_pPrivate->m_listAvailablePropertyIds : QStringList(); } QString nmPebiResultSceneController::getCurrentPropertyId() const { return hasSnapshot() ? m_pPrivate->m_sCurrentPropertyId : QString(); } int nmPebiResultSceneController::getCurrentFrameIndex() const { return hasSnapshot() ? m_pPrivate->m_nCurrentFrameIndex : -1; } bool nmPebiResultSceneController::isPropertyTimeDependent( const QString& sPropertyId) const { if(!hasSnapshot() || !m_pPrivate->m_pSnapshot->hasProperty(sPropertyId)) { return false; } nmPebiResultPropertyDescriptor oDescriptor; return nmPebiResultPropertyCatalog::findDescriptor( sPropertyId, oDescriptor) && oDescriptor.m_eTimeType == NM_PebiResultProperty_Dynamic; } int nmPebiResultSceneController::getPropertyFrameCount( const QString& sPropertyId) const { return hasSnapshot() ? m_pPrivate->m_pSnapshot->getPropertyFrameCount(sPropertyId) : 0; } bool nmPebiResultSceneController::getPropertyTimeAt( const QString& sPropertyId, int nFrameIndex, double& dTime) const { // 快照接口保证失败时不写输出参数,这里直接保持该事务语义。 return hasSnapshot() && m_pPrivate->m_pSnapshot->getPropertyTimeAt( sPropertyId, nFrameIndex, dTime); } bool nmPebiResultSceneController::getCurrentPropertyRange( double dRange[2]) const { if(!hasSnapshot() || dRange == nullptr || !m_pPrivate->m_bHasPropertyRange) { return false; } dRange[0] = m_pPrivate->m_dBusinessRange[0]; dRange[1] = m_pPrivate->m_dBusinessRange[1]; return true; } bool nmPebiResultSceneController::getCurrentDisplayRange( double dRange[2]) const { if(!hasSnapshot() || dRange == nullptr || !m_pPrivate->m_bHasPropertyRange) { return false; } dRange[0] = m_pPrivate->m_dDisplayRange[0]; dRange[1] = m_pPrivate->m_dDisplayRange[1]; return true; } bool nmPebiResultSceneController::getCurrentDisplaySettings( nmPebiResultDisplaySettings& oSettings) const { if(!hasSnapshot() || !m_pPrivate->m_mapDisplaySettings.contains( m_pPrivate->m_sCurrentPropertyId)) { return false; } oSettings = m_pPrivate->m_mapDisplaySettings.value( m_pPrivate->m_sCurrentPropertyId); return true; } bool nmPebiResultSceneController::isScalarBarVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bScalarBarVisible; } bool nmPebiResultSceneController::isScaleGridVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bScaleGridVisible; } bool nmPebiResultSceneController::areWellNamesVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bWellNamesVisible; }