第 8 阶段练习与参考答案
先按教程目录阅读第 25–27 章并运行阶段示例。参考答案重在说明每一步应验证什么;性能耗时请以自己浏览器的记录为准。
练习 1:四种“更新”
对一条 ImageData → ImageMarchingCubes → Mapper → Actor 管线,分别说明:①写入 scalars.getData()[i];②调用 scalars.dataChange();③调用 iso.getOutputData();④调用 renderWindow.render()。哪一步会清除 getRange() 的旧缓存?
参考答案
①只改底层 TypedArray;②清除 vtkDataArray 的范围缓存并提高修改序号;③检查管线是否失效,需要时重算过滤结果;④请求更新屏幕画面。第 ② 步清除 getRange() 缓存。只调用第 ④ 步不能保证第 ① 步的改值被 Filter 看见。
练习 2:观察原位编辑
在示例中依次记录初始、点击“原位改 TypedArray”后、点击“调用 dataChange()”后的五个值:独立扫描的最大值、getRange() 最大值、标量 getMTime()、等值面三角形数和输出 getMTime()。哪些值会在第一步修改后立即变?为什么?
参考答案
原位编辑后,独立扫描的最大值会变为 250,因为 TypedArray 内容确实写入;缓存的 getRange()、对象修改序号和等值面输出仍可能保持旧值。dataChange() 后,范围缓存失效,标量修改序号升高,ImageData 聚合序号也随之升高;下次取等值面输出时重算,新增小球使几何通常改变。具体三角形数依当前分辨率和阈值,不应死记。
练习 3:modified() 与 dataChange()
为什么批量原位编辑 vtkDataArray 后,单独调用 scalars.modified() 仍可能导致数值范围错误?什么时候用 setValue() 更方便?
参考答案
在 37.4.2 中,modified() 提高修改序号,但不清除 vtkDataArray 缓存的 ranges;dataChange() 会清范围缓存并调用 modified()。修改极少数值时,setValue() 会在值变化时内部调用 dataChange(),写法更直接;批量编辑时直接写 TypedArray、末尾只调用一次 dataChange() 可避免重复通知。
练习 4:只改颜色要重算三角形吗
示例里点击“只改 Actor 颜色”,比较等值面输出修改序号和画面。为何颜色可变而输出几何序号保持不变?如果改的是 iso.setContourValue(90) 呢?
参考答案
Actor 的 Property 控制显示外观,不改 ImageData 的标量,也不改 Filter 参数,所以无需重算三角形;调用 renderWindow.render() 即可看到新颜色。改变等值面阈值是改变 Filter 参数,下一次请求输出时要用新阈值重新生成网格,再绘制画面。
练习 5:估算内存
一份 512×512×512、单分量 Float32 的 Point Data 数组占多少原始字节和 MiB?若有两个这样的数组,仅数值最低要多少?为什么浏览器内存可能比这个数大?
参考答案
单数组 512³×4=536870912 字节,即 512 MiB;两个数组是 1 GiB 原始数值。下载缓冲、解码中间数据、Filter 输出、vtk.js 对象与 GPU 纹理或缓冲都可能额外占用内存,实际还受具体读取和渲染实现影响。
练习 6:定位一次卡顿
某页面首次打开很慢,第二次打开快;另一页面首次打开正常,但拖动等值面阈值时卡顿。分别先查哪个阶段?列出至少一个可量化的观察值。
参考答案
前者先看 Network 请求时间、状态码和缓存行为,再看解码与数据构造。后者先用 performance.now() 包住 iso.getOutputData(),记录输入维度和输出单元数,并确认拖动是否每个 input 事件都触发重算。若过滤不慢但画面仍卡,再看 GPU 上传、绘制和浏览器 Performance 帧记录。
练习 7:提高交互速度的代价
比较三种做法:①vtkVolumeMapper.setImageSampleDistance(2);②用降采样后的 ImageData 做预览;③只在松开阈值滑块时更新等值面。它们分别改变哪一层?哪种可能改变测量或小结构判断?
参考答案
①减少屏幕平面的射线密度,主要改变绘制质量与成本,源数据仍在内存;②更换较低分辨率的源数据,会改变可提取的细节和测量结果,需要标注预览质量;③减少过滤器重算频率,松开后的结果仍可用原输入和阈值计算。①也可能使小结构在画面上难辨,②最直接改变算法可用的数据。
练习 8:核实一个有争议的 API
本教程版本的 HttpDataSetReader.d.ts 把 loadData() 返回类型写成 string,实际代码却可以 await reader.loadData()。请描述核实路径:先看什么文件,再做什么最小实验?为什么不直接套用网络搜索结果?
参考答案
先确认项目锁定 @kitware/vtk.js 37.4.2;查看该版本 IO/Core/HttpDataSetReader.d.ts 的声明,再查看同目录 .js 的 loadData 实现;用第 7 阶段示例或更小的本地数据集运行 await reader.loadData(),观察 Network 请求和 Point Data 是否得到数组。网络搜索结果可能来自另一版本、另一格式 Reader 或旧 API,必须和当前安装版本及实测对照。
练习 9:追踪继承来的方法
在 ImageMarchingCubes.js 中找不到完整的 getOutputData() 和 setInputConnection() 实现,应该继续读哪里?如何确认 getOutputData() 是否可能重算?
参考答案
该类通过 macros.algo(...) 装配通用算法方法;继续读安装包的 macros.js,找 getOutputData()、shouldUpdate() 和 update()。getOutputData() 会检查 shouldUpdate();输入、上游或算法参数的修改序号较新时,请求 requestData 重算。再用小例子比较修改前后的输出几何和修改序号。