#include "nmPebiGridSceneController.h" #include "nmPebiGridViewData.h" #include "nmVTKMeshLayer.h" #include "nmVTKRenderLayer.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 #include #include #include #include #include namespace { QString controllerText(const char* pText) { return QCoreApplication::translate( "nmPebiGridSceneController", pText); } double calculateWellOverlayOffset(vtkUnstructuredGrid* pGrid) { if(pGrid == NULL) { return 0.0001; } double dBounds[6]; pGrid->GetBounds(dBounds); if(!vtkMath::AreBoundsInitialized(dBounds)) { return 0.0001; } for(int nIndex = 0; nIndex < 6; ++nIndex) { if(!vtkMath::IsFinite(dBounds[nIndex])) { return 0.0001; } } const double dWidth = dBounds[1] - dBounds[0]; const double dHeight = dBounds[3] - dBounds[2]; const double dSceneScale = std::sqrt( dWidth * dWidth + dHeight * dHeight); if(!vtkMath::IsFinite(dSceneScale) || dSceneScale <= 0.0) { return 0.0001; } // 使用场景尺度的十万分之一,避免千米级网格下偏移小于深度缓冲精度。 return qMax(0.0001, dSceneScale * 0.00001); } /** @brief 单个井位 Actor 的 VTK 对象集合,由井位图层独占。 */ struct nmPebiGridWellPointEntry { vtkSmartPointer m_pPoints; vtkSmartPointer m_pPolyData; vtkSmartPointer m_pMapper; vtkSmartPointer m_pActor; }; /** @brief 单口井全部井筒和裂缝线段的 VTK 对象集合。 */ struct nmPebiGridWellLineEntry { vtkSmartPointer m_pPolyData; vtkSmartPointer m_pMapper; vtkSmartPointer m_pActor; }; class nmPebiGridWellPointLayer : public nmVTKRenderLayer { public: explicit nmPebiGridWellPointLayer(const QString& sLayerId) : nmVTKRenderLayer(sLayerId), m_pAssembly(vtkSmartPointer::New()), m_dZOffset(0.0) { setRenderProp(m_pAssembly); } virtual ~nmPebiGridWellPointLayer() { // Scene 删除图层前已 detach;这里仅释放图层独占的逐井 VTK 对象。 clearEntries(); } void setZOffset(double dZOffset) { m_dZOffset = dZOffset; } void setWells(const QVector& vecWells) { QSet setIncomingCodes; for(int nIndex = 0; nIndex < vecWells.size(); ++nIndex) { setIncomingCodes.insert(vecWells[nIndex].m_sWellCode); } QMap::iterator oIterator = m_mapEntries.begin(); while(oIterator != m_mapEntries.end()) { if(!setIncomingCodes.contains(oIterator.key())) { m_pAssembly->RemovePart(oIterator.value()->m_pActor); delete oIterator.value(); oIterator = m_mapEntries.erase(oIterator); } else { ++oIterator; } } // 以稳定井编码查找并更新 Actor,井改名不会被误判为另一口井。 for(int nIndex = 0; nIndex < vecWells.size(); ++nIndex) { const nmPebiGridWellViewData& oWell = vecWells[nIndex]; nmPebiGridWellPointEntry* pEntry = m_mapEntries.value(oWell.m_sWellCode, NULL); if(pEntry == NULL) { pEntry = createEntry(); m_mapEntries.insert(oWell.m_sWellCode, pEntry); m_pAssembly->AddPart(pEntry->m_pActor); } pEntry->m_pPoints->SetPoint( 0, oWell.m_oPosition.x(), oWell.m_oPosition.y(), m_dZOffset); pEntry->m_pPoints->Modified(); pEntry->m_pPolyData->Modified(); } } private: nmPebiGridWellPointEntry* createEntry() { nmPebiGridWellPointEntry* pEntry = new nmPebiGridWellPointEntry(); pEntry->m_pPoints = vtkSmartPointer::New(); pEntry->m_pPoints->InsertNextPoint(0.0, 0.0, m_dZOffset); vtkSmartPointer pVertices = vtkSmartPointer::New(); pVertices->InsertNextCell(1); pVertices->InsertCellPoint(0); pEntry->m_pPolyData = vtkSmartPointer::New(); pEntry->m_pPolyData->SetPoints(pEntry->m_pPoints); pEntry->m_pPolyData->SetVerts(pVertices); pEntry->m_pMapper = vtkSmartPointer::New(); pEntry->m_pMapper->SetInputData(pEntry->m_pPolyData); pEntry->m_pActor = vtkSmartPointer::New(); pEntry->m_pActor->SetMapper(pEntry->m_pMapper); pEntry->m_pActor->GetProperty()->SetColor(1.0, 0.15, 0.05); pEntry->m_pActor->GetProperty()->SetPointSize(9.0); pEntry->m_pActor->PickableOff(); return pEntry; } void clearEntries() { QMap::iterator oIterator = m_mapEntries.begin(); while(oIterator != m_mapEntries.end()) { m_pAssembly->RemovePart(oIterator.value()->m_pActor); delete oIterator.value(); ++oIterator; } m_mapEntries.clear(); } vtkSmartPointer m_pAssembly; double m_dZOffset; QMap m_mapEntries; }; class nmPebiGridWellLineLayer : public nmVTKRenderLayer { public: explicit nmPebiGridWellLineLayer(const QString& sLayerId) : nmVTKRenderLayer(sLayerId), m_pAssembly(vtkSmartPointer::New()), m_dZOffset(0.0) { setRenderProp(m_pAssembly); } virtual ~nmPebiGridWellLineLayer() { clearEntries(); } void setZOffset(double dZOffset) { m_dZOffset = dZOffset; } void setWells(const QVector& vecWells) { QSet setIncomingCodes; for(int nIndex = 0; nIndex < vecWells.size(); ++nIndex) { setIncomingCodes.insert(vecWells[nIndex].m_sWellCode); } QMap::iterator oIterator = m_mapEntries.begin(); while(oIterator != m_mapEntries.end()) { if(!setIncomingCodes.contains(oIterator.key())) { m_pAssembly->RemovePart(oIterator.value()->m_pActor); delete oIterator.value(); oIterator = m_mapEntries.erase(oIterator); } else { ++oIterator; } } for(int nIndex = 0; nIndex < vecWells.size(); ++nIndex) { const nmPebiGridWellViewData& oWell = vecWells[nIndex]; nmPebiGridWellLineEntry* pEntry = m_mapEntries.value(oWell.m_sWellCode, NULL); if(pEntry == NULL) { pEntry = createEntry(); m_mapEntries.insert(oWell.m_sWellCode, pEntry); m_pAssembly->AddPart(pEntry->m_pActor); } updateEntry(pEntry, oWell); } } private: nmPebiGridWellLineEntry* createEntry() { nmPebiGridWellLineEntry* pEntry = new nmPebiGridWellLineEntry(); pEntry->m_pPolyData = vtkSmartPointer::New(); pEntry->m_pMapper = vtkSmartPointer::New(); pEntry->m_pMapper->SetInputData(pEntry->m_pPolyData); pEntry->m_pMapper->SetScalarModeToUseCellData(); pEntry->m_pMapper->SetColorModeToDirectScalars(); pEntry->m_pMapper->ScalarVisibilityOn(); pEntry->m_pActor = vtkSmartPointer::New(); pEntry->m_pActor->SetMapper(pEntry->m_pMapper); pEntry->m_pActor->GetProperty()->SetLineWidth(2.0); pEntry->m_pActor->PickableOff(); return pEntry; } void appendSegment(vtkPoints* pPoints, vtkCellArray* pLines, vtkUnsignedCharArray* pColors, const QLineF& oLine, bool bFracture) { const vtkIdType nFirstPoint = pPoints->InsertNextPoint( oLine.x1(), oLine.y1(), m_dZOffset); const vtkIdType nSecondPoint = pPoints->InsertNextPoint( oLine.x2(), oLine.y2(), m_dZOffset); pLines->InsertNextCell(2); pLines->InsertCellPoint(nFirstPoint); pLines->InsertCellPoint(nSecondPoint); if(bFracture) { pColors->InsertNextTuple3(255, 45, 25); } else { pColors->InsertNextTuple3(30, 30, 30); } } void updateEntry(nmPebiGridWellLineEntry* pEntry, const nmPebiGridWellViewData& oWell) { vtkSmartPointer pPoints = vtkSmartPointer::New(); vtkSmartPointer pLines = vtkSmartPointer::New(); vtkSmartPointer pColors = vtkSmartPointer::New(); pColors->SetName("WellLineColors"); pColors->SetNumberOfComponents(3); int nIndex = 0; for(nIndex = 0; nIndex < oWell.m_vecWellSegments.size(); ++nIndex) { appendSegment(pPoints, pLines, pColors, oWell.m_vecWellSegments[nIndex], false); } for(nIndex = 0; nIndex < oWell.m_vecFractureSegments.size(); ++nIndex) { appendSegment(pPoints, pLines, pColors, oWell.m_vecFractureSegments[nIndex], true); } pEntry->m_pPolyData->SetPoints(pPoints); pEntry->m_pPolyData->SetLines(pLines); pEntry->m_pPolyData->GetCellData()->SetScalars(pColors); pEntry->m_pPolyData->Modified(); } void clearEntries() { QMap::iterator oIterator = m_mapEntries.begin(); while(oIterator != m_mapEntries.end()) { m_pAssembly->RemovePart(oIterator.value()->m_pActor); delete oIterator.value(); ++oIterator; } m_mapEntries.clear(); } vtkSmartPointer m_pAssembly; double m_dZOffset; QMap m_mapEntries; }; class nmPebiGridWellNameLayer : public nmVTKRenderLayer { public: explicit nmPebiGridWellNameLayer(const QString& sLayerId) : nmVTKRenderLayer(sLayerId), m_pAssembly(vtkSmartPointer::New()), m_dZOffset(0.0) { setRenderProp(m_pAssembly); } virtual ~nmPebiGridWellNameLayer() { clearEntries(); } void setZOffset(double dZOffset) { m_dZOffset = dZOffset; } void setWells(const QVector& vecWells) { QSet setIncomingCodes; for(int nIndex = 0; nIndex < vecWells.size(); ++nIndex) { setIncomingCodes.insert(vecWells[nIndex].m_sWellCode); } QMap >::iterator oIterator = m_mapActors.begin(); while(oIterator != m_mapActors.end()) { if(!setIncomingCodes.contains(oIterator.key())) { m_pAssembly->RemovePart(oIterator.value()); oIterator = m_mapActors.erase(oIterator); } else { ++oIterator; } } for(int nIndex = 0; nIndex < vecWells.size(); ++nIndex) { const nmPebiGridWellViewData& oWell = vecWells[nIndex]; vtkSmartPointer pActor = m_mapActors.value(oWell.m_sWellCode); if(pActor == NULL) { pActor = vtkSmartPointer::New(); pActor->GetTextProperty()->SetFontSize(14); pActor->GetTextProperty()->SetColor(1.0, 1.0, 1.0); pActor->GetTextProperty()->BoldOn(); pActor->PickableOff(); m_mapActors.insert(oWell.m_sWellCode, pActor); m_pAssembly->AddPart(pActor); } const QByteArray aWellName = oWell.m_sWellName.toUtf8(); pActor->SetInput(aWellName.constData()); pActor->SetPosition(oWell.m_oPosition.x(), oWell.m_oPosition.y(), m_dZOffset); } } private: void clearEntries() { QMap >::iterator oIterator = m_mapActors.begin(); while(oIterator != m_mapActors.end()) { m_pAssembly->RemovePart(oIterator.value()); ++oIterator; } m_mapActors.clear(); } vtkSmartPointer m_pAssembly; double m_dZOffset; QMap > m_mapActors; }; } class nmPebiGridSceneControllerPrivate { public: explicit nmPebiGridSceneControllerPrivate( nmWxVTKRenderContainerWidget* pRenderContainer) : m_pContainer(pRenderContainer), m_pMeshLayer(NULL), m_pScalarBarLayer(NULL), m_pWellPointLayer(NULL), m_pWellLineLayer(NULL), m_pWellNameLayer(NULL), m_eDisplayMode(nmPebiGridSceneController::DisplayMode_SurfaceWithEdges), m_dOpacity(1.0), m_bWellVisible(true), m_bWellNameVisible(true), m_bHasData(false) { } bool ensureBaseLayers() { nmVTKScene* pScene = m_pContainer != NULL ? m_pContainer->getScene() : NULL; if(pScene == NULL) { return false; } if(m_pMeshLayer == NULL) { nmVTKMeshLayer* pLayer = new nmVTKMeshLayer("pebi.grid.mesh"); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pMeshLayer = pLayer; // 固定色和边线完全沿用旧 PEBI 网格预览外观。 m_pMeshLayer->getActor()->GetProperty()->SetColor( 0.0, 120.0 / 255.0, 215.0 / 255.0); m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor( 0.0, 0.0, 0.0); } if(m_pWellPointLayer == NULL) { nmPebiGridWellPointLayer* pLayer = new nmPebiGridWellPointLayer("pebi.grid.well_positions"); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pWellPointLayer = pLayer; } if(m_pWellLineLayer == NULL) { nmPebiGridWellLineLayer* pLayer = new nmPebiGridWellLineLayer("pebi.grid.well_lines"); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pWellLineLayer = pLayer; } if(m_pWellNameLayer == NULL) { nmPebiGridWellNameLayer* pLayer = new nmPebiGridWellNameLayer("pebi.grid.well_names"); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pWellNameLayer = pLayer; } return true; } bool ensureScalarBarLayer() { if(m_pScalarBarLayer != NULL) { return true; } nmVTKScene* pScene = m_pContainer != NULL ? m_pContainer->getScene() : NULL; if(pScene == NULL || m_pMeshLayer == NULL) { return false; } 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); if(!pScene->addLayer(pLayer)) { delete pLayer; return false; } m_pScalarBarLayer = pLayer; return true; } bool applyProperty(const QString& sPropertyName) { if(m_pMeshLayer == NULL) { return false; } if(sPropertyName.isEmpty()) { m_sPropertyName.clear(); m_pMeshLayer->setScalarVisibility(false); if(m_pScalarBarLayer != NULL) { 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 != NULL) { 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(true); return true; } void releaseGridData() { // 普通网格替换只解除 Mapper 和旧 ViewData;井 Actor 留给稳定编码增量更新。 if(m_pMeshLayer != NULL) { m_pMeshLayer->setScalarVisibility(false); m_pMeshLayer->setDataSet(NULL); m_pMeshLayer->setVisible(false); } if(m_pScalarBarLayer != NULL) { m_pScalarBarLayer->setVisible(false); } m_oViewData.clear(); m_bHasData = false; } void releaseViewData() { releaseGridData(); // 只有切换分析、显式清空或析构才移除全部井条目。 if(m_pWellPointLayer != NULL) { m_pWellPointLayer->setWells( QVector()); m_pWellPointLayer->setVisible(false); } if(m_pWellLineLayer != NULL) { m_pWellLineLayer->setWells( QVector()); m_pWellLineLayer->setVisible(false); } if(m_pWellNameLayer != NULL) { m_pWellNameLayer->setWells( QVector()); m_pWellNameLayer->setVisible(false); } m_sPropertyName.clear(); } nmWxVTKRenderContainerWidget* m_pContainer; ///< 非所有权指针。 nmVTKMeshLayer* m_pMeshLayer; ///< Scene 所有,控制器只观察。 nmVTKScalarBarLayer* m_pScalarBarLayer; ///< Scene 所有,按需创建。 nmPebiGridWellPointLayer* m_pWellPointLayer; ///< Scene 所有。 nmPebiGridWellLineLayer* m_pWellLineLayer; ///< Scene 所有。 nmPebiGridWellNameLayer* m_pWellNameLayer; ///< Scene 所有。 nmPebiGridViewData m_oViewData; nmPebiGridSceneController::DisplayMode m_eDisplayMode; double m_dOpacity; bool m_bWellVisible; bool m_bWellNameVisible; bool m_bHasData; QString m_sPropertyName; }; nmPebiGridSceneController::nmPebiGridSceneController( nmWxVTKRenderContainerWidget* pContainer) : m_pPrivate(new nmPebiGridSceneControllerPrivate(pContainer)) { } nmPebiGridSceneController::~nmPebiGridSceneController() { if(m_pPrivate != NULL) { // Layer 已归 Scene 所有;这里只解除输入和非所有权指针,不能直接 delete Layer。 m_pPrivate->releaseViewData(); m_pPrivate->m_pContainer = NULL; m_pPrivate->m_pMeshLayer = NULL; m_pPrivate->m_pScalarBarLayer = NULL; m_pPrivate->m_pWellPointLayer = NULL; m_pPrivate->m_pWellLineLayer = NULL; m_pPrivate->m_pWellNameLayer = NULL; delete m_pPrivate; m_pPrivate = NULL; } } bool nmPebiGridSceneController::setViewData( const nmPebiGridViewData& oViewData, QString* pError) { if(pError != NULL) { pError->clear(); } if(m_pPrivate == NULL || m_pPrivate->m_pContainer == NULL || !oViewData.isValid()) { if(pError != NULL) { *pError = controllerText("The grid preview data is invalid."); } return false; } if(!m_pPrivate->ensureBaseLayers()) { if(pError != NULL) { *pError = controllerText("Unable to create the grid render layers."); } return false; } const bool bFirstData = !m_pPrivate->m_bHasData; QString sRequestedProperty = m_pPrivate->m_sPropertyName; if(bFirstData && sRequestedProperty.isEmpty() && oViewData.hasMaterial()) { // 与旧预览保持一致:首次存在有效 Material 时直接启用单元着色。 sRequestedProperty = "Material"; } // 更换网格必须先断开旧 Mapper 输入,避免旧分析数据被 Scene 隐式保留。 m_pPrivate->releaseGridData(); m_pPrivate->m_oViewData = oViewData; m_pPrivate->m_pMeshLayer->setDataSet(oViewData.getGrid()); m_pPrivate->m_pMeshLayer->setDisplayMode( m_pPrivate->m_eDisplayMode == DisplayMode_SurfaceWithEdges ? nmVTKMeshLayer::DisplayMode_SurfaceWithEdges : nmVTKMeshLayer::DisplayMode_Surface); m_pPrivate->m_pMeshLayer->setOpacity(m_pPrivate->m_dOpacity); m_pPrivate->m_pMeshLayer->setVisible(true); const double dWellOverlayOffset = calculateWellOverlayOffset(oViewData.getGrid()); // 三类井图元使用同一场景尺度的分层偏移,只改变显示坐标,不写回井数据。 m_pPrivate->m_pWellLineLayer->setZOffset(dWellOverlayOffset); m_pPrivate->m_pWellPointLayer->setZOffset(dWellOverlayOffset * 2.0); m_pPrivate->m_pWellNameLayer->setZOffset(dWellOverlayOffset * 3.0); m_pPrivate->m_pWellPointLayer->setWells(oViewData.getWells()); m_pPrivate->m_pWellLineLayer->setWells(oViewData.getWells()); m_pPrivate->m_pWellNameLayer->setWells(oViewData.getWells()); m_pPrivate->m_pWellPointLayer->setVisible(m_pPrivate->m_bWellVisible); m_pPrivate->m_pWellLineLayer->setVisible(m_pPrivate->m_bWellVisible); m_pPrivate->m_pWellNameLayer->setVisible( m_pPrivate->m_bWellNameVisible); m_pPrivate->m_bHasData = true; m_pPrivate->applyProperty(sRequestedProperty); // setViewData 只用于几何替换,每次都重新适配;属性刷新走独立接口并保留相机。 m_pPrivate->m_pContainer->fitTopViewWithoutRender(); m_pPrivate->m_pContainer->showReadyState(); return true; } void nmPebiGridSceneController::clear() { if(m_pPrivate == NULL) { return; } m_pPrivate->releaseViewData(); m_pPrivate->m_eDisplayMode = DisplayMode_SurfaceWithEdges; m_pPrivate->m_dOpacity = 1.0; m_pPrivate->m_bWellVisible = true; m_pPrivate->m_bWellNameVisible = true; } void nmPebiGridSceneController::setDisplayMode(DisplayMode eMode) { if(m_pPrivate == NULL || m_pPrivate->m_pMeshLayer == NULL) { return; } m_pPrivate->m_eDisplayMode = eMode == DisplayMode_Surface ? DisplayMode_Surface : DisplayMode_SurfaceWithEdges; m_pPrivate->m_pMeshLayer->setDisplayMode( m_pPrivate->m_eDisplayMode == DisplayMode_SurfaceWithEdges ? nmVTKMeshLayer::DisplayMode_SurfaceWithEdges : nmVTKMeshLayer::DisplayMode_Surface); m_pPrivate->m_pContainer->renderView(); } void nmPebiGridSceneController::setOpacity(double dOpacity) { if(m_pPrivate == NULL || m_pPrivate->m_pMeshLayer == NULL || !m_pPrivate->m_pMeshLayer->setOpacity(dOpacity)) { return; } m_pPrivate->m_dOpacity = m_pPrivate->m_pMeshLayer->getOpacity(); m_pPrivate->m_pContainer->renderView(); } bool nmPebiGridSceneController::setProperty(const QString& sPropertyName) { if(m_pPrivate == NULL || !m_pPrivate->m_bHasData) { return false; } const bool bSucceeded = m_pPrivate->applyProperty(sPropertyName); m_pPrivate->m_pContainer->renderView(); return bSucceeded; } void nmPebiGridSceneController::setWellVisible(bool bVisible) { if(m_pPrivate == NULL) { return; } m_pPrivate->m_bWellVisible = bVisible; if(m_pPrivate->m_pWellPointLayer != NULL) { m_pPrivate->m_pWellPointLayer->setVisible(bVisible); } if(m_pPrivate->m_pWellLineLayer != NULL) { m_pPrivate->m_pWellLineLayer->setVisible(bVisible); } if(m_pPrivate->m_pContainer != NULL) { m_pPrivate->m_pContainer->renderView(); } } void nmPebiGridSceneController::setWellNameVisible(bool bVisible) { if(m_pPrivate == NULL) { return; } m_pPrivate->m_bWellNameVisible = bVisible; if(m_pPrivate->m_pWellNameLayer != NULL) { m_pPrivate->m_pWellNameLayer->setVisible(bVisible); } if(m_pPrivate->m_pContainer != NULL) { m_pPrivate->m_pContainer->renderView(); } } bool nmPebiGridSceneController::hasViewData() const { return m_pPrivate != NULL && m_pPrivate->m_bHasData; } QString nmPebiGridSceneController::getProperty() const { return m_pPrivate != NULL ? m_pPrivate->m_sPropertyName : QString(); }