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/nmData/nmPebiCellMapping.cpp

333 lines
12 KiB
C++

#include "nmPebiCellMapping.h"
#include <limits.h>
#include <vtkAbstractArray.h>
#include <vtkCellData.h>
#include <vtkDataArray.h>
#include <vtkFieldData.h>
#include <vtkIntArray.h>
#include <vtkMath.h>
#include <vtkPointData.h>
#include <vtkSmartPointer.h>
#include <vtkUnstructuredGrid.h>
namespace {
const char g_sOriginalCellIdArrayName[] = "OriginalCellId";
const char g_sOriginalCellCountArrayName[] = "OriginalCellCount";
bool failAttach(QString* pError, const char* pMessage)
{
if(pError != NULL) {
*pError = QString::fromLatin1(pMessage);
}
return false;
}
nmPebiCellMapping::MappingStatus failRead(
QString* pError,
const char* pMessage)
{
if(pError != NULL) {
*pError = QString::fromLatin1(pMessage);
}
return nmPebiCellMapping::MappingStatus_Invalid;
}
bool isSupportedMappingArray(vtkDataArray* pArray)
{
if(pArray == NULL) {
return false;
}
const int nDataType = pArray->GetDataType();
return nDataType == VTK_INT || nDataType == VTK_ID_TYPE;
}
bool readMappingValue(
vtkDataArray* pArray,
vtkIdType nTupleId,
vtkIdType* pValue)
{
if(pArray == NULL || pValue == NULL) {
return false;
}
const double dValue = pArray->GetComponent(nTupleId, 0);
if(!vtkMath::IsFinite(dValue) || dValue < 0.0 ||
dValue > static_cast<double>(INT_MAX)) {
return false;
}
const vtkIdType nValue = static_cast<vtkIdType>(dValue);
if(static_cast<double>(nValue) != dValue) {
return false;
}
*pValue = nValue;
return true;
}
bool validateMappingValues(
const QVector<vtkIdType>& vecVtkToOriginal,
vtkIdType nOriginalCellCount,
QVector<vtkIdType>* pOriginalToVtk,
QString* pError)
{
if(nOriginalCellCount <= 0 || nOriginalCellCount > INT_MAX ||
nOriginalCellCount < static_cast<vtkIdType>(
vecVtkToOriginal.size())) {
return failAttach(pError,
"PEBI original cell count is invalid for the mapping.");
}
QVector<vtkIdType> vecOriginalToVtk(
static_cast<int>(nOriginalCellCount),
static_cast<vtkIdType>(-1));
for(int nVtkCellId = 0;
nVtkCellId < vecVtkToOriginal.size(); ++nVtkCellId) {
const vtkIdType nOriginalCellId =
vecVtkToOriginal[nVtkCellId];
if(nOriginalCellId < 0 ||
nOriginalCellId >= nOriginalCellCount) {
return failAttach(pError,
"PEBI mapping contains an out-of-range original cell ID.");
}
if(vecOriginalToVtk[static_cast<int>(nOriginalCellId)] >= 0) {
return failAttach(pError,
"PEBI mapping contains a duplicate original cell ID.");
}
vecOriginalToVtk[static_cast<int>(nOriginalCellId)] =
static_cast<vtkIdType>(nVtkCellId);
}
// 双向核对可避免以后调整构造过程时只维护其中一个方向。
for(int nVtkCellId = 0;
nVtkCellId < vecVtkToOriginal.size(); ++nVtkCellId) {
const vtkIdType nOriginalCellId =
vecVtkToOriginal[nVtkCellId];
if(vecOriginalToVtk[static_cast<int>(nOriginalCellId)] !=
static_cast<vtkIdType>(nVtkCellId)) {
return failAttach(pError,
"PEBI cell mapping is not reversible.");
}
}
if(pOriginalToVtk != NULL) {
*pOriginalToVtk = vecOriginalToVtk;
}
return true;
}
}
const char* nmPebiCellMapping::originalCellIdArrayName()
{
return g_sOriginalCellIdArrayName;
}
const char* nmPebiCellMapping::originalCellCountArrayName()
{
return g_sOriginalCellCountArrayName;
}
bool nmPebiCellMapping::attach(
vtkUnstructuredGrid* pGrid,
const QVector<vtkIdType>& vecVtkToOriginal,
vtkIdType nOriginalCellCount,
QString* pError)
{
if(pError != NULL) {
pError->clear();
}
if(pGrid == NULL || pGrid->GetNumberOfCells() <= 0 ||
pGrid->GetNumberOfCells() > INT_MAX ||
static_cast<vtkIdType>(vecVtkToOriginal.size()) !=
pGrid->GetNumberOfCells()) {
return failAttach(pError,
"PEBI grid cell count does not match the mapping.");
}
vtkCellData* pCellData = pGrid->GetCellData();
vtkPointData* pPointData = pGrid->GetPointData();
vtkFieldData* pFieldData = pGrid->GetFieldData();
if(pCellData == NULL || pPointData == NULL || pFieldData == NULL) {
return failAttach(pError,
"PEBI grid data containers are unavailable.");
}
if(pCellData->GetAbstractArray(originalCellIdArrayName()) != NULL ||
pCellData->GetAbstractArray(originalCellCountArrayName()) != NULL ||
pPointData->GetAbstractArray(originalCellIdArrayName()) != NULL ||
pPointData->GetAbstractArray(originalCellCountArrayName()) != NULL ||
pFieldData->GetAbstractArray(originalCellIdArrayName()) != NULL ||
pFieldData->GetAbstractArray(originalCellCountArrayName()) != NULL) {
return failAttach(pError,
"PEBI grid already contains reserved mapping arrays.");
}
if(!validateMappingValues(vecVtkToOriginal,
nOriginalCellCount, NULL, pError)) {
return false;
}
vtkSmartPointer<vtkIntArray> pOriginalCellIds =
vtkSmartPointer<vtkIntArray>::New();
pOriginalCellIds->SetName(originalCellIdArrayName());
pOriginalCellIds->SetNumberOfComponents(1);
pOriginalCellIds->SetNumberOfTuples(
static_cast<vtkIdType>(vecVtkToOriginal.size()));
for(int nVtkCellId = 0;
nVtkCellId < vecVtkToOriginal.size(); ++nVtkCellId) {
pOriginalCellIds->SetValue(
static_cast<vtkIdType>(nVtkCellId),
static_cast<int>(vecVtkToOriginal[nVtkCellId]));
}
vtkSmartPointer<vtkIntArray> pOriginalCellCount =
vtkSmartPointer<vtkIntArray>::New();
pOriginalCellCount->SetName(originalCellCountArrayName());
pOriginalCellCount->SetNumberOfComponents(1);
pOriginalCellCount->SetNumberOfTuples(1);
pOriginalCellCount->SetValue(0, static_cast<int>(nOriginalCellCount));
vtkDataArray* pPreviousScalars = pCellData->GetScalars();
if(pCellData->AddArray(pOriginalCellIds) < 0) {
return failAttach(pError,
"Unable to add the PEBI original cell ID array.");
}
if(pFieldData->AddArray(pOriginalCellCount) < 0) {
pCellData->RemoveArray(originalCellIdArrayName());
return failAttach(pError,
"Unable to add the PEBI original cell count array.");
}
const bool bAttached =
pCellData->GetAbstractArray(originalCellIdArrayName()) ==
pOriginalCellIds.GetPointer() &&
pFieldData->GetAbstractArray(originalCellCountArrayName()) ==
pOriginalCellCount.GetPointer() &&
pCellData->GetScalars() == pPreviousScalars &&
pCellData->GetScalars() != pOriginalCellIds.GetPointer();
if(!bAttached) {
// VTK 不提供成组 AddArray第二步或事后校验失败时显式撤销整组。
pFieldData->RemoveArray(originalCellCountArrayName());
pCellData->RemoveArray(originalCellIdArrayName());
return failAttach(pError,
"Unable to attach the complete PEBI cell mapping.");
}
return true;
}
nmPebiCellMapping::MappingStatus nmPebiCellMapping::read(
vtkUnstructuredGrid* pGrid,
QVector<vtkIdType>* pVtkToOriginal,
QVector<vtkIdType>* pOriginalToVtk,
QString* pError)
{
if(pVtkToOriginal != NULL) {
pVtkToOriginal->clear();
}
if(pOriginalToVtk != NULL) {
pOriginalToVtk->clear();
}
if(pError != NULL) {
pError->clear();
}
if(pVtkToOriginal != NULL &&
pVtkToOriginal == pOriginalToVtk) {
return failRead(pError,
"PEBI mapping outputs must use different containers.");
}
if(pGrid == NULL) {
return failRead(pError, "PEBI grid is unavailable.");
}
vtkCellData* pCellData = pGrid->GetCellData();
vtkPointData* pPointData = pGrid->GetPointData();
vtkFieldData* pFieldData = pGrid->GetFieldData();
if(pCellData == NULL || pPointData == NULL || pFieldData == NULL) {
return failRead(pError,
"PEBI grid data containers are unavailable.");
}
vtkAbstractArray* pOriginalCellIdsAbstract =
pCellData->GetAbstractArray(originalCellIdArrayName());
vtkAbstractArray* pOriginalCellCountAbstract =
pFieldData->GetAbstractArray(originalCellCountArrayName());
if(pCellData->GetAbstractArray(originalCellCountArrayName()) != NULL ||
pPointData->GetAbstractArray(originalCellIdArrayName()) != NULL ||
pPointData->GetAbstractArray(originalCellCountArrayName()) != NULL ||
pFieldData->GetAbstractArray(originalCellIdArrayName()) != NULL) {
return failRead(pError,
"PEBI grid contains mapping arrays in invalid locations.");
}
if(pOriginalCellIdsAbstract == NULL &&
pOriginalCellCountAbstract == NULL) {
return MappingStatus_Missing;
}
if(pOriginalCellIdsAbstract == NULL ||
pOriginalCellCountAbstract == NULL) {
return failRead(pError,
"PEBI grid contains an incomplete cell mapping.");
}
vtkDataArray* pOriginalCellIds =
vtkDataArray::SafeDownCast(pOriginalCellIdsAbstract);
vtkDataArray* pOriginalCellCount =
vtkDataArray::SafeDownCast(pOriginalCellCountAbstract);
if(!isSupportedMappingArray(pOriginalCellIds) ||
!isSupportedMappingArray(pOriginalCellCount)) {
return failRead(pError,
"PEBI cell mapping arrays must use Int32 or vtkIdType integers.");
}
const vtkIdType nVtkCellCount = pGrid->GetNumberOfCells();
if(nVtkCellCount <= 0 || nVtkCellCount > INT_MAX ||
pOriginalCellIds->GetNumberOfComponents() != 1 ||
pOriginalCellIds->GetNumberOfTuples() != nVtkCellCount ||
pOriginalCellCount->GetNumberOfComponents() != 1 ||
pOriginalCellCount->GetNumberOfTuples() != 1) {
return failRead(pError,
"PEBI cell mapping array dimensions are invalid.");
}
if(pCellData->GetScalars() == pOriginalCellIds) {
return failRead(pError,
"PEBI original cell IDs must not be active scalars.");
}
vtkIdType nOriginalCellCount = 0;
if(!readMappingValue(pOriginalCellCount, 0, &nOriginalCellCount) ||
nOriginalCellCount <= 0) {
return failRead(pError,
"PEBI original cell count is not a valid positive integer.");
}
QVector<vtkIdType> vecVtkToOriginal(
static_cast<int>(nVtkCellCount));
for(vtkIdType nVtkCellId = 0;
nVtkCellId < nVtkCellCount; ++nVtkCellId) {
vtkIdType nOriginalCellId = 0;
if(!readMappingValue(
pOriginalCellIds, nVtkCellId, &nOriginalCellId)) {
return failRead(pError,
"PEBI mapping contains a non-integer or out-of-range ID.");
}
vecVtkToOriginal[static_cast<int>(nVtkCellId)] = nOriginalCellId;
}
QVector<vtkIdType> vecOriginalToVtk;
QString sValidationError;
if(!validateMappingValues(vecVtkToOriginal,
nOriginalCellCount,
&vecOriginalToVtk,
&sValidationError)) {
if(pError != NULL) {
*pError = sValidationError;
}
return MappingStatus_Invalid;
}
if(pVtkToOriginal != NULL) {
*pVtkToOriginal = vecVtkToOriginal;
}
if(pOriginalToVtk != NULL) {
*pOriginalToVtk = vecOriginalToVtk;
}
return MappingStatus_Valid;
}