diff --git a/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h b/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h index b5d8115..35cee7e 100644 --- a/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h +++ b/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h @@ -194,7 +194,7 @@ private: // 生成vtk数据 void genPebiVTK(const HX_NWTM_GRID_OUTPUT1& P1, QString vtkDir); - // 根据网格输出结构体创建 vtkUnstructuredGrid 对象 + // 根据网格输出结构体创建带唯一原始单元映射的 vtkUnstructuredGrid 对象 vtkSmartPointer createPebiUnstructuredGrid( const HX_NWTM_GRID_OUTPUT1& P1, const QAtomicInt* pCancelRequested = NULL); diff --git a/Include/nmNum/nmData/nmDataAnalyzeManager.h b/Include/nmNum/nmData/nmDataAnalyzeManager.h index dca4be3..ca36370 100644 --- a/Include/nmNum/nmData/nmDataAnalyzeManager.h +++ b/Include/nmNum/nmData/nmDataAnalyzeManager.h @@ -599,7 +599,7 @@ public: /** @brief 整体替换 DLL 井槽位顺序,并由分析方案统一重编号。 */ void setSolverWellOrder(const QVector& vecSolverWellOrder); /** - * @brief 按输入版本一次性提交后台生成的井顺序和 VTK 网格。 + * @brief 按输入版本一次性提交带有效单元映射的井顺序和 VTK 网格。 * @return 输入仍为当前版本且提交成功时返回 true;过期结果不修改现有成果。 */ bool commitPebiGridResult( @@ -785,8 +785,13 @@ public: /** @brief 显式刷新框架井压力和流量;成果加载流程不得调用。 */ bool loadWellPreAndFlow(); - // 设置当前分析下的VTK网格对象 - void setUnstructuredGrid(vtkSmartPointer grid); + /** + * @brief 设置当前分析下的 VTK 网格对象。 + * + * 允许缺少映射的旧版几何和空指针清理,拒绝携带损坏映射的网格, + * 避免绕过正式建网提交入口破坏当前网格状态。 + */ + void setUnstructuredGrid(vtkSmartPointer pGrid); // 获取当前分析下的VTK网格对象 vtkSmartPointer getUnstructuredGrid() const; // 清理当前分析下的VTK网格对象 diff --git a/Include/nmNum/nmData/nmNumericalResultPersistence.h b/Include/nmNum/nmData/nmNumericalResultPersistence.h index fc51af0..c6ff113 100644 --- a/Include/nmNum/nmData/nmNumericalResultPersistence.h +++ b/Include/nmNum/nmData/nmNumericalResultPersistence.h @@ -126,11 +126,19 @@ public: nmNumericalWellGaugeData& oGaugeData, QString* pError = NULL); - /** @brief 以二进制 VTU 写出网格并检查 VTK 返回值和文件存在性。 */ + /** + * @brief 以二进制 VTU 写出网格,允许 Missing 旧几何并拒绝 Invalid 映射。 + * + * 写入成功还要求 VTK 返回成功且目标文件实际存在。 + */ static bool writeGrid(vtkUnstructuredGrid* pGrid, const QString& sFilePath, QString* pError = NULL); - /** @brief 从 VTU 读取网格;调用方接管返回的 VTK 引用。 */ + /** + * @brief 从 VTU 读取网格;允许缺少映射的旧版几何,拒绝损坏的映射。 + * + * 成功时调用方接管返回的 VTK 引用;失败时输出保持为空。 + */ static bool readGrid(const QString& sFilePath, vtkSmartPointer& pGrid, QString* pError = NULL); diff --git a/Include/nmNum/nmData/nmPebiCellMapping.h b/Include/nmNum/nmData/nmPebiCellMapping.h new file mode 100644 index 0000000..455438a --- /dev/null +++ b/Include/nmNum/nmData/nmPebiCellMapping.h @@ -0,0 +1,63 @@ +#ifndef NMPEBICELLMAPPING_H +#define NMPEBICELLMAPPING_H + +#include "nmData_global.h" + +#include +#include + +#include + +class vtkUnstructuredGrid; + +/** + * @brief 读写原始 PEBI 单元与 VTK Cell 之间的内部身份映射。 + * + * 映射随 vtkUnstructuredGrid 保存在 CellData 和 FieldData 中。本工具类 + * 不缓存网格、数组或 DataManager 指针,但调用方仍须保证传入网格在调用 + * 期间没有被其他线程并发读写。 + */ +class NM_DATA_EXPORT nmPebiCellMapping +{ +public: + enum MappingStatus + { + MappingStatus_Missing = 0, + MappingStatus_Valid, + MappingStatus_Invalid + }; + + /** @brief 返回 CellData 中原始单元 ID 数组的保留名称。 */ + static const char* originalCellIdArrayName(); + /** @brief 返回 FieldData 中原始单元总数数组的保留名称。 */ + static const char* originalCellCountArrayName(); + + /** + * @brief 将一份已验证的正向映射附加到尚未发布的网格。 + * + * 保留名称已被占用、映射不完整或 ID 不可逆时不修改网格。该函数只 + * 使用 AddArray() 登记映射,不会把 OriginalCellId 设为活动 Scalars。 + */ + static bool attach( + vtkUnstructuredGrid* pGrid, + const QVector& vecVtkToOriginal, + vtkIdType nOriginalCellCount, + QString* pError = NULL); + + /** + * @brief 读取并完整验证网格携带的正向与反向映射。 + * + * 两个数组均不存在时返回 Missing;只存在一个或任一字段损坏时返回 + * Invalid。输出在进入函数时清空,且只在返回 Valid 时接收完整结果。 + */ + static MappingStatus read( + vtkUnstructuredGrid* pGrid, + QVector* pVtkToOriginal = NULL, + QVector* pOriginalToVtk = NULL, + QString* pError = NULL); + +private: + nmPebiCellMapping(); +}; + +#endif // NMPEBICELLMAPPING_H diff --git a/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp b/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp index 156b058..f06c67a 100644 --- a/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp @@ -1,10 +1,13 @@ #include "nmCalculationPebiGrid.h" #include #include +#include #include #include #include +#include #include +#include #include #include @@ -26,9 +29,11 @@ #include "nmDataBinaryTools.h" #include "nmDataPvtParaForPebi.h" #include "nmDataTimeStepSetting.h" +#include "nmPebiCellMapping.h" #include #include +#include #include #include @@ -902,88 +907,126 @@ vtkSmartPointer nmCalculationPebiGrid::createPebiUnstructur const HX_NWTM_GRID_OUTPUT1& P1, const QAtomicInt* pCancelRequested) { - vtkSmartPointer pUnstructuredGrid = vtkSmartPointer::New(); - vtkSmartPointer pPoints = vtkSmartPointer::New(); - - // 用于存储所有有效单元引用的唯一点索引 + const size_t nPointCount = P1.PEBI_cell.p.size(); + const size_t nCellDefinitionCount = P1.PEBI_cell.pindex.size(); + const size_t nOriginalCellCount = P1.PEBI_cell.isplot.size(); + if(nPointCount == 0 || nPointCount > static_cast(INT_MAX) || + nOriginalCellCount == 0 || + nOriginalCellCount > static_cast(INT_MAX) || + nCellDefinitionCount > static_cast(INT_MAX) || + nCellDefinitionCount != nOriginalCellCount) { + qWarning() << "PEBI grid output dimensions are invalid."; + return nullptr; + } + + // 第一遍只验证 DLL 输出并收集点,不向 VTK 对象提交半成品几何。 QSet setUniquePointIndices; - - // 1. 预处理单元数据,确定哪些单元是有效的,并收集这些单元引用的所有唯一点索引 - // 这一步先不向 vtkPoints 添加点,而是收集需要添加的点的索引。 + int nVisibleCellCount = 0; for(size_t i = 0; i < P1.PEBI_cell.pindex.size(); ++i) { if((i % 256) == 0 && isCancellationRequested(pCancelRequested)) { return nullptr; } - // 只处理 isplot 为 1 的单元 - if(i >= P1.PEBI_cell.isplot.size() || P1.PEBI_cell.isplot[i] != 1) { + const std::vector& vecIndices = P1.PEBI_cell.pindex[i]; + if(vecIndices.size() > static_cast(INT_MAX)) { + qWarning() << "PEBI cell point count exceeds VTK capacity:" << i; + return nullptr; + } + if(P1.PEBI_cell.isplot[i] != 1) { continue; } - const std::vector& vecIndices = P1.PEBI_cell.pindex[i]; - - if(vecIndices.empty()) continue; - - // 确定实际点数(检查首尾是否相同) - int nPointsInCell = vecIndices.size(); - + if(vecIndices.empty()) { + qWarning() << "PEBI cell has an invalid point count:" << i; + return nullptr; + } + int nPointsInCell = static_cast(vecIndices.size()); if(nPointsInCell > 1 && vecIndices[0] == vecIndices[nPointsInCell - 1]) { nPointsInCell--; } + // 保留既有 VTK_LINE 规则,因此去除闭合尾点后至少需要两个不同点。 + if(nPointsInCell < 2) { + qWarning() << "PEBI cell has fewer than two usable points:" << i; + return nullptr; + } - if(nPointsInCell < 2) continue; // 过滤掉无效单元(点数小于2) - - // 将此有效单元引用的所有点索引添加到 setUniquePointIndices 集合中 + QSet setCellPointIndices; for(int j = 0; j < nPointsInCell; ++j) { - setUniquePointIndices.insert(vecIndices[j]); + const int nOriginalPointId = vecIndices[j]; + if(nOriginalPointId < 0 || + static_cast(nOriginalPointId) >= nPointCount) { + qWarning() << "PEBI cell references an invalid point:" + << i << nOriginalPointId; + return nullptr; + } + if(setCellPointIndices.contains(nOriginalPointId)) { + qWarning() << "PEBI cell contains a repeated interior point:" + << i << nOriginalPointId; + return nullptr; + } + const double dX = P1.PEBI_cell.p[nOriginalPointId].x; + const double dY = P1.PEBI_cell.p[nOriginalPointId].y; + if(!vtkMath::IsFinite(dX) || !vtkMath::IsFinite(dY)) { + qWarning() << "PEBI cell references a non-finite point:" << i; + return nullptr; + } + setCellPointIndices.insert(nOriginalPointId); + setUniquePointIndices.insert(nOriginalPointId); } + ++nVisibleCellCount; + } + if(nVisibleCellCount <= 0 || setUniquePointIndices.isEmpty()) { + qWarning() << "PEBI grid output contains no visible cells."; + return nullptr; + } + if(isCancellationRequested(pCancelRequested)) { + return nullptr; } - // 创建一个映射表,将原始点索引映射到 VTK 中的新点索引 + vtkSmartPointer pUnstructuredGrid = + vtkSmartPointer::New(); + vtkSmartPointer pPoints = + vtkSmartPointer::New(); QMap mapOriginalToVtkPointId; vtkIdType currentVtkPointId = 0; + pPoints->SetNumberOfPoints( + static_cast(setUniquePointIndices.size())); - // 2. 根据收集到的唯一点索引,将这些点添加到 vtkPoints - pPoints->SetNumberOfPoints(setUniquePointIndices.size()); - - // 遍历 setUniquePointIndices + int nProcessedPointCount = 0; foreach(int nOriginalIdx, setUniquePointIndices) { - if(nOriginalIdx >= 0 && nOriginalIdx < P1.PEBI_cell.p.size()) { - pPoints->SetPoint(currentVtkPointId, P1.PEBI_cell.p[nOriginalIdx].x, P1.PEBI_cell.p[nOriginalIdx].y, 0.0); - mapOriginalToVtkPointId[nOriginalIdx] = currentVtkPointId; - currentVtkPointId++; - } else { - // 处理异常情况:如果 setUniquePointIndices 中包含了无效的原始点索引 - qDebug() << "Warning: Invalid original point index" << nOriginalIdx << "found in setUniquePointIndices."; + if((nProcessedPointCount % 256) == 0 && + isCancellationRequested(pCancelRequested)) { + return nullptr; } + pPoints->SetPoint(currentVtkPointId, + P1.PEBI_cell.p[nOriginalIdx].x, + P1.PEBI_cell.p[nOriginalIdx].y, + 0.0); + mapOriginalToVtkPointId.insert( + nOriginalIdx, currentVtkPointId); + ++currentVtkPointId; + ++nProcessedPointCount; } - pUnstructuredGrid->SetPoints(pPoints); + pUnstructuredGrid->Allocate( + static_cast(nVisibleCellCount)); - // 3. 再次遍历单元数据,这次是根据新的 VTK 点索引来插入单元 + // 第二遍只提交已经验证过的可见单元,并同步构造 VTK -> 原始单元映射。 + QVector vecVtkToOriginal; + vecVtkToOriginal.reserve(nVisibleCellCount); for(size_t i = 0; i < P1.PEBI_cell.pindex.size(); ++i) { if((i % 256) == 0 && isCancellationRequested(pCancelRequested)) { return nullptr; } - // 再次检查 isplot 标志,确保只处理有效单元 - if(i >= P1.PEBI_cell.isplot.size() || P1.PEBI_cell.isplot[i] != 1) { + if(P1.PEBI_cell.isplot[i] != 1) { continue; } const std::vector& vecIndices = P1.PEBI_cell.pindex[i]; - - if(vecIndices.empty()) continue; - - // 检查首尾是否重复 - int nPointsInCell = vecIndices.size(); - + int nPointsInCell = static_cast(vecIndices.size()); if(nPointsInCell > 1 && vecIndices[0] == vecIndices[nPointsInCell - 1]) { nPointsInCell--; } - - if(nPointsInCell < 2) continue; // 过滤掉无效单元 - int vtkCellType = 0; - if(nPointsInCell == 2) { vtkCellType = VTK_LINE; } else if(nPointsInCell == 3) { @@ -994,24 +1037,52 @@ vtkSmartPointer nmCalculationPebiGrid::createPebiUnstructur vtkCellType = VTK_POLYGON; } - // 使用映射表将原始索引转换为新的 VTK 索引 - vtkIdType* pts = new vtkIdType[nPointsInCell]; - + QVector vecVtkPointIds(nPointsInCell); for(int j = 0; j < nPointsInCell; ++j) { - if(mapOriginalToVtkPointId.contains(vecIndices[j])) { // 确保点在映射表中 - pts[j] = mapOriginalToVtkPointId[vecIndices[j]]; - } else { - qDebug() << "Error: Point" << vecIndices[j] << "for cell" << i << "not found in map. This should not happen!"; - // 可以选择跳过此单元或进行其他错误处理 - delete[] pts; + QMap::const_iterator oPointIt = + mapOriginalToVtkPointId.constFind(vecIndices[j]); + if(oPointIt == mapOriginalToVtkPointId.constEnd()) { + qWarning() << "Validated PEBI point is missing from the map:" + << i << vecIndices[j]; return nullptr; } + vecVtkPointIds[j] = oPointIt.value(); } - pUnstructuredGrid->InsertNextCell(vtkCellType, nPointsInCell, pts); - delete[] pts; + const vtkIdType nExpectedVtkCellId = + static_cast(vecVtkToOriginal.size()); + const vtkIdType nInsertedVtkCellId = + pUnstructuredGrid->InsertNextCell( + vtkCellType, + static_cast(nPointsInCell), + vecVtkPointIds.data()); + if(nInsertedVtkCellId != nExpectedVtkCellId) { + qWarning() << "PEBI VTK cell insertion order is invalid."; + return nullptr; + } + vecVtkToOriginal.append(static_cast(i)); } + if(isCancellationRequested(pCancelRequested)) { + return nullptr; + } + if(vecVtkToOriginal.size() != nVisibleCellCount || + pUnstructuredGrid->GetNumberOfCells() != + static_cast(vecVtkToOriginal.size())) { + qWarning() << "PEBI visible cell mapping is incomplete."; + return nullptr; + } + + QString sMappingError; + if(!nmPebiCellMapping::attach( + pUnstructuredGrid, + vecVtkToOriginal, + static_cast(nOriginalCellCount), + &sMappingError)) { + qWarning() << "Unable to attach PEBI cell mapping:" + << sMappingError; + return nullptr; + } return pUnstructuredGrid; } diff --git a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp index 6d30f50..ed41275 100644 --- a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp +++ b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp @@ -1,6 +1,7 @@ #include "nmDataAnalyzeManager.h" #include "nmAttrRegistry.h" +#include "nmPebiCellMapping.h" #include "nmPebiResultSnapshot.h" #include "nmPebiResultSnapshotSerializer.h" #include "nmNumericalSaveSession.h" @@ -4540,7 +4541,21 @@ bool nmDataAnalyzeManager::commitPebiGridResult( return false; } - // 第三步:整体替换 DLL 槽位顺序,再登记同一输入版本并替换 VTK 网格。 + // 第三步:新生成网格必须携带完整映射;验证失败不得修改井顺序和版本。 + QString sMappingError; + const nmPebiCellMapping::MappingStatus eMappingStatus = + nmPebiCellMapping::read( + pGrid, NULL, NULL, &sMappingError); + if(eMappingStatus != nmPebiCellMapping::MappingStatus_Valid) { + qWarning() << "Cannot commit PEBI grid with invalid cell mapping:" + << (eMappingStatus == + nmPebiCellMapping::MappingStatus_Missing + ? QString("mapping arrays are missing") + : sMappingError); + return false; + } + + // 第四步:整体替换 DLL 槽位顺序,再登记同一输入版本并替换 VTK 网格。 // setSolverWellOrder() 会统一重编号,避免逐井追加期间暴露半成品顺序。 m_oNumericalAnalysisCase.setSolverWellOrder(vecSolverWellOrder); if(!m_oNumericalAnalysisCase.markGridBuiltIfCurrent( @@ -7163,9 +7178,21 @@ vtkSmartPointer nmDataAnalyzeManager::getUnstructuredGrid() return m_pVtkUnstructuredGrid; } -void nmDataAnalyzeManager::setUnstructuredGrid(vtkSmartPointer grid) +void nmDataAnalyzeManager::setUnstructuredGrid( + vtkSmartPointer pGrid) { - m_pVtkUnstructuredGrid = grid; + if(pGrid != NULL) { + QString sMappingError; + const nmPebiCellMapping::MappingStatus eMappingStatus = + nmPebiCellMapping::read( + pGrid, NULL, NULL, &sMappingError); + if(eMappingStatus == nmPebiCellMapping::MappingStatus_Invalid) { + qWarning() << "Cannot install PEBI grid with invalid cell mapping:" + << sMappingError; + return; + } + } + m_pVtkUnstructuredGrid = pGrid; } void nmDataAnalyzeManager::clearUnstructuredGrid() diff --git a/Src/nmNum/nmData/nmNumericalResultPersistence.cpp b/Src/nmNum/nmData/nmNumericalResultPersistence.cpp index f0cfea5..64ab494 100644 --- a/Src/nmNum/nmData/nmNumericalResultPersistence.cpp +++ b/Src/nmNum/nmData/nmNumericalResultPersistence.cpp @@ -1,6 +1,7 @@ #include "nmNumericalResultPersistence.h" #include "nmDataWellBase.h" +#include "nmPebiCellMapping.h" #include #include @@ -1026,6 +1027,17 @@ bool nmNumericalResultPersistence::writeGrid( return setError(pError, "Numerical grid is empty."); } + QString sMappingError; + const nmPebiCellMapping::MappingStatus eMappingStatus = + nmPebiCellMapping::read( + pGrid, NULL, NULL, &sMappingError); + if(eMappingStatus == nmPebiCellMapping::MappingStatus_Invalid) + { + return setError(pError, sMappingError.isEmpty() + ? QString("Numerical grid cell mapping is invalid.") + : sMappingError); + } + vtkNew pWriter; pWriter->SetInputData(pGrid); pWriter->SetFileName(sFilePath.toLocal8Bit().constData()); @@ -1065,6 +1077,18 @@ bool nmNumericalResultPersistence::readGrid( { return setError(pError, "Numerical VTU grid is invalid."); } + + QString sMappingError; + const nmPebiCellMapping::MappingStatus eMappingStatus = + nmPebiCellMapping::read( + pOutput, NULL, NULL, &sMappingError); + if(eMappingStatus == nmPebiCellMapping::MappingStatus_Invalid) + { + return setError(pError, sMappingError.isEmpty() + ? QString("Numerical VTU grid cell mapping is invalid.") + : sMappingError); + } + // Missing 表示旧版几何,仍可加载;映射相关能力必须另行要求 Valid。 pGrid = pOutput; return pGrid != NULL; } diff --git a/Src/nmNum/nmData/nmPebiCellMapping.cpp b/Src/nmNum/nmData/nmPebiCellMapping.cpp new file mode 100644 index 0000000..0211da7 --- /dev/null +++ b/Src/nmNum/nmData/nmPebiCellMapping.cpp @@ -0,0 +1,290 @@ +#include "nmPebiCellMapping.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 validateMappingValues( + const QVector& vecVtkToOriginal, + vtkIdType nOriginalCellCount, + QVector* pOriginalToVtk, + QString* pError) +{ + if(nOriginalCellCount <= 0 || nOriginalCellCount > INT_MAX || + nOriginalCellCount < static_cast( + vecVtkToOriginal.size())) { + return failAttach(pError, + "PEBI original cell count is invalid for the mapping."); + } + + QVector vecOriginalToVtk( + static_cast(nOriginalCellCount), + static_cast(-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(nOriginalCellId)] >= 0) { + return failAttach(pError, + "PEBI mapping contains a duplicate original cell ID."); + } + vecOriginalToVtk[static_cast(nOriginalCellId)] = + static_cast(nVtkCellId); + } + + // 双向核对可避免以后调整构造过程时只维护其中一个方向。 + for(int nVtkCellId = 0; + nVtkCellId < vecVtkToOriginal.size(); ++nVtkCellId) { + const vtkIdType nOriginalCellId = + vecVtkToOriginal[nVtkCellId]; + if(vecOriginalToVtk[static_cast(nOriginalCellId)] != + static_cast(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& vecVtkToOriginal, + vtkIdType nOriginalCellCount, + QString* pError) +{ + if(pError != NULL) { + pError->clear(); + } + if(pGrid == NULL || pGrid->GetNumberOfCells() <= 0 || + pGrid->GetNumberOfCells() > INT_MAX || + static_cast(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 pOriginalCellIds = + vtkSmartPointer::New(); + pOriginalCellIds->SetName(originalCellIdArrayName()); + pOriginalCellIds->SetNumberOfComponents(1); + pOriginalCellIds->SetNumberOfTuples( + static_cast(vecVtkToOriginal.size())); + for(int nVtkCellId = 0; + nVtkCellId < vecVtkToOriginal.size(); ++nVtkCellId) { + pOriginalCellIds->SetValue( + static_cast(nVtkCellId), + vecVtkToOriginal[nVtkCellId]); + } + + vtkSmartPointer pOriginalCellCount = + vtkSmartPointer::New(); + pOriginalCellCount->SetName(originalCellCountArrayName()); + pOriginalCellCount->SetNumberOfComponents(1); + pOriginalCellCount->SetNumberOfTuples(1); + pOriginalCellCount->SetValue(0, 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* pVtkToOriginal, + QVector* 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."); + } + + vtkIdTypeArray* pOriginalCellIds = + vtkIdTypeArray::SafeDownCast(pOriginalCellIdsAbstract); + vtkIdTypeArray* pOriginalCellCount = + vtkIdTypeArray::SafeDownCast(pOriginalCellCountAbstract); + if(pOriginalCellIds == NULL || pOriginalCellCount == NULL) { + return failRead(pError, + "PEBI cell mapping arrays have invalid types."); + } + + 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."); + } + + const vtkIdType nOriginalCellCount = pOriginalCellCount->GetValue(0); + QVector vecVtkToOriginal( + static_cast(nVtkCellCount)); + for(vtkIdType nVtkCellId = 0; + nVtkCellId < nVtkCellCount; ++nVtkCellId) { + vecVtkToOriginal[static_cast(nVtkCellId)] = + pOriginalCellIds->GetValue(nVtkCellId); + } + + QVector 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; +}