第 9 章:vtkPolyData、vtkPoints 与 vtkCellArray

学习目标

本章手工建立一个由两个三角形组成的正方形。读完后,你应该能分别说出点坐标和拓扑连接存在哪里,读懂 <code>vtkCellArray</code> 的编码,并解释“4 个点、2 个单元、8 个连接数组数字”为什么可以同时成立。

9.1 几何位置与连接关系是两类信息

假设正方形四角分别为:

点 ID 坐标
0 (-1, -1, 0)
1 ( 1, -1, 0)
2 ( 1, 1, 0)
3 (-1, 1, 0)

仅有这四个坐标,程序仍不知道该画一条折线、一个四边形,还是两个三角形。还需要拓扑连接。本例规定两个三角形:

单元 ID 使用的点 ID
0 0、1、2
1 0、2、3

两者共享点 0 与点 2,沿对角线形成完整正方形。这里的“点”是数据集中的唯一点,而不是每个三角形各自复制一份 Three.js 顶点。

9.2 用 vtkPoints 保存坐标

阶段示例中的代码是:

const points = vtkPoints.newInstance();
points.setData(new Float32Array([
  -1, -1, 0, // point 0
   1, -1, 0, // point 1
   1,  1, 0, // point 2
  -1,  1, 0, // point 3
]), 3);

<code>setData(…, 3)</code> 的第二个参数表示一个点由 3 个分量组成,即 x、y、z。底层数组有 12 个数,对应 4 个点。可用 <code>points.getPoint(2)</code> 验证第 2 个点为 <code>[1, 1, 0]</code>。

<code>vtkPoints</code> 关注空间坐标;温度、压力等属性通常另外存为 <code>vtkDataArray</code>,并挂到数据集的 Point Data。不要把所有与点有关的数字都塞进坐标数组。

9.3 用 vtkCellArray 保存三角形

本例的连接数组为:

const polys = vtkCellArray.newInstance();
polys.setData(new Uint32Array([
  3, 0, 1, 2, // triangle 0
  3, 0, 2, 3, // triangle 1
]));

读法是:每个单元先写所含点的个数,再写相应的点 ID。因此第一个 <code>3</code> 不是点 ID,而是说“接下来 3 个点 ID 构成这个单元”。一共两个三角形,每个占 <code>1 + 3</code> 个数字,所以连接数组长度为 8。

你也可以逐个插入单元:

polys.insertNextCell([0, 1, 2]);
polys.insertNextCell([0, 2, 3]);

此方法传入的是点 ID 列表,不需要自己在数组前写 3。与直接 <code>setData</code> 相比,它适合逐步构造;已知完整连接数组时,直接写 TypedArray 更便于检查和批量创建。

连接数组中的点 ID 必须能在 <code>vtkPoints</code> 中找到。若只定义了点 0–3,却写入点 ID 8,数据就不再有有效的拓扑意义。

9.4 用 vtkPolyData 组合它们

const polyData = vtkPolyData.newInstance();
polyData.setPoints(points);
polyData.setPolys(polys);

随后:

polyData.getNumberOfPoints(); // 4
polyData.getNumberOfCells();  // 2
polyData.getPoints();         // vtkPoints
polyData.getPolys();          // vtkCellArray

<code>vtkPolyData</code> 还可容纳顶点单元 <code>verts</code>、线单元 <code>lines</code> 和三角带 <code>strips</code>。所以 PolyData 并不等于“只存三角形的对象”。在只包含两块 <code>polys</code> 的本例中,单元 0 和 1 的对应关系很直接;混合多种单元类型后,应特别检查各类单元的顺序和数据数组归属,不要凭直觉填 Cell Data。

9.5 与 Three.js 的索引几何比较

你可以把点 ID 类比成索引几何中的顶点索引,但 vtk.js 的 <code>vtkCellArray</code> 明确编码了“一个单元有多少个点”。它可以表达线、多边形等不同类型的单元,而不只是一串每三个索引成一组的三角形。

对于表面显示,Mapper 最终仍要把这些单元交给 GPU 绘制;必要时多边形会被转换为适合绘制的三角形。但原始 <code>vtkPolyData</code> 的单元结构仍有意义:裁剪、选取、统计和 Cell Data 都会以数据集的单元为依据。

几何位置和拓扑关系的区别也影响修改行为:

本章小结

<code>vtkPoints</code> 存空间坐标,<code>vtkCellArray</code> 存点如何组成单元,<code>vtkPolyData</code> 将两者和后续的属性数组组织成一个数据对象。下一章给 4 个点和 2 个单元分别赋值,观察相同的几何结构怎样呈现两种完全不同的数值图像。