第 7 阶段练习与参考答案
先按教程目录阅读第 22–24 章并运行阶段示例。每题先自己解释或修改代码,再展开参考答案。
练习 1:文件、Reader 和输出对象
你收到三个 URL:/data/scan.vti、/data/surface.vtp、/data/case/index.json。分别选什么 Reader?它们可能输出什么数据对象?为何不能只看输出对象都是 vtkImageData,就用同一个 Reader 读所有入口?
参考答案
.vti 对应 vtkXMLImageDataReader,通常输出 vtkImageData;.vtp 对应 vtkXMLPolyDataReader,通常输出 vtkPolyData;vtk.js 数据集目录的 index.json 对应 vtkHttpDataSetReader,输出由清单的 vtkClass 决定。Reader 解析的是磁盘编码,输出类描述的是内存结构。不同编码可以产生同类内存对象,仍须用各自的解析器。
练习 2:元数据、选择与下载
运行示例,清空 Network 记录并刷新。在未点击按钮前,两个数组的 getArrays() 项目中 enable 是什么?Point Data 中能否用 getArrayByName('Density') 取得实际数值?只执行 reader.enableArray('pointData','Density',true) 后,哪个状态改变?何时出现 data/Density 请求?
参考答案
Reader 以 enableArray:false 创建,因此两项的 enable 都为 false。首次只读元数据,getArrayByName('Density') 返回空值。调用 enableArray 后,Density 的 enable 变为 true,但尚未下载;await reader.loadData() 才请求 data/Density 并把数组注册到 Point Data。Network 结果应以实际请求为准;浏览器缓存可影响显示的传输大小。
练习 3:大小与结构核对
教学图像 extent 是 [0,31,0,31,0,31]。有多少个采样点?有多少个体素单元?一个单分量 Float32Array 应有多少值、多少原始字节?如果清单把 size 误写成 32×32×31,会出现什么风险?
参考答案
每轴 32 个点,共 32³=32768 点。相邻点构成的体素每轴 31 个,共 31³=29791 个体素。单分量 Float32 数组有 32768 个值,原始长度为 32768×4=131072 字节。若 size 写错,Reader 的长度检查会报错或数据解释不一致;后续算法也可能索引到缺失或错位数值。不能用体素数代替 Point Data 的元组数。
练习 4:active scalars 与切换
依次加载 Density 和 Contrast 后,写出切换到 Density 并让等值面使用它的关键代码。这个操作应再次下载 Density 吗?若只在 Reader 中调用 enableArray('pointData','Contrast',false),会使 Contrast 从 Point Data 消失吗?
参考答案
image.getPointData().setActiveScalars('Density');
image.modified();
const surface = iso.getOutputData();
已经下载的 Density 留在内存中,切换 active scalars 不需要再次下载。禁用 Contrast 只影响后续加载选择,不会从 Point Data 删除已注册数组。setActiveScalars 还要确认数组名确实存在。
练习 5:等值面和体渲染
对同一份三维标量场,把 iso.setContourValue(75) 改为 120,与在合成体渲染中提高 75 附近的不透明度有什么不同?如何用数据对象证明前者产生了新几何?
参考答案
改变等值面阈值会重新计算阈值交叉位置,输出新的 vtkPolyData 点与三角形。改变体渲染不透明度改变的是射线采样值对应的显示权重,原 vtkImageData 不因此变成表面。可查询 iso.getOutputData().getNumberOfPoints()、getNumberOfCells() 以及点坐标、单元连接;不同阈值通常产生不同结果。单元数量偶尔相同也不表示几何完全相同。
练习 6:裁剪平面的方向
平面 origin=[2,0,0]、normal=[1,0,0],clip.value=0。在本教程版本默认 insideOut=false 时主要保留哪一侧?把 insideOut 设为 true 呢?点 [3,1,1] 的隐式函数值是多少?
参考答案
平面函数为 f(x,y,z)=x−2。默认主要保留 f>0,即 x>2;反转后主要保留 f≤0,即 x≤2。点 [3,1,1] 的函数值是 1。跨越平面的三角形还会生成位于 f=0 的边缘点,所以输出网格中可能有精确落在平面上的点。
练习 7:排查空结果
页面成功读取 index.json,但等值面没有三角形。按数据读取、数组、过滤器、显示管线的顺序列出至少四个具体检查项。
参考答案
先查二进制数组请求是否成功、字节数与清单 size/dataType 是否匹配;再查 image.getPointData().getScalars() 是否存在、是否是想要的单分量数组、getRange() 是否覆盖阈值;再查 iso.getOutputData().getNumberOfCells() 是否大于零;最后查 Mapper 是否接 iso.getOutputPort() 或裁剪输出端口、Actor 是否加入 Renderer、裁剪是否把全部网格去掉、相机和 clipping range 是否合适。每一步都看实际数据,不靠画面猜测。
练习 8:把教学数据换成真实文件
假设拿到一个带单位和空间方向信息的真实 .vti 文件。描述替换本阶段示例时需要改变的读取步骤,以及保留哪些核对步骤。为什么直接把其 URL 填进 reader.setUrl('/sample/index.json') 的位置不行?
参考答案
改用 vtkXMLImageDataReader 读取 .vti,等待 setUrl 完成并取 getOutputData()。记录来源、单位、origin、spacing、direction、extent、数组名、分量数与值范围,再选 active scalars,连接等值面或体渲染管线;过滤阈值要依据真实数值单位重新选择。vtkHttpDataSetReader 预期 JSON 清单和外置数组,无法把 .vti XML 当作这种清单解析。