奇怪的概念
TTK和VTK
TTK(The Topology ToolKit)是一个用于拓扑数据分析的开源库,专注于处理和分析复杂数据集中的拓扑结构。它提供了各种算法和工具,用于从数据集中提取和分析拓扑特征。TTK 是一个功能强大的库,能够处理大规模的数据集,并用于科学计算和可视化。
VTK(Visualization Toolkit)是一个开源的库,用于科学数据的可视化。VTK 提供了广泛的功能,包括数据处理、数据可视化和图形渲染。它是科学计算和可视化领域中非常重要的工具之一。
TTK 是一个拓扑分析工具包,专注于从复杂数据集中提取和分析拓扑特征。
VTK 是一个科学数据可视化工具,提供数据处理和渲染功能。
TTK 使用 VTK 提供的基础设施来实现高级拓扑分析,VTK 则通过 TTK 扩展了其功能以支持更多的数据分析和可视化应用。
过滤器
简单的说就是一个程序(好像是对象),对输入进行处理,变成输出。
在 VTK(Visualization Toolkit)中,过滤器是一种处理数据的对象,它可以对输入数据进行操作或转换,并生成输出数据。可以将其视为数据处理的“管道”或“处理单元”。每个 VTK 过滤器通常执行一个具体的任务,比如数据转换、数据处理、数据分析等。
vtk module
方便编译用的,说明当前模块的依赖项,使得在编译的时候只需要编译依赖的即可。
vtk.module
文件 是描述一个 VTK 模块的配置文件,定义了模块的基本信息、依赖关系和构建要求。在该文件中,包含了模块的名称、版本号、依赖的其他模块,以及构建时需要的编译标志。
1 2 3 4 5 6
| NAME VTK::FiltersSources
DEPENDS VTK::CommonDataModel VTK::CommonCore
|
这个文件告诉构建系统:
- 模块名是
VTK::FiltersSources
。
- 这个模块依赖于两个其他模块:
VTK::CommonDataModel
和 VTK::CommonCore
。
vtk.module 文件中的关键部分
- NAME:表示模块的名称。通常模块名称与其所在的路径相关联。
- DEPENDS:列出该模块依赖的其他模块。这些模块需要在编译时一同加载。
- PRIVATE_DEPENDS:列出在模块内部使用的依赖模块(这些依赖不会暴露给模块的使用者)。
VTK module 是一个用于管理 VTK 代码依赖、组织和编译过程的模块化系统,通过 vtk.module
文件来定义模块的依赖和功能。TTK module类似。
三角化对象
把复杂的几何对象转化为便于计算的三角化对象。
三角化对象(ttk::Triangulation
)用于表示输入数据集(vtkDataSet
)的几何和拓扑结构。具体来说,它将输入的几何对象(例如点、边、面等)转化为一种便于计算的格式,即三角网格。这个三角化对象可以用于后续的几何和拓扑分析,如查找邻居、顶点之间的关系等。
三角化对象的作用就是将复杂的几何结构或数据集分割成较小的三角形或三角面片。这些分割后的三角形或面片构成了三角化对象。在数据处理的过程中,通过操作这些三角化对象,能够简化并加速几何计算,如插值、网格处理、以及其他几何分析。
输入对象、输入数据集、输入数组
数据在不同层次上的组织方式和作用范围。
输入对象 包含了 输入数据集 和 输入数组。输入对象(如 .vti
文件)描述了整体的数据结构,而输入数据集描述了网格点及其连接关系,输入数组则存储了与这些网格点或单元关联的具体数值数据。
.vti
文件就属于输入对象。它是一个 VTK Image Data 格式的文件,用于存储规则网格数据(也称为结构化网格数据)。这种格式存储的是具有规则结构的三维网格数据,通常包括每个网格点的坐标和网格点或网格单元的相关属性(如密度、温度、速度等)。
在VTK中,vtkDataObject
是所有数据对象的基类,vtkDataSet
是其中一种常见的子类,表示具体的数据集形式,如网格数据、图像数据等。.vti
文件 就是一个 输入对象,属于 vtkImageData
类型,它是 vtkDataSet
的子类。
输入数据集是指输入对象中的几何和拓扑结构信息,它描述了数据的空间分布和组织形式。VTK支持多种类型的数据集,例如:
vtkImageData
(结构化网格,规则网格,如 .vti
文件)
vtkUnstructuredGrid
(非结构化网格)
vtkPolyData
(多边形数据)
vtkRectilinearGrid
(直角网格)
这些数据集对象定义了点(顶点)和单元(如三角形、四边形)的空间位置以及它们之间的连接关系,但不包含具体的数值数据(如标量场或矢量场),这些数值数据由输入数组表示。
输入数组表示存储在数据集中的具体数值信息,如标量场或矢量场。每个数组可能包含与数据集的点或单元关联的不同属性(如每个顶点的温度或每个单元的压力)。在 VTK 中,这些数组可以是:
- 点数据数组(PointData):每个点(顶点)的属性。
- 单元数据数组(CellData):每个单元(如三角形、四边形等)的属性。
在 .vti
文件中,通常包含了一个或多个输入数组,用来存储与每个网格点(或单元)相关的属性值,例如密度、压力、温度等。代码中使用的 GetInputArrayToProcess
方法就是为了获取这样的一个数组。
ttkhelloworld.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
#pragma once
#include <ttkHelloWorldModule.h>
#include <ttkAlgorithm.h>
#include <HelloWorld.h>
class TTKHELLOWORLD_EXPORT ttkHelloWorld : public ttkAlgorithm , protected ttk::HelloWorld { private:
std::string OutputArrayName{"AveragedScalarField"};
public:
vtkSetMacro(OutputArrayName, const std::string &); vtkGetMacro(OutputArrayName, std::string);
static ttkHelloWorld *New(); vtkTypeMacro(ttkHelloWorld, ttkAlgorithm);
protected:
ttkHelloWorld(); ~ttkHelloWorld() override = default;
int FillInputPortInformation(int port, vtkInformation *info) override;
int FillOutputPortInformation(int port, vtkInformation *info) override;
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override; };
|
override
用于标识一个成员函数是用于重写基类中的虚函数。目的是提高代码的安全性、可维护性、可读性。
在 override = default
的情况下,default
用于告诉编译器使用默认的实现。这通常用于默认构造函数、拷贝构造函数、析构函数等。
ttkHelloWorld.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
| #include <ttkHelloWorld.h>
#include <vtkInformation.h>
#include <vtkDataArray.h> #include <vtkDataSet.h> #include <vtkObjectFactory.h> #include <vtkPointData.h> #include <vtkSmartPointer.h>
#include <ttkMacros.h> #include <ttkUtils.h>
vtkStandardNewMacro(ttkHelloWorld);
ttkHelloWorld::ttkHelloWorld() { this->SetNumberOfInputPorts(1); this->SetNumberOfOutputPorts(1); }
int ttkHelloWorld::FillInputPortInformation(int port, vtkInformation *info) { if(port == 0) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet"); return 1; } return 0; }
int ttkHelloWorld::FillOutputPortInformation(int port, vtkInformation *info) { if(port == 0) { info->Set(ttkAlgorithm::SAME_DATA_TYPE_AS_INPUT_PORT(), 0); return 1; } return 0; }
int ttkHelloWorld::RequestData(vtkInformation *ttkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) {
vtkDataSet *inputDataSet = vtkDataSet::GetData(inputVector[0]); if(!inputDataSet) return 0;
vtkDataArray *inputArray = this->GetInputArrayToProcess(0, inputVector); if(!inputArray) { this->printErr("无法检索输入数组。"); return 0; }
if(this->GetInputArrayAssociation(0, inputVector) != 0) { this->printErr("输入数组需要是点数据数组。"); return 0; } if(inputArray->GetNumberOfComponents() != 1) { this->printErr("输入数组需要是标量数组。"); return 0; }
this->printMsg("开始计算..."); this->printMsg(" 标量数组: " + std::string(inputArray->GetName()));
vtkSmartPointer<vtkDataArray> const outputArray = vtkSmartPointer<vtkDataArray>::Take(inputArray->NewInstance()); outputArray->SetName(this->OutputArrayName.data()); outputArray->SetNumberOfComponents(1); outputArray->SetNumberOfTuples(inputArray->GetNumberOfTuples());
ttk::Triangulation *triangulation = ttkAlgorithm::GetTriangulation(inputDataSet); if(!triangulation) return 0;
this->preconditionTriangulation(triangulation);
int status = 0; ttkVtkTemplateMacro(inputArray->GetDataType(), triangulation->getType(), (status = this->computeAverages<VTK_TT, TTK_TT>( (VTK_TT *)ttkUtils::GetVoidPointer(outputArray), (VTK_TT *)ttkUtils::GetVoidPointer(inputArray), (TTK_TT *)triangulation->getData())));
if(status != 1) return 0;
vtkDataSet *outputDataSet = vtkDataSet::GetData(outputVector, 0);
outputDataSet->ShallowCopy(inputDataSet);
outputDataSet->GetPointData()->AddArray(outputArray);
return 1; }
|
vtkStandardNewMacro
1 2 3
|
vtkStandardNewMacro(ttkHelloWorld);
|
vtkStandardNewMacro
是 VTK (Visualization Toolkit) 中一个常用的宏,用于自动生成标准的构造函数和 New
方法。
构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13
|
ttkHelloWorld::ttkHelloWorld() { this->SetNumberOfInputPorts(1); this->SetNumberOfOutputPorts(1); }
|
设置输入端口指定的数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13
|
int ttkHelloWorld::FillInputPortInformation(int port, vtkInformation *info) { if(port == 0) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet"); return 1; } return 0; }
|
通过 FillInputPortInformation
方法为 VTK 过滤器的输入端口指定所需的数据类型。
输出端口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
int ttkHelloWorld::FillOutputPortInformation(int port, vtkInformation *info) { if(port == 0) { info->Set(ttkAlgorithm::SAME_DATA_TYPE_AS_INPUT_PORT(), 0); return 1; } return 0; }
|
当 port == 0
时,函数将指定输出端口 0 的数据类型与输入端口 0 的数据类型相同,使用的是 ttkAlgorithm::SAME_DATA_TYPE_AS_INPUT_PORT()
关键字。
ttkHelloWorld::RequestData
函数
它是 VTK 过滤器管道中的一个核心方法,用于执行过滤器的主要计算逻辑。该函数负责从输入中提取数据、进行预处理、调用实际计算代码,并将计算结果放入输出对象。
这段代码从输入数据集中提取标量数组,进行预处理,并调用 computeAverages
函数进行计算。计算结果被存储到输出数组中,并最终添加到输出数据集中。
函数定义
1 2 3
| int ttkHelloWorld::RequestData(vtkInformation *ttkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector)
|
步骤1:获取输入数据对象
1 2 3 4 5
|
vtkDataSet *inputDataSet = vtkDataSet::GetData(inputVector[0]); if(!inputDataSet) return 0;
|
调用 vtkDataSet::GetData(inputVector[0])
,从输入向量的第 0 号端口中获取输入数据对象(必须是 vtkDataSet
类型,已在 FillInputPortInformation
方法中指定)。
步骤 2:获取输入数组
1 2 3 4 5
| vtkDataArray *inputArray = this->GetInputArrayToProcess(0, inputVector); if(!inputArray) { this->printErr("无法检索输入数组。"); return 0; }
|
使用 GetInputArrayToProcess(0, inputVector)
获取输入数组。第一个参数 0
表示这是第一个数组。
步骤 3:检查输入数组的格式
1 2 3 4 5 6 7 8 9
| if(this->GetInputArrayAssociation(0, inputVector) != 0) { this->printErr("输入数组需要是点数据数组。"); return 0; } if(inputArray->GetNumberOfComponents() != 1) { this->printErr("输入数组需要是标量数组。"); return 0; }
|
检查输入数组的关联性,确保它是点数据(0
表示点数据),检查数组是否是标量数组,即每个元组只能有一个分量。如果 GetNumberOfComponents()
返回的组件数量不是 1,则返回错误。
步骤 4:输出日志信息
1 2
| this->printMsg("开始计算..."); this->printMsg(" 标量数组: " + std::string(inputArray->GetName()));
|
步骤 5:创建输出数组
1 2 3 4 5 6 7 8
|
vtkSmartPointer<vtkDataArray> const outputArray = vtkSmartPointer<vtkDataArray>::Take(inputArray->NewInstance()); outputArray->SetName(this->OutputArrayName.data()); outputArray->SetNumberOfComponents(1); outputArray->SetNumberOfTuples(inputArray->GetNumberOfTuples());
|
- 使用
vtkSmartPointer
创建一个新的输出数组,类型与输入数组相同。NewInstance()
创建与输入数组相同类型的对象。
- 设置输出数组的名称和组件数量(1),并为输出数组分配与输入数组相同数量的元组。
步骤 6:获取和预处理三角化对象
1 2 3 4 5 6 7 8
| ttk::Triangulation *triangulation = ttkAlgorithm::GetTriangulation(inputDataSet); if(!triangulation) return 0;
this->preconditionTriangulation(triangulation);
|
- 调用
ttkAlgorithm::GetTriangulation(inputDataSet)
获取输入数据集的三角化对象(ttk::Triangulation
),如果不存在则创建它。如果获取失败,则返回 0
。
- 调用
this->preconditionTriangulation(triangulation)
对三角化对象进行预处理,这在基类中实现,可能涉及准备一些邻接表等数据结构。
步骤 7:调用计算逻辑
1 2 3 4 5 6 7 8 9 10 11
| int status = 0; ttkVtkTemplateMacro(inputArray->GetDataType(), triangulation->getType(), (status = this->computeAverages<VTK_TT, TTK_TT>( (VTK_TT *)ttkUtils::GetVoidPointer(outputArray), (VTK_TT *)ttkUtils::GetVoidPointer(inputArray), (TTK_TT *)triangulation->getData())));
if(status != 1) return 0;
|
ttkVtkTemplateMacro
是一个宏,用于处理不同的数据类型(模板化处理)。
- 宏会根据
inputArray
和 triangulation
的数据类型调用 computeAverages
方法,执行实际的计算。computeAverages
方法使用模板类型来进行数据操作。
ttkUtils::GetVoidPointer
获取数组的原始数据指针(C 风格的指针)。
标量值是指从输入数组中获取的点数据(例如温度、压力等物理量)的具体数值。computeAverages
函数负责计算三角化对象中每个顶点的标量值的平均值。具体来说,对于每个顶点,程序会找到与该顶点相邻的其他顶点,获取它们的标量值,然后计算这些邻居点的标量值的平均值,作为该顶点的新标量值。
步骤 8:输出数据处理
1 2 3 4 5 6 7 8
| vtkDataSet *outputDataSet = vtkDataSet::GetData(outputVector, 0);
outputDataSet->ShallowCopy(inputDataSet);
outputDataSet->GetPointData()->AddArray(outputArray);
|
- 从
outputVector
中获取输出数据集,并进行浅拷贝,意味着 outputDataSet
将复制输入数据集的结构信息。
- 将计算得到的输出数组添加到输出数据集的点数据中。
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
#include <CommandLineParser.h> #include <ttkHelloWorld.h>
#include <vtkCellData.h> #include <vtkDataArray.h> #include <vtkDataSet.h> #include <vtkPointData.h> #include <vtkSmartPointer.h> #include <vtkXMLDataObjectWriter.h> #include <vtkXMLGenericDataObjectReader.h>
int main(int argc, char **argv) {
std::vector<std::string> inputFilePaths; std::vector<std::string> inputArrayNames; std::string outputArrayName{"AveragedArray"}; std::string outputPathPrefix{"output"}; bool listArrays{false};
{ ttk::CommandLineParser parser;
parser.setArgument( "i", &inputFilePaths, "Input data-sets (*.vti, *vtu, *vtp)", false); parser.setArgument("a", &inputArrayNames, "Input array names", true); parser.setArgument( "o", &outputPathPrefix, "Output file prefix (no extension)", true); parser.setOption("l", &listArrays, "List available arrays");
parser.setArgument("O", &outputArrayName, "Output array name", true);
parser.parse(argc, argv); }
ttk::Debug msg; msg.setDebugMsgPrefix("HelloWorld");
auto helloWorld = vtkSmartPointer<ttkHelloWorld>::New();
vtkDataArray *defaultArray = nullptr; for(size_t i = 0; i < inputFilePaths.size(); i++) { auto reader = vtkSmartPointer<vtkXMLGenericDataObjectReader>::New(); reader->SetFileName(inputFilePaths[i].data()); reader->Update();
auto inputDataObject = reader->GetOutput(); if(!inputDataObject) { msg.printErr("Unable to read input file `" + inputFilePaths[i] + "' :("); return 1; }
auto inputAsVtkDataSet = vtkDataSet::SafeDownCast(inputDataObject);
if(listArrays) { msg.printMsg(inputFilePaths[i] + ":"); if(inputAsVtkDataSet) { msg.printMsg(" PointData:"); auto pointData = inputAsVtkDataSet->GetPointData(); for(int j = 0; j < pointData->GetNumberOfArrays(); j++) msg.printMsg(" - " + std::string(pointData->GetArrayName(j)));
msg.printMsg(" CellData:"); auto cellData = inputAsVtkDataSet->GetCellData(); for(int j = 0; j < cellData->GetNumberOfArrays(); j++) msg.printMsg(" - " + std::string(cellData->GetArrayName(j))); } else { msg.printErr("Unable to list arrays on file `" + inputFilePaths[i] + "'"); return 1; } } else { helloWorld->SetInputDataObject(i, reader->GetOutput());
if(!defaultArray) { defaultArray = inputAsVtkDataSet->GetPointData()->GetArray(0); if(!defaultArray) defaultArray = inputAsVtkDataSet->GetCellData()->GetArray(0); } } }
if(listArrays) { return 0; }
if(!inputArrayNames.size()) { if(defaultArray) inputArrayNames.emplace_back(defaultArray->GetName()); } for(size_t i = 0; i < inputArrayNames.size(); i++) helloWorld->SetInputArrayToProcess(i, 0, 0, 0, inputArrayNames[i].data());
helloWorld->Update();
if(!outputPathPrefix.empty()) { for(int i = 0; i < helloWorld->GetNumberOfOutputPorts(); i++) { auto output = helloWorld->GetOutputDataObject(i); auto writer = vtkSmartPointer<vtkXMLWriter>::Take( vtkXMLDataObjectWriter::NewWriter(output->GetDataObjectType()));
std::string outputFileName = outputPathPrefix + "_port_" + std::to_string(i) + "." + writer->GetDefaultFileExtension(); msg.printMsg("Writing output file `" + outputFileName + "'..."); writer->SetInputDataObject(output); writer->SetFileName(outputFileName.data()); writer->Update(); } }
return 0; }
|
头文件
1 2 3 4 5 6 7 8 9 10 11 12
| #include <CommandLineParser.h> #include <ttkHelloWorld.h>
#include <vtkCellData.h> #include <vtkDataArray.h> #include <vtkDataSet.h> #include <vtkPointData.h> #include <vtkSmartPointer.h> #include <vtkXMLDataObjectWriter.h> #include <vtkXMLGenericDataObjectReader.h>
|
- TTK 相关头文件:
CommandLineParser.h
用于解析命令行参数,ttkHelloWorld.h
是 TTK 的示例过滤器类,负责执行简单的计算任务。
- VTK 相关头文件:用于处理 VTK 格式的网格数据。
vtkCellData
和 vtkPointData
用于访问单元和点上的数据数组,vtkDataArray
表示数据数组,vtkDataSet
表示数据集。vtkSmartPointer
是 VTK 中常用的智能指针,用于自动管理内存。vtkXMLGenericDataObjectReader
是用于读取通用 VTK XML 格式数据的读取器,vtkXMLDataObjectWriter
用于将数据写回 VTK 文件。
程序变量声明部分
1 2 3 4 5 6 7
|
std::vector<std::string> inputFilePaths; std::vector<std::string> inputArrayNames; std::string outputArrayName{"AveragedArray"}; std::string outputPathPrefix{"output"}; bool listArrays{false};
|
命令行参数解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
{ ttk::CommandLineParser parser;
parser.setArgument( "i", &inputFilePaths, "Input data-sets (*.vti, *vtu, *vtp)", false); parser.setArgument("a", &inputArrayNames, "Input array names", true); parser.setArgument( "o", &outputPathPrefix, "Output file prefix (no extension)", true); parser.setOption("l", &listArrays, "List available arrays");
parser.setArgument("O", &outputArrayName, "Output array name", true);
parser.parse(argc, argv); }
|
-i
:指定输入文件路径,可以接受多个文件。
-a
:指定要处理的数组名称。
-o
:指定输出文件的前缀。
-l
:列出输入文件中的可用数组。
-O
:指定输出数组的名称。
调试信息输出设置
1 2 3 4
|
ttk::Debug msg; msg.setDebugMsgPrefix("HelloWorld");
|
TTK 中的 Debug
类用于调试信息的输出。这里设置了调试信息前缀为 "HelloWorld"
,后续的调试信息都会带上这个前缀。
初始化 TTK 模块
1 2 3
|
auto helloWorld = vtkSmartPointer<ttkHelloWorld>::New();
|
读取输入文件并解析数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| vtkDataArray *defaultArray = nullptr; for(size_t i = 0; i < inputFilePaths.size(); i++) { auto reader = vtkSmartPointer<vtkXMLGenericDataObjectReader>::New(); reader->SetFileName(inputFilePaths[i].data()); reader->Update();
auto inputDataObject = reader->GetOutput(); if(!inputDataObject) { msg.printErr("Unable to read input file `" + inputFilePaths[i] + "' :("); return 1; }
|
这段代码的核心功能是读取用户指定的 VTK 格式文件并确保文件读取成功。
defaultArray
是指向 vtkDataArray
类型的指针,初始值为 nullptr
。这个指针将用于保存第一个找到的默认数组(点数据或单元数据),如果用户没有指定特定的数组,程序将使用这个默认数组进行处理。
循环遍历 inputFilePaths
,程序读取用户通过命令行传入的所有 VTK 格式文件。inputFilePaths
是一个字符串数组,存储了输入文件的路径。
初始化 vtkXMLGenericDataObjectReader
读取器。vtkXMLGenericDataObjectReader
是一个 VTK 中的读取器类,它可以解析和读取 XML 格式的 VTK 数据对象(如 .vti
, .vtu
, .vtp
等)。reader->SetFileName(inputFilePaths[i].data());
设置要读取的文件路径,这里通过 inputFilePaths[i].data()
获取当前文件路径的字符指针。reader->Update();
执行更新操作,正式读取文件内容。
最后检查文件是否成功读取。reader->GetOutput();
获取读取的 VTK 数据对象。如果 inputDataObject
为空(即文件读取失败),则会通过 msg.printErr
打印错误信息,并且程序返回 1
,表示程序执行错误。
读取VTK数据对象,并根据用户的选择执行不同的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| auto inputAsVtkDataSet = vtkDataSet::SafeDownCast(inputDataObject);
if(listArrays) { msg.printMsg(inputFilePaths[i] + ":"); if(inputAsVtkDataSet) { msg.printMsg(" PointData:"); auto pointData = inputAsVtkDataSet->GetPointData(); for(int j = 0; j < pointData->GetNumberOfArrays(); j++) msg.printMsg(" - " + std::string(pointData->GetArrayName(j)));
msg.printMsg(" CellData:"); auto cellData = inputAsVtkDataSet->GetCellData(); for(int j = 0; j < cellData->GetNumberOfArrays(); j++) msg.printMsg(" - " + std::string(cellData->GetArrayName(j))); } else { msg.printErr("Unable to list arrays on file `" + inputFilePaths[i] + "'"); return 1; } } else { helloWorld->SetInputDataObject(i, reader->GetOutput());
if(!defaultArray) { defaultArray = inputAsVtkDataSet->GetPointData()->GetArray(0); if(!defaultArray) defaultArray = inputAsVtkDataSet->GetCellData()->GetArray(0); } }
|
这段代码主要负责根据用户输入的命令行参数,判断是否需要打印输入文件中的点数据和单元数据的数组列表。如果打印,则输出每个文件中包含的数组名称;否则,将输入数据传递给 ttkHelloWorld
模块,并选取默认的点或单元数据数组作为处理的目标。
如果 defaultArray
还没有被初始化,选择输入数据中的一个默认数组作为处理的目标。
- 首先尝试从点数据中获取第一个数组
GetPointData()->GetArray(0)
。
- 如果点数据中没有数组,则尝试从单元数据中获取
GetCellData()->GetArray(0)
。
目的是确保有一个默认的数组可以被处理,以防用户没有指定特定的数组。
指定要处理的输入数组
1 2 3 4 5 6 7 8 9
|
if(!inputArrayNames.size()) { if(defaultArray) inputArrayNames.emplace_back(defaultArray->GetName()); } for(size_t i = 0; i < inputArrayNames.size(); i++) helloWorld->SetInputArrayToProcess(i, 0, 0, 0, inputArrayNames[i].data());
|
检查用户是否指定了数组名称,如果没有,选择一个默认数组。遍历所有数组,将它们传递给模块进行处理。
SetInputArrayToProcess
是 VTK(Visualization Toolkit)中一个常见的方法,用于指定输入数据中哪一个数组将被用于处理。
在 VTK 中,数据通常分为两类:
- 点数据(PointData):存储与几何体的顶点(即点)相关的属性。例如,每个顶点的颜色、法线等。
- 单元数据(CellData):存储与几何体的单元(例如多边形、四面体等)相关的属性。
每个数据集可以有多个点数据数组和单元数据数组。因此,在处理数据时,我们需要指定:
- 我们要处理的是点数据还是单元数据。
- 我们要处理的是哪个具体的数组。
1 2 3 4 5 6 7
| helloWorld->SetInputArrayToProcess( i, 0, 0, 0, inputArrayNames[i].data() );
|
参数解释:
i
(第 i
个输入端口):
0
(索引类型):
用来指定索引的类型。常见的值包括:
0
表示点数据(vtkDataObject::FIELD_ASSOCIATION_POINTS
)。
1
表示单元数据(vtkDataObject::FIELD_ASSOCIATION_CELLS
)。
0
(输入索引):
- 在复杂的 VTK 管道中,过滤器可以同时接受多个输入。这是指定要处理的输入数据集的索引(从
0
开始计数)。
- 对于单一输入的过滤器(如这里的
ttkHelloWorld
),输入索引通常设置为 0
,表示第一个输入数据集。
0
(字段类别):
- 用于区分输入数据的字段类别。常见的值有:
0
表示 vtkDataObject::POINT_DATA
(即点数据)。
1
表示 vtkDataObject::CELL_DATA
(即单元数据)。
inputArrayNames[i].data()
(要处理的数组名称):
- 这里指定了我们要处理的数组的名称。
inputArrayNames[i]
是一个存储数组名称的字符串,data()
函数将这个字符串转换为 C 风格的字符数组(即 char*
),以便传递给 VTK 的内部函数。
执行过滤器
调用 helloWorld->Update()
将触发 ttkHelloWorld
过滤器执行其逻辑,并对用户指定的数组进行处理,调用 helloWorld->Update()
实际上是触发了 ttkHelloWorld::RequestData
函数。
输出数据写入磁盘中的文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
if(!outputPathPrefix.empty()) { for(int i = 0; i < helloWorld->GetNumberOfOutputPorts(); i++) { auto output = helloWorld->GetOutputDataObject(i); auto writer = vtkSmartPointer<vtkXMLWriter>::Take( vtkXMLDataObjectWriter::NewWriter(output->GetDataObjectType()));
std::string outputFileName = outputPathPrefix + "_port_" + std::to_string(i) + "." + writer->GetDefaultFileExtension(); msg.printMsg("Writing output file `" + outputFileName + "'..."); writer->SetInputDataObject(output); writer->SetFileName(outputFileName.data()); writer->Update(); } }
|
这段代码的功能是在 outputPathPrefix
不为空的情况下,将 ttkHelloWorld
过滤器的输出数据写入到磁盘的 XML 文件中。