#include "nmPebiResultSceneController.h" #include "nmPebiResultPropertyCatalog.h" #include "nmPebiResultSnapshot.h" #include "nmPebiResultSnapshotRenderAdapter.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.") }; 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 getDisplayRange(const double dBusinessRange[2], double dDisplayRange[2]) { if(dBusinessRange == nullptr || dDisplayRange == nullptr || !vtkMath::IsFinite(dBusinessRange[0]) || !vtkMath::IsFinite(dBusinessRange[1]) || dBusinessRange[0] > dBusinessRange[1]) { return false; } dDisplayRange[0] = dBusinessRange[0]; dDisplayRange[1] = dBusinessRange[1]; if(dBusinessRange[0] < dBusinessRange[1]) { return true; } // VTK 需要非零显示区间;业务层仍保存并返回原始常量范围 [c,c]。 const double dValue = dBusinessRange[0]; const double dEpsilon = qMax(1.0, fabs(dValue)) * 1.0e-6; double dMinimum = dValue - dEpsilon; double dMaximum = dValue + dEpsilon; if(!vtkMath::IsFinite(dMinimum)) { dMinimum = dValue; } if(!vtkMath::IsFinite(dMaximum)) { dMaximum = dValue; } if(dMinimum >= dMaximum) { return false; } dDisplayRange[0] = dMinimum; dDisplayRange[1] = dMaximum; return true; } bool getValidatedPropertyRanges( const QSharedPointer& pSnapshot, const QString& sPropertyId, double dBusinessRange[2], double dDisplayRange[2]) { return !pSnapshot.isNull() && pSnapshot->getPropertyRange(sPropertyId, dBusinessRange) && getDisplayRange(dBusinessRange, dDisplayRange); } 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 areSnapshotWellsValid( const QSharedPointer& pSnapshot) { if(pSnapshot.isNull()) { return false; } QSet setWellIds; for(int nIndex = 0; nIndex < pSnapshot->getWellCount(); ++nIndex) { const nmPebiResultWellSnapshot* pWell = pSnapshot->getWellAt(nIndex); if(pWell == nullptr || pWell->m_sWellInstanceId.isEmpty() || pWell->m_sWellCode.isEmpty() || !vtkMath::IsFinite(pWell->m_oLocation.x()) || !vtkMath::IsFinite(pWell->m_oLocation.y()) || setWellIds.contains(pWell->m_sWellInstanceId)) { return false; } setWellIds.insert(pWell->m_sWellInstanceId); } return true; } 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_pAdapter(nullptr), m_nCurrentFrameIndex(-1), m_bHasPropertyRange(false), m_bHasBounds(false), m_bMeshVisible(true), // 5C 补齐属性名和单位标题前,避免显示语义不完整的色标。 m_bScalarBarVisible(false), m_bScaleGridVisible(false), m_bCellEdgesVisible(false) { 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; } // 5A 固定保留该 ID,井位图层在 5C 才创建。 Q_UNUSED(RESULT_WELLS_LAYER_ID); } 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_pMeshLayer = nullptr; m_pScalarBarLayer = nullptr; m_pScaleGridLayer = 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; } return bPointersIntact; } bool hasOwnedBaseLayers() const { if(!isResultSceneGuiThread()) { return false; } nmVTKScene* pScene = getScene(); return pScene != nullptr && m_pMeshLayer != nullptr && m_pScalarBarLayer != nullptr && m_pScaleGridLayer != 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; } 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) { bCreatedMesh = false; bCreatedScalarBar = false; bCreatedScaleGrid = 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); return false; } nmVTKScalarBarLayer* pLayer = new nmVTKScalarBarLayer( QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)); pLayer->setVisible(false); pLayer->setPosition(0.05, 0.1); pLayer->setSize(0.1, 0.8); if(!pScene->addLayer(pLayer)) { delete pLayer; rollbackCreatedLayers( bCreatedMesh, 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); 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); return false; } m_pScaleGridLayer = pLayer; bCreatedScaleGrid = true; } return true; } void rollbackCreatedLayers(bool bCreatedMesh, bool bCreatedScalarBar, bool bCreatedScaleGrid) { synchronizeLayerPointers(); nmVTKScene* pScene = getScene(); if(pScene == nullptr) { return; } 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, const double dDisplayRange[2]) { if(m_pMeshLayer == nullptr || pAdapter == nullptr || dDisplayRange == nullptr) { return false; } // MeshLayer 在触碰旧 Mapper 前完成全部校验,失败不会破坏旧画面。 return m_pMeshLayer->setInputConnection( pAdapter->getOutputPort(), pAdapter->getCurrentScalarArrayName(), nmVTKMeshLayer::ScalarAssociation_CellData, dDisplayRange[0], dDisplayRange[1]); } void applyLayerVisibility(bool bHasData) { synchronizeLayerPointers(); if(m_pMeshLayer != nullptr) { m_pMeshLayer->setDisplayMode(m_bCellEdgesVisible ? nmVTKMeshLayer::DisplayMode_SurfaceWithEdges : nmVTKMeshLayer::DisplayMode_Surface); m_pMeshLayer->setScalarVisibility(bHasData); m_pMeshLayer->setVisible(bHasData && m_bMeshVisible); } if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setVisible( bHasData && m_bScalarBarVisible); } if(m_pScaleGridLayer != nullptr) { m_pScaleGridLayer->setVisible( bHasData && m_bScaleGridVisible); } if(m_pContainer != nullptr) { m_pContainer->setRotationCenterPickTarget( bHasData && m_bMeshVisible && 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()); } applyLayerVisibility(false); return true; } const bool bMeshRestored = bindMeshAdapter( m_pAdapter, m_dDisplayRange); 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()); } if(m_pScaleGridLayer != nullptr) { m_pScaleGridLayer->setVisible(false); } delete m_pAdapter; m_pAdapter = nullptr; m_pSnapshot.clear(); m_listAvailablePropertyIds.clear(); m_sCurrentPropertyId.clear(); m_nCurrentFrameIndex = -1; m_bHasPropertyRange = false; m_bHasBounds = false; } void removeOwnedLayers() { synchronizeLayerPointers(); nmVTKScene* pScene = getScene(); if(pScene == nullptr) { return; } // 只删除仍与本控制器记录身份一致的图层,绝不误删同 ID 的外部替代层。 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 所有。 nmPebiResultSnapshotRenderAdapter* m_pAdapter; ///< 控制器独占。 QSharedPointer m_pSnapshot; QStringList m_listAvailablePropertyIds; QString m_sCurrentPropertyId; int m_nCurrentFrameIndex; double m_dBusinessRange[2]; double m_dDisplayRange[2]; double m_dBounds[6]; bool m_bHasPropertyRange; bool m_bHasBounds; bool m_bMeshVisible; bool m_bScalarBarVisible; bool m_bScaleGridVisible; bool m_bCellEdgesVisible; }; 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; } double dBusinessRange[2]; double dDisplayRange[2]; double dBounds[6]; if(pCandidateAdapter->getCurrentScalarArrayName().isEmpty() || !getValidatedPropertyRanges(pSnapshot, sInitialProperty, dBusinessRange, dDisplayRange) || !validateAdapterOutput(pCandidateAdapter, pSnapshot->getResultCellCount(), dBounds) || !areSnapshotWellsValid(pSnapshot)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "The result render data failed validation."); } return false; } bool bCreatedMesh = false; bool bCreatedScalarBar = false; bool bCreatedScaleGrid = false; if(!m_pPrivate->ensureBaseLayers( bCreatedMesh, bCreatedScalarBar, bCreatedScaleGrid)) { delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "Unable to create the result render layers."); } 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, dDisplayRange); if(!bCommitted) { const bool bRestored = m_pPrivate->restorePreviousScene(); if(!bRestored) { // 不允许 Release 构建留下“逻辑有快照、实际无输入”的半恢复状态。 m_pPrivate->releaseActiveData(); } m_pPrivate->rollbackCreatedLayers( bCreatedMesh, bCreatedScalarBar, bCreatedScaleGrid); delete pCandidateAdapter; if(pError != nullptr) { *pError = controllerText( "Unable to commit the result render data."); } return false; } nmPebiResultSnapshotRenderAdapter* pPreviousAdapter = m_pPrivate->m_pAdapter; m_pPrivate->m_pAdapter = pCandidateAdapter; m_pPrivate->m_pSnapshot = pSnapshot; m_pPrivate->m_listAvailablePropertyIds = pSnapshot->getAvailablePropertyIds(); m_pPrivate->m_sCurrentPropertyId = sInitialProperty; m_pPrivate->m_nCurrentFrameIndex = 0; m_pPrivate->m_dBusinessRange[0] = dBusinessRange[0]; m_pPrivate->m_dBusinessRange[1] = dBusinessRange[1]; m_pPrivate->m_dDisplayRange[0] = dDisplayRange[0]; m_pPrivate->m_dDisplayRange[1] = dDisplayRange[1]; copyBounds(dBounds, m_pPrivate->m_dBounds); m_pPrivate->m_bHasPropertyRange = true; m_pPrivate->m_bHasBounds = true; m_pPrivate->m_pScalarBarLayer->setLookupTable( m_pPrivate->m_pMeshLayer->getLookupTable()); 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(); return true; } void nmPebiResultSceneController::clear() { Q_ASSERT(isResultSceneGuiThread()); if(isResultSceneGuiThread() && m_pPrivate != nullptr) { m_pPrivate->releaseActiveData(); } } 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; } double dBusinessRange[2]; double dDisplayRange[2]; if(!getValidatedPropertyRanges( m_pPrivate->m_pSnapshot, sPropertyId, dBusinessRange, dDisplayRange)) { if(pError != nullptr) { *pError = controllerText( "The result property range is invalid."); } return false; } const QString sPreviousProperty = m_pPrivate->m_sCurrentPropertyId; const int nPreviousFrame = m_pPrivate->m_nCurrentFrameIndex; const double dPreviousDisplayRange[2] = { m_pPrivate->m_dDisplayRange[0], m_pPrivate->m_dDisplayRange[1] }; 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, dDisplayRange[0], dDisplayRange[1]); } if(!bApplied) { const bool bAdapterRestored = m_pPrivate->m_pAdapter->setPropertyFrame( sPreviousProperty, nPreviousFrame); const bool bMapperRestored = bAdapterRestored && m_pPrivate->m_pMeshLayer->setScalarArray( m_pPrivate->m_pAdapter->getCurrentScalarArrayName(), nmVTKMeshLayer::ScalarAssociation_CellData, dPreviousDisplayRange[0], dPreviousDisplayRange[1]); if(!bMapperRestored) { // 极端恢复失败时清空逻辑和 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_dBusinessRange[0] = dBusinessRange[0]; m_pPrivate->m_dBusinessRange[1] = dBusinessRange[1]; m_pPrivate->m_dDisplayRange[0] = dDisplayRange[0]; m_pPrivate->m_dDisplayRange[1] = dDisplayRange[1]; m_pPrivate->m_bHasPropertyRange = true; m_pPrivate->m_pContainer->renderView(); return true; } void nmPebiResultSceneController::setMeshVisible(bool bVisible) { Q_ASSERT(isResultSceneGuiThread()); if(!isResultSceneGuiThread() || m_pPrivate == nullptr) { return; } m_pPrivate->discardActiveDataIfLayersLost(); if(m_pPrivate->m_bMeshVisible == bVisible) { return; } m_pPrivate->m_bMeshVisible = bVisible; const bool bHasData = hasSnapshot(); m_pPrivate->applyLayerVisibility(bHasData); if(bHasData) { m_pPrivate->m_pContainer->renderView(); } } 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::setCellEdgesVisible(bool bVisible) { Q_ASSERT(isResultSceneGuiThread()); if(!isResultSceneGuiThread() || m_pPrivate == nullptr) { return; } m_pPrivate->discardActiveDataIfLayersLost(); if(m_pPrivate->m_bCellEdgesVisible == bVisible) { return; } m_pPrivate->m_bCellEdgesVisible = 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(); } 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::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::isMeshVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bMeshVisible; } 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::areCellEdgesVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bCellEdgesVisible; }