#include "nmVolumeSlicePipeline.h" #include "nmVolumeDataRenderAdapter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { /** @brief 记录 VTK 错误;回调只访问本次同步计算的栈标记,不接触界面。 */ void recordSliceError(vtkObject*, unsigned long, void* pClientData, void*) { *static_cast(pClientData) = true; } } bool nmVolumeSlicePipeline::build(const QSharedPointer& pData, const nmVolumeSliceSettings& oSettings, vtkSmartPointer& pOutput, QString* pError) { if(pError != nullptr) pError->clear(); nmVolumeSliceSettings oNormalized = oSettings; if(!oNormalized.normalize(pError)) return false; if(pData.isNull()) { if(pError != nullptr) *pError = QCoreApplication::translate("nmVolumeSlicePipeline", "No volume model is available."); return false; } try { nmVolumeDataRenderAdapter oAdapter(pData); bool bFailed = false; vtkSmartPointer pObserver = vtkSmartPointer::New(); pObserver->SetClientData(&bFailed); pObserver->SetCallback(recordSliceError); vtkSmartPointer pPlane = vtkSmartPointer::New(); pPlane->SetOrigin(oNormalized.m_dOrigin); pPlane->SetNormal(oNormalized.m_dNormal); vtkSmartPointer pCutter = vtkSmartPointer::New(); pCutter->SetInputConnection(oAdapter.getOutputPort()); pCutter->SetCutFunction(pPlane); pCutter->SetValue(0, 0.0); pCutter->GenerateCutScalarsOff(); pCutter->GenerateTrianglesOff(); pCutter->SetOutputPointsPrecision(vtkAlgorithm::DOUBLE_PRECISION); pCutter->AddObserver(vtkCommand::ErrorEvent, pObserver); pCutter->Update(); pCutter->RemoveObservers(vtkCommand::ErrorEvent, pObserver); pObserver->SetClientData(nullptr); vtkPolyData* pResult = pCutter->GetOutput(); if(!bFailed && pResult != nullptr && pCutter->GetErrorCode() == vtkErrorCode::NoError) { for(vtkIdType nPoint = 0; nPoint < pResult->GetNumberOfPoints(); ++nPoint) { double dPoint[3]; pResult->GetPoint(nPoint, dPoint); if(!vtkMath::IsFinite(dPoint[0]) || !vtkMath::IsFinite(dPoint[1]) || !vtkMath::IsFinite(dPoint[2])) { bFailed = true; break; } } // 空截面可能没有属性数组;非空截面必须完整保留发布数据的属性关联。 const QVector& vecProperties = pData->getProperties(); for(int nProperty = 0; !bFailed && pResult->GetNumberOfCells() > 0 && nProperty < vecProperties.size(); ++nProperty) { const nmVolumePropertyDescriptor& oProperty = vecProperties.at(nProperty); vtkDataSetAttributes* pAttributes = oProperty.m_eAssociation == NM_VolumeProperty_CellData ? static_cast(pResult->GetCellData()) : static_cast(pResult->GetPointData()); vtkDataArray* pArray = pAttributes->GetArray(oProperty.m_sArrayName.toUtf8().constData()); const vtkIdType nExpected = oProperty.m_eAssociation == NM_VolumeProperty_CellData ? pResult->GetNumberOfCells() : pResult->GetNumberOfPoints(); bFailed = pArray == nullptr || pArray->GetNumberOfComponents() != 1 || pArray->GetNumberOfTuples() != nExpected; } if(!bFailed) { vtkSmartPointer pCandidate = vtkSmartPointer::New(); // 与临时 Cutter 完全断开;后续视图刷新不能重新执行切片。 pCandidate->DeepCopy(pResult); // 节点插值也可能产生非有限数,只规范化独立输出,绝不写入冻结的原始数组。 for(int nProperty = 0; nProperty < vecProperties.size(); ++nProperty) { const nmVolumePropertyDescriptor& oProperty = vecProperties.at(nProperty); vtkDataArray* pArray = oProperty.m_eAssociation == NM_VolumeProperty_CellData ? pCandidate->GetCellData()->GetArray(oProperty.m_sArrayName.toUtf8().constData()) : pCandidate->GetPointData()->GetArray(oProperty.m_sArrayName.toUtf8().constData()); for(vtkIdType nTuple = 0; pArray != nullptr && nTuple < pArray->GetNumberOfTuples(); ++nTuple) { if(!vtkMath::IsFinite(pArray->GetComponent(nTuple, 0))) { pArray->SetComponent(nTuple, 0, std::numeric_limits::quiet_NaN()); } } } pOutput = pCandidate; return true; } } if(pError != nullptr) *pError = QCoreApplication::translate("nmVolumeSlicePipeline", "Unable to compute a valid slice. The previous view has been kept."); } catch(const std::bad_alloc&) { if(pError != nullptr) *pError = QCoreApplication::translate("nmVolumeSlicePipeline", "Not enough memory to compute the slice."); } return false; }