#include "nmVTKPlanarPolygonLayer.h" #include #include #include #include #include #include #include #include #include #include namespace { bool isPlanarPolygonLayerGuiThread() { // VTK Prop 和 Qt 视图的生命周期都由 GUI 线程统一管理。 QCoreApplication* pApplication = QCoreApplication::instance(); return pApplication == nullptr || pApplication->thread() == QThread::currentThread(); } bool isFinitePoint(const QPointF& oPoint) { return vtkMath::IsFinite(oPoint.x()) && vtkMath::IsFinite(oPoint.y()); } } class nmVTKPlanarPolygonLayerPrivate { public: nmVTKPlanarPolygonLayerPrivate() : m_pPoints(vtkSmartPointer::New()), m_pVertices(vtkSmartPointer::New()), m_pLines(vtkSmartPointer::New()), m_pPolyData(vtkSmartPointer::New()), m_pMapper(vtkSmartPointer::New()), m_pActor(vtkSmartPointer::New()), m_bClosed(false) { m_pPoints->SetDataTypeToDouble(); m_pPolyData->SetPoints(m_pPoints); m_pPolyData->SetVerts(m_pVertices); m_pPolyData->SetLines(m_pLines); m_pMapper->SetInputData(m_pPolyData); // 黄色点线只是业务覆盖层:不参与拾取,也不扩大相机自适应范围。 m_pActor->SetMapper(m_pMapper); m_pActor->GetProperty()->SetColor(1.0, 1.0, 0.0); m_pActor->GetProperty()->SetLineWidth(2.0f); m_pActor->GetProperty()->SetPointSize(7.0f); m_pActor->GetProperty()->LightingOff(); m_pActor->PickableOff(); m_pActor->UseBoundsOff(); } vtkActor* getActor() const { return m_pActor; } bool setPolygon(const QVector& vecPoints, bool bClosed) { for(int nIndex = 0; nIndex < vecPoints.size(); ++nIndex) { if(!isFinitePoint(vecPoints[nIndex])) { return false; } } // 先构建完整候选 PolyData,成功后再一次替换,避免视图看到半更新状态。 vtkSmartPointer pCandidatePoints = vtkSmartPointer::New(); vtkSmartPointer pCandidateVertices = vtkSmartPointer::New(); vtkSmartPointer pCandidateLines = vtkSmartPointer::New(); pCandidatePoints->SetDataTypeToDouble(); const vtkIdType nPointCount = static_cast(vecPoints.size()); pCandidatePoints->SetNumberOfPoints(nPointCount); for(vtkIdType nPointId = 0; nPointId < nPointCount; ++nPointId) { const QPointF& oPoint = vecPoints[static_cast(nPointId)]; pCandidatePoints->SetPoint(nPointId, oPoint.x(), oPoint.y(), 0.0); pCandidateVertices->InsertNextCell(1, &nPointId); } const bool bCandidateClosed = bClosed && nPointCount >= 3; if(nPointCount >= 2) { // VTK 折线不会自动闭合,完成后需在索引尾部再写入首点。 const vtkIdType nLinePointCount = nPointCount + (bCandidateClosed ? 1 : 0); pCandidateLines->InsertNextCell(nLinePointCount); for(vtkIdType nPointId = 0; nPointId < nPointCount; ++nPointId) { pCandidateLines->InsertCellPoint(nPointId); } if(bCandidateClosed) { pCandidateLines->InsertCellPoint(0); } } m_vecPoints = vecPoints; m_bClosed = bCandidateClosed; m_pPoints = pCandidatePoints; m_pVertices = pCandidateVertices; m_pLines = pCandidateLines; m_pPolyData->SetPoints(m_pPoints); m_pPolyData->SetVerts(m_pVertices); m_pPolyData->SetLines(m_pLines); m_pPolyData->Modified(); return true; } void clear() { setPolygon(QVector(), false); } vtkSmartPointer m_pPoints; vtkSmartPointer m_pVertices; vtkSmartPointer m_pLines; vtkSmartPointer m_pPolyData; vtkSmartPointer m_pMapper; vtkSmartPointer m_pActor; QVector m_vecPoints; bool m_bClosed; }; nmVTKPlanarPolygonLayer::nmVTKPlanarPolygonLayer( const QString& sLayerId) : nmVTKRenderLayer(sLayerId), m_pPrivate(new nmVTKPlanarPolygonLayerPrivate) { Q_ASSERT(isPlanarPolygonLayerGuiThread()); setRenderProp(m_pPrivate->getActor()); } nmVTKPlanarPolygonLayer::~nmVTKPlanarPolygonLayer() { Q_ASSERT(isPlanarPolygonLayerGuiThread()); if(m_pPrivate != nullptr) { detach(); delete m_pPrivate; m_pPrivate = nullptr; } } bool nmVTKPlanarPolygonLayer::setPolygon( const QVector& vecPoints, bool bClosed) { Q_ASSERT(isPlanarPolygonLayerGuiThread()); return isPlanarPolygonLayerGuiThread() && m_pPrivate != nullptr && m_pPrivate->setPolygon(vecPoints, bClosed); } void nmVTKPlanarPolygonLayer::clear() { Q_ASSERT(isPlanarPolygonLayerGuiThread()); if(isPlanarPolygonLayerGuiThread() && m_pPrivate != nullptr) { m_pPrivate->clear(); } } QVector nmVTKPlanarPolygonLayer::getPoints() const { return m_pPrivate != nullptr ? m_pPrivate->m_vecPoints : QVector(); } bool nmVTKPlanarPolygonLayer::isClosed() const { return m_pPrivate != nullptr && m_pPrivate->m_bClosed; }