You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmVolumeSlicePipeline.cpp

106 lines
5.5 KiB
C++

#include "nmVolumeSlicePipeline.h"
#include "nmVolumeDataRenderAdapter.h"
#include <QCoreApplication>
#include <new>
#include <limits>
#include <vtkAlgorithm.h>
#include <vtkCallbackCommand.h>
#include <vtkCellData.h>
#include <vtkCommand.h>
#include <vtkCutter.h>
#include <vtkDataArray.h>
#include <vtkErrorCode.h>
#include <vtkMath.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
namespace {
/** @brief 记录 VTK 错误;回调只访问本次同步计算的栈标记,不接触界面。 */
void recordSliceError(vtkObject*, unsigned long, void* pClientData, void*)
{
*static_cast<bool*>(pClientData) = true;
}
}
bool nmVolumeSlicePipeline::build(const QSharedPointer<const nmVolumeData>& pData,
const nmVolumeSliceSettings& oSettings, vtkSmartPointer<vtkPolyData>& 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<vtkCallbackCommand> pObserver = vtkSmartPointer<vtkCallbackCommand>::New();
pObserver->SetClientData(&bFailed);
pObserver->SetCallback(recordSliceError);
vtkSmartPointer<vtkPlane> pPlane = vtkSmartPointer<vtkPlane>::New();
pPlane->SetOrigin(oNormalized.m_dOrigin);
pPlane->SetNormal(oNormalized.m_dNormal);
vtkSmartPointer<vtkCutter> pCutter = vtkSmartPointer<vtkCutter>::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<nmVolumePropertyDescriptor>& 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<vtkDataSetAttributes*>(pResult->GetCellData())
: static_cast<vtkDataSetAttributes*>(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<vtkPolyData> pCandidate = vtkSmartPointer<vtkPolyData>::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<double>::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;
}