AbstractWidgetFactory

Methods

getPlaceFactor

Get the place factor.

getViewIds

Get a list of all the view ids.

getViewWidgets

Get a list of all the instances of the widget.

getWidgetForView

Will return the widget associated with the view with Id id locator.viewId.
If there is no widget associated with the view, a new widget will be constructed, provided
that the renderer, viewType, and optionally initialValues are also provided.

Argument Type Required Description
locator IGetWidgetForViewParams Yes

getWidgetState

Get the vtkWidgetState instance

invokeWidgetChangeEvent

Invoke the WidgetChange event

Argument Type Required Description
args Yes The event payload

onWidgetChangeEvent

Register a callback to be called when the WidgetChange event arise.

Argument Type Required Description
cb EventHandler Yes The callback to be invoked.
priority Number No The priority of this subscription

placeWidget

Place a new widget at the given bounds.

Argument Type Required Description
bounds Bounds Yes

setContextVisibility

Set the context visibility for each associated view widget.

Argument Type Required Description
visible Boolean Yes

setDragable

Set the dragable flag for each underlying view widget.

Argument Type Required Description
dragable Boolean Yes

setHandleVisiblity

Set the handles visibility for each underlying view widget.

Argument Type Required Description
visible Boolean Yes

setPickable

Set the pickable flag for each underlying view widget.

Argument Type Required Description
pickable Boolean Yes

setPlaceFactor

Set the place factor.

Argument Type Required Description
factor Number Yes

setVisibility

Set the visiblity on each underlying view widget.

Argument Type Required Description
visible Boolean Yes

Source

index.d.ts
import vtkAbstractWidget from '../AbstractWidget';
import vtkRenderer from '../../../Rendering/Core/Renderer';
import vtkWidgetState from '../WidgetState';
import { ViewTypes } from '../WidgetManager/Constants';
import { Bounds, Nullable } from '../../../types';
import { EventHandler, vtkSubscription, vtkObject } from '../../../interfaces';

export interface IGetWidgetForViewParams {
viewId: number;
renderer?: vtkRenderer;
viewType?: ViewTypes;
initialValues?: object;
}

export interface vtkAbstractWidgetFactory<
WidgetInstance extends vtkAbstractWidget
> extends vtkObject {
/**
* Will return the widget associated with the view with Id id `locator.viewId`.
* If there is no widget associated with the view, a new widget will be constructed, provided
* that the renderer, viewType, and optionally initialValues are also provided.
*
* @param {IGetWidgetForViewParams} locator
*/
getWidgetForView(locator: IGetWidgetForViewParams): Nullable<WidgetInstance>;

/**
* Get a list of all the view ids.
*/
getViewIds(): string[];

/**
* Get a list of all the instances of the widget.
*/
getViewWidgets(): vtkAbstractWidget[];

/**
* Set the visiblity on each underlying view widget.
*
* @param {Boolean} visible
*/
setVisibility(visible: boolean): void;

/**
* Set the pickable flag for each underlying view widget.
*
* @param {Boolean} pickable
*/
setPickable(pickable: boolean): void;

/**
* Set the dragable flag for each underlying view widget.
*
* @param {Boolean} dragable
*/
setDragable(dragable: boolean): void;

/**
* Set the context visibility for each associated view widget.
*
* @param {Boolean} visible
*/
setContextVisibility(visible: boolean): void;

/**
* Set the handles visibility for each underlying view widget.
*
* @param {Boolean} visible
*/
setHandleVisiblity(visible: boolean): void;

/**
* Place a new widget at the given bounds.
*
* @param {Bounds} bounds
*/
placeWidget(bounds: Bounds): void;

/**
* Get the place factor.
*/
getPlaceFactor(): number;

/**
* Set the place factor.
*
* @param {Number} factor
*/
setPlaceFactor(factor: number): void;

/**
* Get the `vtkWidgetState` instance
*/
getWidgetState(): vtkWidgetState;

/**
* Register a callback to be called when the WidgetChange event arise.
*
* @param {EventHandler} cb The callback to be invoked.
* @param {Number} [priority] The priority of this subscription
*/
onWidgetChangeEvent(
cb: EventHandler,
priority?: number
): Readonly<vtkSubscription>;

/**
* Invoke the WidgetChange event
*
* @param args The event payload
*/
invokeWidgetChangeEvent(...args: unknown[]): void;
}

/**
* This hack is to "remember" the generic type T that this function turn publicAPI into
* This is because typescript is completely "structural" and doesn't have any way to declare "nominal" types
* See: https://github.com/microsoft/TypeScript/issues/202
* For example, in this code, widgetInstance is a vtkResliceCursorWidgetCPRInstance:
*
* import vtkResliceCursorWidget from '@kitware/vtk.js/Widgets/Widgets3D/ResliceCursorWidget';
* import widgetBehavior from '@kitware/vtk.js/Widgets/Widgets3D/ResliceCursorWidget/cprBehavior';
*
* const widget = vtkResliceCursorWidget.newInstance({
* planes: ['Y', 'Z'],
* behavior: widgetBehavior,
* });
* const widgetInstance = widgetManager.addWidget(widget);
*/
declare const ExtendSymbol: unique symbol;
export type ExtendWidgetBehavior<WidgetInstance> = ((
publicAPI: object,
model: object
) => void) & { [ExtendSymbol]: WidgetInstance };

export interface IAbstractWidgetFactoryInitialValues<
WidgetInstance extends vtkAbstractWidget = vtkAbstractWidget
> {
behavior?: ExtendWidgetBehavior<WidgetInstance>;
}

/**
* Method used to decorate a given object (publicAPI+model) with vtkAbstractWidgetFactory characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param initialValues (default: {})
*/
export function extend<
WidgetInstance extends vtkAbstractWidget = vtkAbstractWidget
>(
publicAPI: object,
model: object,
initialValues?: IAbstractWidgetFactoryInitialValues<WidgetInstance>
): void;

/**
* Method used to create a new instance of vtkAbstractWidgetFactory
*
* @param initialValues for pre-setting some of its content
*/
export function newInstance<
WidgetInstance extends vtkAbstractWidget = vtkAbstractWidget
>(
initialValues?: IAbstractWidgetFactoryInitialValues<WidgetInstance>
): vtkAbstractWidgetFactory<WidgetInstance>;

export declare const vtkAbstractWidgetFactory: {
newInstance: typeof newInstance;
extend: typeof extend;
};

export default vtkAbstractWidgetFactory;
index.js
import macro from 'vtk.js/Sources/macros';
import vtkAbstractWidget from 'vtk.js/Sources/Widgets/Core/AbstractWidget';
import { extractRenderingComponents } from 'vtk.js/Sources/Widgets/Core/WidgetManager';

function NoOp() {}

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

function vtkAbstractWidgetFactory(publicAPI, model) {
model.classHierarchy.push('vtkAbstractWidgetFactory');

// DO NOT share on the model ------------------------------------------------
const viewToWidget = {};
// DO NOT share on the model ------------------------------------------------

// Can be called with just ViewId after the widget has been registered
publicAPI.getWidgetForView = ({
viewId,
renderer,
viewType,
initialValues,
}) => {
if (!viewToWidget[viewId]) {
if (!renderer) {
return null;
}

const { interactor, apiSpecificRenderWindow, camera } =
extractRenderingComponents(renderer);
const widgetModel = {};
const widgetPublicAPI = {};

macro.obj(widgetPublicAPI, widgetModel);
Object.assign(widgetPublicAPI, {
onWidgetChange: publicAPI.onWidgetChange,
});
Object.assign(widgetModel, {
widgetState: model.widgetState,
manipulator: model.manipulator,
viewType,
renderer,
camera,
apiSpecificRenderWindow,
factory: publicAPI,
});
macro.moveToProtected(widgetPublicAPI, widgetModel, [
'renderer',
'camera',
'apiSpecificRenderWindow',
'factory',
]);
macro.get(widgetPublicAPI, widgetModel, ['viewType']);
macro.safeArrays(widgetModel);
vtkAbstractWidget.extend(widgetPublicAPI, widgetModel, initialValues);

// Create representations for that view
/* eslint-disable no-shadow */
const widgetInitialValues = initialValues; // Avoid shadowing
widgetModel.representations = publicAPI
.getRepresentationsForViewType(viewType)
.map(({ builder, labels, initialValues }) =>
builder.newInstance({
_parentProp: widgetPublicAPI,
labels,
...initialValues,
...widgetInitialValues,
})
);
/* eslint-enable no-shadow */

widgetModel.representations.forEach((r) => {
r.setInputData(widgetModel.widgetState);
r.getActors().forEach((actor) => {
widgetModel.actorToRepresentationMap.set(actor, r);
});
});

model.behavior(widgetPublicAPI, widgetModel);
// Forward representation methods
['coincidentTopologyParameters', ...(model.methodsToLink || [])].forEach(
(methodName) => {
const set = `set${macro.capitalize(methodName)}`;
const get = `get${macro.capitalize(methodName)}`;
const methods = {
[methodName]: [],
[set]: [],
[get]: [],
};
widgetModel.representations.forEach((representation) => {
if (representation[methodName]) {
methods[methodName].push(representation[methodName]);
}
if (representation[set]) {
methods[set].push(representation[set]);
}
if (representation[get]) {
methods[get].push(representation[get]);
}
});

Object.keys(methods).forEach((name) => {
const calls = methods[name];
if (calls.length === 1) {
widgetPublicAPI[name] = calls[0];
} else if (calls.length > 1) {
widgetPublicAPI[name] = macro.chain(...calls);
}
});
}
);

// Custom delete to detach from parent
widgetPublicAPI.delete = macro.chain(() => {
delete viewToWidget[viewId];
}, widgetPublicAPI.delete);

widgetPublicAPI.setInteractor(interactor);
const viewWidget = Object.freeze(widgetPublicAPI);
viewToWidget[viewId] = viewWidget;
return viewWidget;
}
return viewToWidget[viewId];
};

// List of all the views the widget has been registered to.
publicAPI.getViewIds = () => Object.keys(viewToWidget);

publicAPI.getViewWidgets = () => Object.values(viewToWidget);

// --------------------------------------------------------------------------
// Widget visibility / enable
// --------------------------------------------------------------------------
// Call methods on all its view widgets

publicAPI.setVisibility = (value) => {
const viewIds = Object.keys(viewToWidget);
for (let i = 0; i < viewIds.length; i++) {
viewToWidget[viewIds[i]].setVisibility(value);
}
};

publicAPI.setPickable = (value) => {
const viewIds = Object.keys(viewToWidget);
for (let i = 0; i < viewIds.length; i++) {
viewToWidget[viewIds[i]].setPickable(value);
}
};

publicAPI.setDragable = (value) => {
const viewIds = Object.keys(viewToWidget);
for (let i = 0; i < viewIds.length; i++) {
viewToWidget[viewIds[i]].setDragable(value);
}
};

publicAPI.setContextVisibility = (value) => {
const viewIds = Object.keys(viewToWidget);
for (let i = 0; i < viewIds.length; i++) {
viewToWidget[viewIds[i]].setContextVisibility(value);
}
};

publicAPI.setHandleVisibility = (value) => {
const viewIds = Object.keys(viewToWidget);
for (let i = 0; i < viewIds.length; i++) {
viewToWidget[viewIds[i]].setHandleVisibility(value);
}
};

// --------------------------------------------------------------------------
// Place Widget API
// --------------------------------------------------------------------------

publicAPI.placeWidget = (bounds) => model.widgetState.placeWidget(bounds);
publicAPI.getPlaceFactor = () => model.widgetState.getPlaceFactor();
publicAPI.setPlaceFactor = (factor) =>
model.widgetState.setPlaceFactor(factor);

// --------------------------------------------------------------------------
// Event Widget API
// --------------------------------------------------------------------------
let unsubscribe = NoOp;
publicAPI.delete = macro.chain(publicAPI.delete, () => unsubscribe());

if (model.widgetState) {
unsubscribe = model.widgetState.onModified(() =>
publicAPI.invokeWidgetChange(model.widgetState)
).unsubscribe;
}
}

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, initialValues);
macro.obj(publicAPI, model);
macro.get(publicAPI, model, ['widgetState']);
macro.event(publicAPI, model, 'WidgetChange');
vtkAbstractWidgetFactory(publicAPI, model);
}

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

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

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

export default { newInstance, extend };