第 10 章:Point Data 与 Cell Data
学习目标
你已经会构造 4 个点和 2 个三角形。本章给点与单元分别附加标量,再把同一份几何数据用两种方式显示。读完后,你应该能按实体数量检查数组长度,解释连续渐变与分块颜色从何而来,并分清“数组已存入数据集”和“Mapper 正在使用该数组”。
10.1 一个正方形,两套数值
第 9 章的数据包含点 0–3、单元 0–1。我们为每个点各给一个温度值:
| 点 ID | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| PointTemperature | 0 | 0 | 1 | 1 |
再为每个三角形各给一个区域值:
| 单元 ID | 0 | 1 |
|---|---|---|
| CellRegion | 0 | 1 |
Point Data 要有 4 个元组,因为数据集有 4 个点;Cell Data 要有 2 个元组,因为有 2 个三角形。这里比较的是元组数,不是 TypedArray 的原始数字个数。若一个点数组每个元组有 3 个分量,那么 4 个点就需要 12 个底层数字。
10.2 把数组挂到正确位置
第 3 阶段示例使用:
const pointTemperature = vtkDataArray.newInstance({
name: "PointTemperature",
numberOfComponents: 1,
values: new Float32Array([0, 0, 1, 1]),
});
polyData.getPointData().setScalars(pointTemperature);
const cellRegion = vtkDataArray.newInstance({
name: "CellRegion",
numberOfComponents: 1,
values: new Float32Array([0, 1]),
});
polyData.getCellData().setScalars(cellRegion);
<code>getPointData()</code> 与 <code>getCellData()</code> 返回不同的属性集合。<code>setScalars</code> 在相应集合中设置当前活动标量。因此,<code>pointTemperature</code> 的第 2 个元组对应点 2,<code>cellRegion</code> 的第 1 个元组对应单元 1。
同一集合可以容纳多个数组。示例还把三分量 <code>Velocity</code> 加入 Point Data:
polyData.getPointData().addArray(velocity);
这不会把 Velocity 自动变成当前显示颜色。它只是让数据集同时保存另一组物理量。后续可以根据需要选择数组或进行计算。
10.3 为什么点数据会出现渐变
左侧图使用点标量。每个三角形的三个角都带有一个数值。渲染表面时,颜色可以在三角形内部平滑变化。Mapper 可以先将点值映射为颜色、再插值颜色,也可以在特定设置下先插值标量、再查颜色表;这两种顺序在非线性颜色映射下可能产生不同结果。两个三角形共享点 0 和点 2,因此公共对角线上的数值保持连续。
右侧图使用单元标量。每个三角形只有一个所属值:单元 0 是 0,单元 1 是 1。它们沿公共边形成明确分界,不需要在单元内部从一个点值渐变到另一个点值。

图中的红蓝对应本示例默认颜色映射的两端;具体颜色不是 Point Data 或 Cell Data 的定义。真正重要的是左侧连续、右侧分块,以及两侧几何体的点和三角形完全相同。
科学含义也不同。假如温度由传感器或计算方法定义在网格点上,插值可能是合理的。假如数值代表每个有限元单元独立的材料编号,硬要插值反而会制造并不存在的“中间材料”。你应先决定数值属于哪里,再考虑画面是否好看。
10.4 数据已存在,还需要 Mapper 选择
本例使用两个 Mapper 指向同一份 <code>vtkPolyData</code>:
const pointMapper = vtkMapper.newInstance();
pointMapper.setInputData(polyData);
pointMapper.setScalarModeToUsePointData();
pointMapper.setScalarVisibility(true);
pointMapper.setScalarRange(0, 1);
const cellMapper = vtkMapper.newInstance();
cellMapper.setInputData(polyData);
cellMapper.setScalarModeToUseCellData();
cellMapper.setScalarVisibility(true);
cellMapper.setScalarRange(0, 1);
两个 Actor 分别持有这两个 Mapper,因此左、右两侧能同时显示不同关联的数据。这里提前使用了 Mapper 的几个标量设置;第 12 和第 14 章会完整解释颜色映射和更多数组选择方式。本章只需抓住:数据集保存数值,Mapper 决定当前用哪一类数值显示。
若关闭 <code>setScalarVisibility</code>,Mapper 不再按这些标量着色,Actor 的单一表面颜色会重新成为主要外观设置。这也解释了为什么修改 <code>actor.getProperty().setColor(…)</code> 有时看起来不起作用:当前标量映射可能正在控制颜色。
10.5 Field Data 与“属性”一词
vtk.js 数据对象还可以带 Field Data,它适合保存不按每个点或每个单元逐一对应的信息。此时不要因为它也叫“数据数组”,就把它当作可直接按点插值的温度场。
在阅读 API 时,“属性”可能指数据集的数值数组,也可能指 Actor 的 <code>vtkProperty</code>。两者不是同一层:
- Point/Cell/Field Data:输入数据具有哪些值。
- <code>vtkProperty</code>:这个可见对象采用什么表面外观。
- Mapper:从数据值到画面颜色的连接与选择。
10.6 最常见的错误
| 错误 | 后果或检查方法 |
|---|---|
| 4 个点却只给 2 个 Point Data 元组 | 数组长度与点数不匹配;先检查 <code>getNumberOfTuples()</code> |
| 2 个单元却给 4 个 Cell Data 元组 | 数组归属可能选错;检查是否本想使用 Point Data |
| 数组在 Point Data,却让 Mapper 使用 Cell Data | 画面可能无法按预期着色 |
| 只设置 Actor 颜色却没看到变化 | 检查 Mapper 是否启用了标量着色 |
| 根据画面渐变反推物理意义 | 先核对原始数据是点值还是单元值 |
本章小结
一个数值数组必须与它所描述的实体关联。Point Data 和 Cell Data 的差别会直接改变插值与画面结果。下一章转向另一种空间结构:<code>vtkImageData</code> 不列出每个点坐标,而是用规则网格参数推算采样位置。