SynchronizableRenderWindow

Methods

clearOneTimeUpdaters

createArrayHandler

createInstanceMap

createProgressHandler

createSceneMtimeHandler

decorate

Argument Type Required Description
renderWindow vtkRenderWindow Yes
name String No

extend

Method used to decorate a given object (publicAPI+model) with vtkCellArray characteristics.

Argument Type Required Description
publicAPI Yes object on which methods will be bounds (public)
model Yes object on which data structure will be bounds (protected)
initialValues ISynchronizableRenderWindowInitialValues No (default: {})

getManagedInstanceIds

getSynchronizedViewId

getSynchronizerContext

Argument Type Required Description
name String No

getSynchronizerContext

newInstance

Method used to create a new instance of vtkSynchronizableRenderWindow

Argument Type Required Description
initialValues ISynchronizableRenderWindowInitialValues No for pre-setting some of its content

setSynchronizedViewId

Argument Type Required Description
viewId String Yes

setSynchronizerContext

Argument Type Required Description
name String Yes
ctx ISynchronizerContext Yes

synchronize

Argument Type Required Description
state IViewState Yes

updateGarbageCollectorThreshold

Argument Type Required Description
v Number Yes

Source

index.d.ts
import { Nullable } from "../../../types";
import vtkRenderWindow, { IRenderWindowInitialValues } from "../../Core/RenderWindow";

// Keeps state for client / server scene synchronization.
export interface ISynchronizerContext {
// Set a function that fetches the data array for the given object.
setFetchArrayFunction(fetcher: (hash: string, dataType: any) => Promise<ArrayBuffer>): void;
// Invokes the fetcher registered by setFetchArrayFunction.
getArray(sha: string, dataType: any, context: ISynchronizerContext): Promise<ArrayBuffer>;
emptyCachedArrays(): void;
freeOldArrays(threshold: number, context: ISynchronizerContext): void;

// instanceMap
getInstance(id: any): any;
getInstanceId(instance: any): any | null;
registerInstance(id: any, instance: any): void;
unregister(id: any): void;
emptyCachedInstances(): void;

// sceneMtimeHandler
getMtime(): number;
incrementMtime(viewId: string): number;
setActiveViewId(viewId: string): void;
getActiveViewId(): string;

// TODO: fill progresshandler
}

export interface ISynchronizableRenderWindowInitialValues extends IRenderWindowInitialValues {
synchronizerContextName?: string; // default: 'default':
synchronizerContext?: Nullable<ISynchronizerContext>;
synchronizedViewId?: Nullable<string>;
}

// Server-side view state.
export interface IViewState {
id: string;
// vtk object type.
type: string;
// vtk object mtime.
mtime: number;
// ID of the parent. Null for the root.
parent?: Nullable<string>;
properties?: {[key: string]: any};
// Child objects.
dependencies?: IViewState[];
extra?: any;
// List of [methodName, args] to be invoked on the object.
calls?: [string, string[]][];
// ViewState may contain other arbitrary key / value pairs.
[key: string]: any;
}

export interface vtkSynchronizableRenderWindow extends vtkRenderWindow {

/**
*
*/
getSynchronizerContext(): ISynchronizerContext;

// methods added by createSyncFunction

/**
*
* @param {IViewState} state
*/
synchronize(state: IViewState): Promise<boolean>;

/**
*
* @param {String} viewId
*/
setSynchronizedViewId(viewId: string): void;

/**
*
*/
getSynchronizedViewId(): string;

/**
*
* @param {Number} v
*/
updateGarbageCollectorThreshold(v: number): void;

/**
*
*/
getManagedInstanceIds(): string[];

/**
*
*/
clearOneTimeUpdaters(): void;
}

/**
* Method used to decorate a given object (publicAPI+model) with vtkCellArray characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {ISynchronizableRenderWindowInitialValues} [initialValues] (default: {})
*/
export function extend(publicAPI: object, model: object, initialValues?: ISynchronizableRenderWindowInitialValues): void;

/**
* Method used to create a new instance of vtkSynchronizableRenderWindow
* @param {ISynchronizableRenderWindowInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(initialValues?: ISynchronizableRenderWindowInitialValues): vtkSynchronizableRenderWindow;

/**
*
* @param {String} [name]
*/
export function getSynchronizerContext(name?: string): ISynchronizerContext;

/**
*
* @param {String} name
* @param {ISynchronizerContext} ctx
*/
export function setSynchronizerContext(name: string, ctx: ISynchronizerContext): ISynchronizerContext;

/**
*
* @param {vtkRenderWindow} renderWindow
* @param {String} [name]
*/
export function decorate(renderWindow: vtkRenderWindow, name?: string): object;

/**
*
*/
export function createInstanceMap(): object;

/**
*
*/
export function createArrayHandler(): object;

/**
*
*/
export function createProgressHandler(): object;

/**
*
*/
export function createSceneMtimeHandler(): object;

/**
*
*/
export declare const vtkSynchronizableRenderWindow: {
newInstance: typeof newInstance;
getSynchronizerContext: typeof getSynchronizerContext;
setSynchronizerContext: typeof setSynchronizerContext;
decorate: typeof decorate,
createInstanceMap: typeof createInstanceMap,
createArrayHandler: typeof createArrayHandler,
createProgressHandler: typeof createProgressHandler,
createSceneMtimeHandler: typeof createSceneMtimeHandler,
vtkObjectManager: object
};
export default vtkSynchronizableRenderWindow;
index.js
import * as macro from 'vtk.js/Sources/macros';
import Base64 from 'vtk.js/Sources/Common/Core/Base64';
import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow';
import vtkObjectManager from './ObjectManager';

const SYNCHRONIZER_CONTEXTS = {};

// ----------------------------------------------------------------------------
// Static methods
// ----------------------------------------------------------------------------

function createArrayHandler() {
const dataArrayCache = {};
let arrayFetcher = null;

function setFetchArrayFunction(fetcher) {
arrayFetcher = fetcher;
}

function getArray(sha, dataType, context) {
const arrayEntry = dataArrayCache[sha];
if (arrayEntry) {
arrayEntry.mtimes[context.getActiveViewId()] = context.getMTime();
return new Promise((resolve, reject) => {
resolve(arrayEntry.array);
});
}

if (!arrayFetcher) {
return Promise.reject(
new Error(
'No array fetcher found, please use "setArrayFetcher" to provide one'
)
);
}

return new Promise((resolve, reject) => {
arrayFetcher(sha).then(
(data) => {
let buffer = data;
if (typeof data === 'string') {
buffer = Base64.toArrayBuffer(data);
}
if (buffer instanceof Blob) {
const fileReader = new FileReader();
fileReader.onload = () => {
const array = macro.newTypedArray(dataType, fileReader.result);
const mtimes = {
[context.getActiveViewId()]: context.getMTime(),
};
dataArrayCache[sha] = { mtimes, array };
resolve(array);
};
fileReader.readAsArrayBuffer(buffer);
} else {
const array = macro.newTypedArray(dataType, buffer);
const mtimes = { [context.getActiveViewId()]: context.getMTime() };
dataArrayCache[sha] = { mtimes, array };
resolve(array);
}
},
(error) => {
console.log('Error getting data array:');
console.log(error);
reject(error);
}
);
});
}

function emptyCachedArrays() {
Object.keys(dataArrayCache).forEach((key) => {
delete dataArrayCache[key];
});
}

function freeOldArrays(threshold, context) {
const mtimeThreshold = context.getMTime() - threshold;
Object.keys(dataArrayCache)
.filter((key) => dataArrayCache[key].mtimes[context.getActiveViewId()])
.filter(
(key) =>
dataArrayCache[key].mtimes[context.getActiveViewId()] < mtimeThreshold
)
.forEach((key) => {
delete dataArrayCache[key];
});
}

return {
setFetchArrayFunction,
getArray,
emptyCachedArrays,
freeOldArrays,
};
}

// ----------------------------------------------------------------------------

function createInstanceMap() {
const instances = {};

function getInstance(id) {
return instances[id];
}

function getInstanceId(instance) {
let instanceId = null;

Object.keys(instances).forEach((id) => {
if (instance === instances[id]) {
instanceId = id;
}
});

return instanceId;
}

function registerInstance(id, instance) {
instances[id] = instance;
}

function unregisterInstance(id) {
delete instances[id];
}

function emptyCachedInstances() {
Object.keys(instances).forEach((key) => {
delete instances[key];
});
}

return {
getInstance,
getInstanceId,
registerInstance,
unregisterInstance,
emptyCachedInstances,
};
}

// ----------------------------------------------------------------------------

function createProgressHandler() {
let readyCount = 0;
const publicAPI = {
start() {
readyCount += 1;
publicAPI.invokeProgressEvent(readyCount);
},
end() {
readyCount -= 1;
publicAPI.invokeProgressEvent(readyCount);
if (readyCount === 0) {
publicAPI.invokeProgressDone();
}
},
resetProgress() {
readyCount = 0;
},
};
const model = {};
macro.event(publicAPI, model, 'progressEvent');
macro.event(publicAPI, model, 'progressDone');

return publicAPI;
}

// ----------------------------------------------------------------------------

function createSceneMtimeHandler() {
const mtimes = {};
let activeViewId = 'default';

function getMTime(viewId) {
const key = viewId || activeViewId;
return mtimes[key] || 1;
}

function incrementMTime(viewId) {
const key = viewId || activeViewId;
if (!mtimes[key]) {
mtimes[key] = 1;
}
mtimes[key] += 1;
}

function setActiveViewId(viewId) {
activeViewId = viewId;
}

function getActiveViewId() {
return activeViewId;
}

return { getMTime, incrementMTime, setActiveViewId, getActiveViewId };
}

// ----------------------------------------------------------------------------

function getSynchronizerContext(name = 'default') {
let ctx = SYNCHRONIZER_CONTEXTS[name];
if (!ctx) {
ctx = {
...createArrayHandler(),
...createInstanceMap(),
...createProgressHandler(),
...createSceneMtimeHandler(),
};
SYNCHRONIZER_CONTEXTS[name] = ctx;
}
return ctx;
}

// ----------------------------------------------------------------------------

function setSynchronizerContext(name, ctx) {
SYNCHRONIZER_CONTEXTS[name] = ctx;
}

// ----------------------------------------------------------------------------

function createSyncFunction(renderWindow, synchronizerContext) {
let lastMtime = -1;
let gcThreshold = 100;

const getManagedInstanceId = (instance) =>
instance.get('managedInstanceId').managedInstanceId;
const getManagedInstanceIds = () =>
macro.traverseInstanceTree(renderWindow, getManagedInstanceId);

function clearOneTimeUpdaters() {
vtkObjectManager.clearOneTimeUpdaters(getManagedInstanceIds());
}

function setSynchronizedViewId(synchronizedViewId) {
renderWindow.set({ synchronizedViewId }, true, true);
}

function getSynchronizedViewId() {
return renderWindow.get('synchronizedViewId').synchronizedViewId;
}

function updateGarbageCollectorThreshold(v) {
gcThreshold = v;
}

function synchronize(state) {
if (!getSynchronizedViewId()) {
setSynchronizedViewId(state.id);
}
const mtime = state.mtime || 0;
if (getSynchronizedViewId() === state.id && lastMtime < mtime) {
return new Promise((resolve, reject) => {
const subscription = synchronizerContext.onProgressDone(() => {
subscription.unsubscribe();
renderWindow.render();
resolve(true);
});
lastMtime = mtime;
synchronizerContext.setActiveViewId(state.id);
synchronizerContext.incrementMTime();
vtkObjectManager.updateRenderWindow(
renderWindow,
state,
synchronizerContext
);
synchronizerContext.freeOldArrays(gcThreshold, synchronizerContext);
});
}
return Promise.resolve(false);
}

return {
synchronize,
setSynchronizedViewId,
getSynchronizedViewId,
updateGarbageCollectorThreshold,
getManagedInstanceIds,
clearOneTimeUpdaters,
};
}

// ----------------------------------------------------------------------------
// vtkSynchronizableRenderWindow methods
// ----------------------------------------------------------------------------

function vtkSynchronizableRenderWindow(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkSynchronizableRenderWindow');

if (!model.synchronizerContext) {
model.synchronizerContext = getSynchronizerContext(
model.synchronizerContextName
);
}

const addOn = createSyncFunction(publicAPI, model.synchronizerContext);

Object.keys(addOn).forEach((methodName) => {
if (publicAPI[methodName]) {
publicAPI[methodName] = macro.chain(
publicAPI[methodName],
addOn[methodName]
);
} else {
publicAPI[methodName] = addOn[methodName];
}
});
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

export const DEFAULT_VALUES = {
synchronizerContextName: 'default',
synchronizerContext: null,
synchronizedViewId: null,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Build VTK API
vtkRenderWindow.extend(publicAPI, model);
macro.get(publicAPI, model, ['synchronizerContext']);

// Object methods
vtkSynchronizableRenderWindow(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(
extend,
'vtkSynchronizableRenderWindow'
);

// ----------------------------------------------------------------------------
// More Static methods
// ----------------------------------------------------------------------------

function decorate(renderWindow, name = 'default') {
const addOn = createSyncFunction(renderWindow, getSynchronizerContext(name));
return {
...addOn,
...renderWindow,
delete: macro.chain(renderWindow.delete, addOn.delete),
};
}

// ----------------------------------------------------------------------------

export default {
newInstance,
extend,
getSynchronizerContext,
setSynchronizerContext,
decorate,
createInstanceMap,
createArrayHandler,
createProgressHandler,
createSceneMtimeHandler,
vtkObjectManager,
};