第 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 都会以数据集的单元为依据。
几何位置和拓扑关系的区别也影响修改行为:
- 修改点 2 的坐标,会改变两个共享它的三角形形状。
- 修改单元 1 的点 ID,会改变连接方式,不一定改变任何点坐标。
- 只移动 Actor,会改变屏幕中的放置位置,却不改变点坐标和连接数组。
本章小结
<code>vtkPoints</code> 存空间坐标,<code>vtkCellArray</code> 存点如何组成单元,<code>vtkPolyData</code> 将两者和后续的属性数组组织成一个数据对象。下一章给 4 个点和 2 个单元分别赋值,观察相同的几何结构怎样呈现两种完全不同的数值图像。