第 27 章:阅读官方示例、API 与源码
学习目标
教程只能覆盖一部分对象。遇到新的 Reader、Filter 或渲染参数时,你需要能自己找到答案,并分辨“文档说明的意图”和“当前安装版本的实际行为”。本章给出一条可重复的查阅路线,带你用第 25 章的数据更新问题和第 23 章的按需加载问题作两次完整追踪。
27.1 从一个可验证的问题开始
不要以“把 vtk.js 源码看一遍”为目标。先把疑问缩小成一次调用:
vtkImageMarchingCubes读哪个输入数组,输出什么数据类型?vtkHttpDataSetReader.setUrl()到底是否下载属性数组?- 改了
vtkDataArray.getData()的元素,为何getRange()还是旧值? vtkVolumeMapper.setSampleDistance()改变沿视线还是屏幕采样?
每个问题都写明安装版本、输入对象、调用顺序和观察值。例如“37.4.2 中,单分量 vtkImageData 的 Point Data 标量直接原位改值后,iso.getOutputData() 的单元数不变”,比“更新有问题”更容易查。源码阅读的终点是能用小例子验证一个具体结论,而不是记住整个类文件。
27.2 四类资料各做什么
| 资料 | 适合回答 | 使用时注意 |
|---|---|---|
| vtk.js 官方示例 | 对象组合、渲染 Profile、最小显示流程 | 示例可能使用不同版本或旧构建方式;先看数据来源和输入类型 |
| vtk.js 官方 API | 类、方法名、参数和概念说明 | 在线页面通常跟随站点发布节奏,不一定等于你的安装版本 |
| Kitware/vtk-js 源码仓库 | 实现、默认值、特殊分支、更新条件 | 浏览时确认 tag/commit;默认分支可能比本教程更新 |
本地安装包 .d.ts 与 .js |
当前 37.4.2 的声明和实际执行代码 |
声明可能有遗漏或旧注释;运行行为以实测和实现为准 |
本教程的所有示例固定 @kitware/vtk.js 37.4.2。先看项目 package.json 和锁文件,确认真正安装的版本,然后在 examples/stage-08/node_modules/@kitware/vtk.js/ 中查同版本代码。若你正在维护不同版本的项目,改用该项目自己的安装包。官方网页、搜索结果和问答文章很有帮助,但不要拿最新页面直接断言旧项目的行为。
27.3 读示例时追五条线
打开一个官方示例,不必逐行先读 DOM 和 CSS。优先标记这五条线:
- 导入与 Profile:是否先导入
Rendering/Profiles/Geometry、Volume或All?示例的 import 路径与你安装的包名是否一致? - 数据来源:是
vtkSphereSource生成、Reader 下载,还是 JavaScript 直接构造?输入究竟是vtkPolyData还是vtkImageData? - 算法连接:哪里调用
setInputData,哪里调用setInputConnection(...getOutputPort())?是否有 Filter 改变数据类型? - 显示连接:Mapper、Actor/Volume、Renderer、相机和
renderWindow.render()怎样串起来? - 交互更新:控件事件修改的是数据、Filter 参数、Mapper、Property 还是 Camera?之后是否等待异步操作、请求输出和重新绘制?
把代码缩成一张流图,例如:
HttpDataSetReader → ImageData → ImageMarchingCubes → PolyData → Mapper → Actor
这样先知道每个对象负责什么,再研究细节。若示例用了你不认识的对象,先查该类的输入和输出类型,再复制调用。看似相似的 vtkMapper、vtkImageMapper 与 vtkVolumeMapper 不能只凭名字替换。
27.4 读 API 时抓住四个问题
查某个对象时依次找:构造方法、输入/输出类型、默认值、修改后怎样生效。例如 vtkClipPolyData:构造 newInstance({clipFunction: plane});输入和输出都是 vtkPolyData;insideOut 的默认值影响保留侧;平面移动后要从输出端口取新结果并重新绘制。
类型声明很适合快速发现方法名,但并非万无一失。一个具体例子是本教程版本的 IO/Core/HttpDataSetReader.d.ts 把 loadData() 返回类型写成 string,而对应 .js 实现返回 Promise,第 7 阶段示例也以 await reader.loadData() 成功运行。遇到这样的冲突,记录版本和复现,优先核对实现与运行结果;不要根据一条有误声明删掉必要的 await。
27.5 源码定位地图:高频对象从哪里找
安装依赖后,以项目的 node_modules/@kitware/vtk.js/ 为根:
| 你要追的对象 | 安装包中的典型位置 | 首先找什么 |
|---|---|---|
vtkDataArray |
Common/Core/DataArray.js 与 .d.ts |
getData、getRange、dataChange、setData |
vtkImageData / vtkDataSet |
Common/DataModel/ImageData.js、DataSet.js |
extent、spacing、getMTime |
vtkHttpDataSetReader |
IO/Core/HttpDataSetReader.js |
setUrl、getArrays、enableArray、loadData |
vtkImageMarchingCubes |
Filters/General/ImageMarchingCubes.js |
requestData、输入 scalars、输出 PolyData |
vtkClipPolyData |
Filters/Core/ClipPolyData.js |
隐式函数、insideOut、输出端口 |
vtkVolumeMapper |
Rendering/Core/VolumeMapper.js、对应 WebGL 实现 |
采样设置与渲染实现的分工 |
| 通用数据管线 | macros.js |
modified、getMTime、algo、shouldUpdate、getOutputData |
许多类的方法由 macros.obj、macros.algo、macros.setGet 等宏装配出来,因此在类文件中搜不到 getOutputPort、setInputData 并不表示方法不存在。若类文件里看到 macros.algo(publicAPI, model, 1, 1),继续去 macros.js 查通用输入、输出和更新逻辑。安装包是发布后的 .js;GitHub 的 Sources/.../index.js 路径与发布包布局可能不同,找文件时不要假设二者目录逐层一致。
在终端里可以先按文件名定位,再搜索方法,不必一次打印整棵目录:
rg --files node_modules/@kitware/vtk.js | rg 'DataArray|ImageMarchingCubes|macros'
rg -n 'dataChange|getRange|shouldUpdate' node_modules/@kitware/vtk.js/Common/Core/DataArray.js node_modules/@kitware/vtk.js/macros.js
这些命令需要在装有依赖的示例项目目录下运行。若只想看某个类是否存在,先看 import 能否解析和 .d.ts 文件,再深挖实现。
27.6 实战追踪一:为什么原位改值后画面不变
从第 25 章的问题出发,按这样的顺序查:
- 在
DataArray.js找getData():它给出 TypedArray 或其中的有效视图。对这个数组直接赋值,不会自动调用对象方法。 - 找
getRange():范围可缓存。再找dataChange():它清空ranges并调用modified()。 - 在
DataSetAttributes/FieldData.js与DataSet.js找getMTime():父对象的查询结果会考虑子数组的较新修改序号。 - 在
macros.js的algo中找shouldUpdate()和getOutputData():输出与输入的修改序号决定是否再执行算法。 - 回到最小示例,分别在赋值前、只赋值后、调用
dataChange()后打印原始值、getRange()、ImageDatagetMTime()和等值面单元数。
这样能定位“值已变、范围没变、Filter 未重算”的具体断点。第 8 阶段示例把这五个观测值直接放在页面上;只改 Actor 颜色时,等值面输出序号不会因此改变。
27.7 实战追踪二:Reader 为何只读到清单
从 reader.setUrl('/sample/index.json',{loadData:false}) 出发:
- 看
HttpDataSetReader.d.ts的setUrl、getArrays、enableArray、loadData,理解公开入口。 - 看
HttpDataSetReader.js的setUrl、updateMetadata与processDataSet:清单被解析为数据对象,属性数组先被登记。 - 看
enableArray:它只切换数组选择状态;再看loadData:它找已启用、仍有ref的数组并请求二进制文件。 - 看
IO/Core/DataAccessHelper/LiteHttpDataAccessHelper.js的fetchArray:baseURL/ref.basepath/ref.id形成请求地址,返回数据按dataType和字节序解释。 - 用浏览器 Network 验证清单请求和两个数组请求的实际时机。不能只因
getArrays()列出名字,就断言数值已经下载。
这套查法也适用于新 Reader:先找公开方法,再找网络/解析分支,最后用真实请求和输出对象证实。若某个方法的注释与实现冲突,应把两者和版本都记下来,并写一个最小验证例子。
27.8 建立自己的小型对象笔记
学完一个高频对象,只记录五件事:输入类型、输出类型、关键方法、默认值、一个已经验证的坑。例如:
vtkImageMarchingCubes
输入:vtkImageData,active Point Data 单分量标量
输出:vtkPolyData
方法:setContourValue、setInputData、getOutputPort
注意:阈值超出范围可能得到空网格;换数组后要检查更新
笔记旁附上项目版本、官方 API 链接、源码位置和你的最小示例。以后升级 vtk.js,先运行示例并重新核对这些记录。能追溯的短笔记比大量没有版本标记的复制代码更可靠。
本章小结
自学 vtk.js 的顺序可以固定为:提出可验证问题 → 看官方示例建立对象关系 → 查 API 和类型声明 → 读同版本实现与通用宏 → 用最小程序观察数据和画面。第 8 阶段到此结束;下一阶段会把这些方法用于表面查看器、体数据查看器和按需加载的综合项目。