#include "nmPebiGridSceneController.h" #include "nmPebiGridViewData.h" #include "nmVTKMeshLayer.h" #include "nmVTKPlanarScaleGridLayer.h" #include "nmVTKScalarBarLayer.h" #include "nmVTKScene.h" #include "nmWxVTKRenderContainerWidget.h" #include #include #include #include #include #include namespace { QString controllerText(const char* pText) { return QCoreApplication::translate( "nmPebiGridSceneController", pText); } bool getFiniteGridBounds(vtkUnstructuredGrid* pGrid, double dBounds[6]) { if(pGrid == nullptr || dBounds == nullptr) { return false; } pGrid->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]; } } class nmPebiGridSceneControllerPrivate { public: explicit nmPebiGridSceneControllerPrivate( nmWxVTKRenderContainerWidget* pRenderContainer) : m_pContainer(pRenderContainer), m_pMeshLayer(nullptr), m_pScalarBarLayer(nullptr), m_pPlanarScaleGridLayer(nullptr), m_bGridLinesVisible(true), m_bScalarBarVisible(true), m_bBoundsGridVisible(false), m_bPropertySelectionExplicit(false), m_bHasData(false) { } bool ensureBaseLayers() { nmVTKScene* pScene = m_pContainer != nullptr ? m_pContainer->getScene() : nullptr; vtkRenderer* pRenderer = m_pContainer != nullptr ? m_pContainer->getRenderer() : nullptr; if(pScene == nullptr || pRenderer == nullptr) { return false; } if(m_pMeshLayer == nullptr) { nmVTKMeshLayer* pLayer = new nmVTKMeshLayer("pebi.grid.mesh"); pLayer->setVisible(false); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pMeshLayer = pLayer; // 无属性时使用稳定灰色;Material 有效时仍由 MeshLayer 的 LUT 着色。 m_pMeshLayer->getActor()->GetProperty()->SetColor( 211.0 / 255.0, 211.0 / 255.0, 211.0 / 255.0); m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor( 0.0, 0.0, 0.0); // 仅关闭当前 PEBI 预览 Actor 的光照,避免固定面色随视角变化。 m_pMeshLayer->getActor()->GetProperty()->LightingOff(); } if(m_pPlanarScaleGridLayer == nullptr) { nmVTKPlanarScaleGridLayer* pLayer = new nmVTKPlanarScaleGridLayer("pebi.grid.scale_grid"); pLayer->setVisible(false); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pPlanarScaleGridLayer = pLayer; } return true; } bool ensureScalarBarLayer() { if(m_pScalarBarLayer != nullptr) { return true; } nmVTKScene* pScene = m_pContainer != nullptr ? m_pContainer->getScene() : nullptr; if(pScene == nullptr || m_pMeshLayer == nullptr) { return false; } // 色标只在第一次选择有效标量时创建,并共享 MeshLayer 的 LUT。 nmVTKScalarBarLayer* pLayer = new nmVTKScalarBarLayer("pebi.grid.scalar_bar"); pLayer->setLookupTable(m_pMeshLayer->getLookupTable()); pLayer->setPosition(0.86, 0.12); pLayer->setSize(0.10, 0.76); pLayer->setVisible(false); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pScalarBarLayer = pLayer; return true; } bool applyProperty(const QString& sPropertyName) { if(m_pMeshLayer == nullptr) { return false; } if(sPropertyName.isEmpty()) { m_sPropertyName.clear(); m_pMeshLayer->setScalarVisibility(false); if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setVisible(false); } return true; } if(sPropertyName != "Material" || !m_oViewData.hasMaterial() || !m_pMeshLayer->setScalarArray( sPropertyName, nmVTKMeshLayer::ScalarAssociation_CellData)) { // 无效属性不能沿用上一网格的标量着色或色标范围。 m_sPropertyName.clear(); m_pMeshLayer->setScalarVisibility(false); if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setVisible(false); } return false; } if(!ensureScalarBarLayer()) { m_sPropertyName.clear(); m_pMeshLayer->setScalarVisibility(false); return false; } m_sPropertyName = sPropertyName; m_pMeshLayer->setScalarVisibility(true); m_pScalarBarLayer->setLookupTable( m_pMeshLayer->getLookupTable()); m_pScalarBarLayer->setTitle(sPropertyName); m_pScalarBarLayer->setVisible(m_bScalarBarVisible); return true; } bool bindViewData(const nmPebiGridViewData& oViewData, const QString& sRequestedProperty, const double dBounds[6]) { if(m_pMeshLayer == nullptr || m_pPlanarScaleGridLayer == nullptr || dBounds == nullptr) { return false; } // 本函数只提交已在外层验证过的数据,不渲染也不改变相机。 m_oViewData = oViewData; if(!m_pMeshLayer->setDataSet(oViewData.getGrid())) { m_oViewData.clear(); return false; } m_pMeshLayer->setEdgeVisibility(m_bGridLinesVisible); m_pMeshLayer->setVisible(true); if(!m_pPlanarScaleGridLayer->setBounds( dBounds[0], dBounds[1], dBounds[2], dBounds[3], dBounds[4])) { m_pMeshLayer->setDataSet(nullptr); m_pMeshLayer->setVisible(false); m_oViewData.clear(); return false; } m_pPlanarScaleGridLayer->setVisible(m_bBoundsGridVisible); m_bHasData = true; // 属性不可用只回退固定面色,不影响已经验证成功的网格几何提交。 applyProperty(sRequestedProperty); return true; } void releaseGridData() { // 先断开 Mapper 对旧网格的引用,再释放 ViewData 的共享引用。 if(m_pMeshLayer != nullptr) { m_pMeshLayer->setScalarVisibility(false); m_pMeshLayer->setDataSet(nullptr); m_pMeshLayer->setVisible(false); } if(m_pScalarBarLayer != nullptr) { m_pScalarBarLayer->setVisible(false); } if(m_pPlanarScaleGridLayer != nullptr) { m_pPlanarScaleGridLayer->setVisible(false); } m_oViewData.clear(); m_sPropertyName.clear(); m_bHasData = false; } nmWxVTKRenderContainerWidget* m_pContainer; ///< 非所有权指针。 nmVTKMeshLayer* m_pMeshLayer; ///< Scene 所有,控制器只观察。 nmVTKScalarBarLayer* m_pScalarBarLayer; ///< Scene 所有,按需创建。 nmVTKPlanarScaleGridLayer* m_pPlanarScaleGridLayer; ///< Scene 所有。 nmPebiGridViewData m_oViewData; bool m_bGridLinesVisible; bool m_bScalarBarVisible; ///< 用户偏好,无属性时仍保留。 bool m_bBoundsGridVisible; bool m_bPropertySelectionExplicit; ///< 是否已由业务界面明确选择属性。 bool m_bHasData; QString m_sRequestedPropertyName; ///< 显式选择偏好,可暂时不可用。 QString m_sPropertyName; }; nmPebiGridSceneController::nmPebiGridSceneController( nmWxVTKRenderContainerWidget* pContainer) : m_pPrivate(new nmPebiGridSceneControllerPrivate(pContainer)) { } nmPebiGridSceneController::~nmPebiGridSceneController() { if(m_pPrivate != nullptr) { // Layer 已归 Scene 所有;这里只解除数据和非所有权指针。 m_pPrivate->releaseGridData(); m_pPrivate->m_pContainer = nullptr; m_pPrivate->m_pMeshLayer = nullptr; m_pPrivate->m_pScalarBarLayer = nullptr; m_pPrivate->m_pPlanarScaleGridLayer = nullptr; delete m_pPrivate; m_pPrivate = nullptr; } } bool nmPebiGridSceneController::setViewData( const nmPebiGridViewData& oViewData, QString* pError) { if(pError != nullptr) { pError->clear(); } if(m_pPrivate == nullptr || m_pPrivate->m_pContainer == nullptr || !oViewData.isValid()) { if(pError != nullptr) { *pError = controllerText("The grid preview data is invalid."); } return false; } double dBounds[6]; if(!getFiniteGridBounds(oViewData.getGrid(), dBounds)) { if(pError != nullptr) { *pError = controllerText("The grid bounds are invalid."); } return false; } if(!m_pPrivate->ensureBaseLayers()) { if(pError != nullptr) { *pError = controllerText("Unable to create the grid render layers."); } return false; } QString sRequestedProperty; if(m_pPrivate->m_bPropertySelectionExplicit) { // 用户明确选择优先;即使属性暂时缺失,也保留偏好供后续网格恢复。 sRequestedProperty = m_pPrivate->m_sRequestedPropertyName; } else if(oViewData.hasMaterial()) { // 默认选择与“是否首次装入网格”无关,首次出现 Material 时同样生效。 sRequestedProperty = "Material"; } // 强引用保留上一份成功数据,提交任一步失败时可以完整恢复旧画面。 const bool bHadPreviousData = m_pPrivate->m_bHasData; const nmPebiGridViewData oPreviousViewData = m_pPrivate->m_oViewData; const QString sPreviousProperty = m_pPrivate->m_sPropertyName; double dPreviousBounds[6]; const bool bHasPreviousBounds = bHadPreviousData && getFiniteGridBounds(oPreviousViewData.getGrid(), dPreviousBounds); // 几何替换先解除旧输入,再共享新网格;Renderer 和图层对象保持不变。 m_pPrivate->releaseGridData(); if(!m_pPrivate->bindViewData(oViewData, sRequestedProperty, dBounds)) { // 回滚同样不渲染;外层失败状态会决定恢复旧画面还是显示错误页。 m_pPrivate->releaseGridData(); if(bHadPreviousData && bHasPreviousBounds) { m_pPrivate->bindViewData( oPreviousViewData, sPreviousProperty, dPreviousBounds); } if(pError != nullptr) { *pError = controllerText("Unable to bind the grid render data."); } return false; } // 几何变化始终恢复正交顶视图并适配;整批更新最终只在就绪入口渲染一次。 m_pPrivate->m_pContainer->fitTopViewWithoutRender(); m_pPrivate->m_pContainer->showReadyState(); return true; } void nmPebiGridSceneController::clear() { if(m_pPrivate == nullptr) { return; } m_pPrivate->releaseGridData(); m_pPrivate->m_sPropertyName.clear(); m_pPrivate->m_sRequestedPropertyName.clear(); m_pPrivate->m_bGridLinesVisible = true; m_pPrivate->m_bScalarBarVisible = true; m_pPrivate->m_bBoundsGridVisible = false; m_pPrivate->m_bPropertySelectionExplicit = false; } void nmPebiGridSceneController::setGridLinesVisible(bool bVisible) { if(m_pPrivate == nullptr) { return; } m_pPrivate->m_bGridLinesVisible = bVisible; if(m_pPrivate->m_pMeshLayer != nullptr) { m_pPrivate->m_pMeshLayer->setEdgeVisibility(bVisible); } if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != nullptr) { m_pPrivate->m_pContainer->renderView(); } } bool nmPebiGridSceneController::setProperty(const QString& sPropertyName) { if(m_pPrivate == nullptr || !m_pPrivate->m_bHasData) { return false; } const bool bSucceeded = m_pPrivate->applyProperty(sPropertyName); // 只有公开入口代表用户/业务明确选择,内部默认绑定不会污染该状态。 m_pPrivate->m_bPropertySelectionExplicit = true; m_pPrivate->m_sRequestedPropertyName = bSucceeded ? sPropertyName : QString(); if(m_pPrivate->m_pContainer != nullptr) { m_pPrivate->m_pContainer->renderView(); } return bSucceeded; } void nmPebiGridSceneController::setScalarBarVisible(bool bVisible) { if(m_pPrivate == nullptr) { return; } // 无属性时只保存偏好;重新选择 Material 后按该偏好恢复色标。 m_pPrivate->m_bScalarBarVisible = bVisible; if(m_pPrivate->m_pScalarBarLayer != nullptr) { m_pPrivate->m_pScalarBarLayer->setVisible( bVisible && m_pPrivate->m_bHasData && !m_pPrivate->m_sPropertyName.isEmpty()); } if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != nullptr) { m_pPrivate->m_pContainer->renderView(); } } void nmPebiGridSceneController::setBoundsGridVisible(bool bVisible) { if(m_pPrivate == nullptr) { return; } m_pPrivate->m_bBoundsGridVisible = bVisible; if(m_pPrivate->m_pPlanarScaleGridLayer != nullptr) { m_pPrivate->m_pPlanarScaleGridLayer->setVisible( bVisible && m_pPrivate->m_bHasData); } if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != nullptr) { m_pPrivate->m_pContainer->renderView(); } } bool nmPebiGridSceneController::hasViewData() const { return m_pPrivate != nullptr && m_pPrivate->m_bHasData; } QString nmPebiGridSceneController::getProperty() const { return m_pPrivate != nullptr ? m_pPrivate->m_sPropertyName : QString(); } bool nmPebiGridSceneController::isGridLinesVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bGridLinesVisible; } bool nmPebiGridSceneController::isScalarBarVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bScalarBarVisible; } bool nmPebiGridSceneController::isBoundsGridVisible() const { return m_pPrivate != nullptr && m_pPrivate->m_bBoundsGridVisible; }