第 3 阶段练习与参考答案
按教程目录完成第 8–11 章后,运行阶段示例并做以下练习。先手算,再修改代码验证。
练习 1:元组与分量
一个 <code>vtkDataArray</code> 的 <code>values</code> 是 18 个数字,<code>numberOfComponents</code> 是 3。它有多少元组?第 4 个元组从底层数组的哪个下标开始?如果这些数字要作为 Point Data,数据集应有多少个点?
参考答案
元组数为 <code>18 ÷ 3 = 6</code>。按从 0 开始的下标,第 4 个元组是元组 3,从 <code>3 × 3 = 9</code> 开始,占底层下标 9、10、11。若每个点一个三分量元组,数据集应有 6 个点。
练习 2:读懂单元连接数组
一份 <code>vtkPolyData</code> 有 5 个点,连接数组为:
[3, 0, 1, 2, 3, 0, 2, 3, 3, 0, 3, 4]
它有几个多边形单元?数组里每个 <code>3</code> 是什么?如果只为这些单元提供 Cell Data 标量,需要几个元组?
参考答案
它有 3 个三角形单元。每个单元开头的 <code>3</code> 表示随后有 3 个点 ID,不是点 ID 3。Cell Data 每个单元一个标量时,需要 3 个元组。点 ID 的有效范围应是 0–4。
练习 3:让两种着色方式交换趋势
修改示例中的 <code>pointTemperature</code>,让正方形左侧的值为 1、右侧为 0;再把 <code>cellRegion</code> 的两个值交换。观察左侧是否仍连续渐变、右侧是否仍保持分块。
参考答案
按点 0–3 的顺序,左边是点 0 和 3,右边是点 1 和 2。可以设置:
pointTemperature.setTuple(0, [1]);
pointTemperature.setTuple(1, [0]);
pointTemperature.setTuple(2, [0]);
pointTemperature.setTuple(3, [1]);
单元值交换为:
cellRegion.setTuple(0, [1]);
cellRegion.setTuple(1, [0]);
renderWindow.render();
颜色方向改变,但点数据仍在单元内部插值,单元数据仍按三角形分块。这种区别由关联位置决定,不由数值大小决定。
练习 4:手算 ImageData 的位置和值
阶段示例中 dimensions 为 <code>[3,2,2]</code>,origin 为 <code>[-1,2,0]</code>,spacing 为 <code>[0.5,1.5,2]</code>。在默认方向下,求下标 <code>(2,1,1)</code> 的:
- 扁平数组索引;
- 世界坐标;
- 标量值。
再按“旋转网格方向 90°”,比较 <code>(1,0,0)</code> 的坐标与标量。
参考答案
扁平索引为 <code>2 + 3 × (1 + 2 × 1) = 11</code>;世界坐标为 <code>[0,3.5,2]</code>;示例的人造标量为 <code>2 + 10 × 1 + 100 × 1 = 112</code>。旋转前 <code>(1,0,0)</code> 位于 <code>[-0.5,2,0]</code>,旋转后位于 <code>[-1,2.5,0]</code>,标量始终为 1。
练习 5:故意制造并修复一个数组错误
保持示例的 4 个点和 2 个三角形不变,假设有人把 <code>PointTemperature</code> 写成 2 个元组,把 <code>CellRegion</code> 写成 4 个元组。不要只凭画面猜测错误;写出应该检查的 API 和正确数量。
参考答案
用 <code>polyData.getNumberOfPoints()</code> 和 <code>polyData.getNumberOfCells()</code> 分别取得 4 和 2;用两个数组的 <code>getNumberOfTuples()</code> 检查元组数。Point Data 标量应有 4 个元组,Cell Data 标量应有 2 个元组。分量数也必须单独检查,因为底层 TypedArray 的数字个数不直接等于元组数。
练习 6:直接修改底层数值
读取 <code>pointTemperature.getRange()</code> 后,直接把其底层数组的第一个值改为 5。怎样让 vtk.js 的范围缓存和修改状态都更新?
参考答案
const raw = pointTemperature.getData();
raw[0] = 5;
pointTemperature.dataChange();
renderWindow.render();
在本教程版本中,<code>dataChange()</code> 会清除已缓存的范围并标记数组已修改。只调用 <code>modified()</code> 可能保留旧的范围缓存。小量修改也可以使用 <code>setComponent</code> 或 <code>setTuple</code>,它们会通过对象 API 处理变化。