vtkObject
vtk类支持通过macro.newInstance实现,一个vtk类实例一般包括两部分 model对象和publicAPI对象,其中类的属性定义在model中,暴露的方法定义在publicAPI中
macro.newInstance
export function newInstance(extend, className) {
const constructor = (initialValues = {}) => {
const model = {};
const publicAPI = {};
extend(publicAPI, model, initialValues);
return Object.freeze(publicAPI);
};
// Register constructor to factory
if (className) {
vtk.register(className, constructor);
}
return constructor;
}
vtkObject是vtk中一切类的基类,通过macro.obj()方法定义到对象中主要包括下面功能
//对象信息修改分发
modified()
onModified(callback)
//对象删除
delete()
isDeleted():boolean
//对象修改时间
getMTime();
//对象RTTI支持
isA(className);
getClassName();
//返回一个格式化对象用于序列化
getState()
vtkObject同时提供了对类属性的访问接口
get(...list); list为要获取的属性名列表,list为空则返回整个model对象,list不为空则返回一个新对象包括了要查询属性的键值。
getReferenceByName(propertyName); 返回model中对应属性值。
因为vtkjs中类属性定义在model中,且是隐式定义,可读性不好,大多数类暴露的属性也同时通过getter 和 setter方法暴露,所以一般不用上面方法直接去访问类属性,而用getter和setter,
一个类暴露了哪些属性也直接通过观察getter和setter可以看出,如:
getter和setter的实现参考上一篇:
vtkjs 学习笔记 二模块化与类(newInstance)

数据的更新通知
这个功能需要依赖vtkObject.modify这个事件来分发,
setter的实现中会默认调用对象modify方法来通知对象修改。
所有的setter都是通过findSetter去生成一个对应setter,一个setter在调用的时候会调用对象modify方法通知对象更新:
function findSetter(field) {
if (typeof field === 'object') {
const fn = objectSetterMap[field.type];
if (fn) {
return (publicAPI, model) => fn(publicAPI, model, field);
}
vtkErrorMacro(`No setter for field ${field}`);
throw new TypeError('No setter for field');
}
return function getSetter(publicAPI, model) {
return function setter(value) {
if (model.deleted) {
vtkErrorMacro('instance deleted - cannot call any method');
return false;
}
if (model[field] !== value) {
model[field] = value;
publicAPI.modified();
return true;
}
return false;
};
};
}
vtkAlgorithm
算法是vtk数据工作流的基础,vtk把数据处理部分抽象为一个个算法,每一个算法有Input Ouput,一个算法的OutPut可以作为下一个算法的Input,这样一系列算法串起来就是数据工作流。
vtkAlgorthm是通过macro.algo方法来实现的,主要实现了下面几个接口去表示输入输出数据:
abstract requestData()
setInputData(),
setInputConnection(),
getOutputData(),
getOutputPort()
它的输入输出一般都是vtkDataSet(通常是vtkPolydata)
所有的filter类,数据源,mapper都需要继承它,由它作为一个数据处理节点去插入vtk工作流。
在定义的时候需要定义这个对象有多少个输入多少个输出
export function algo(publicAPI, model, numberOfInputs, numberOfOutputs)
如这个是Source类的定义,表示这个是数据源,有0个输入1个输出

这个是Mapper的定义,表示Mapper是数据流的最终接收者

这个是所有filter的定义,表示是数据流中的处理算法,既有输入又有输出
macro
.
algo
(
publicAPI
,
model
,
1
,
1
);

setInputData & getOutputData
设置获取数据,数据一般都是vtkDataSet 如vtkPolyData
this.mapper.setInputData(this.coneSource.getOutputData());
这种方式获取的数据是一次性的,所以当数据修改后不会刷新显示
setInputConnection & getOutputPort
setInputConnection设置的是一个输出函数,输出函数会调用getOutputData()来获取输出数据。
getOutputPort是获取一个输出函数
使用它们的好处是每次render都会通过调用输出函数调用getOutputData重新获取数据,这样如果数据源有修改则会同步渲染。
一般的用法是
this.mapper.setInputConnection(this.coneSource.getOutputPort());
输出函数定义如下,其直接执行可以获取输出数据,同时有filter属性,可以访问这个输出数据的算法对象
function getOutputPort(port = 0) {
const outputPortAccess = () => getOutputData(port); //获取数据
// Add reference to filter
outputPortAccess.filter = publicAPI;
return outputPortAccess;
}
类似于下面这个接口的实现,下面这个是ts定义,
interface vtkOutputPort{
():void;
}
interface vtkOutputPortAccess extends vtkOutputPort{
filter:vtkAlgorithm;
}
requestData接口
一个算法派生类需要实现requestData接口来生成 output数据
下面是vtkLineFilter的requestData的实现
publicAPI.requestData = (inData, outData) => {
const dataset = vtkPolyData.newInstance();
dataset.getPoints().setData(inData[0].getPoints().getData());
dataset.getLines().setData(inData[0].getLines().getData());
outData[0] = dataset;
};
requestData会在算法类对象update时调用请求重新计算输出数据,getOutputData()会调用update()
publicAPI.update = () => {
const ins = [];
if (model.numberOfInputs) {
let count = 0;
while (count < model.numberOfInputs) {
ins[count] = publicAPI.getInputData(count);
count++;
}
}
if (publicAPI.shouldUpdate() && publicAPI.requestData) {
publicAPI.requestData(ins, model.output);
}
};
function getOutputData(port = 0) {
if (model.deleted) {
vtkErrorMacro('instance deleted - cannot call any method');
return null;
}
if (publicAPI.shouldUpdate()) {
publicAPI.update();
}
return model.output[port];
}