OrientationMarkerWidget

Introduction

vtkOrientationMarkerWidget is a 2D widget for manipulating a marker prop

Methods

computeViewport

Get the computed viewport size.
The format is [left, bottom, right, top].

delete

Dereference any internal object and remove any subscription.
It gives custom class to properly detach themselves from the DOM
or any external dependency that could prevent their deletion
when the GC runs.

extend

Method used to decorate a given object (publicAPI+model) with vtkOrientationMarkerWidget 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 IOrientationMarkerWidgetInitialValues No (default: {})

getActor

getEnabled

Get wheter the orientation marker is enabled.

getInteractor

Get the render window interactor associated with the widget.

getMaxPixelSize

Get the maximum side length, in pixels, for the orientation marker widget
viewport.

getMinPixelSize

Get the minimum side length, in pixels, for the orientation marker widget
viewport.

getParentRenderer

Gets the parent renderer, if any.

getRenderer

Get the renderer associated with the widget.

getViewportCorner

Get the viewport corner.

getViewportSize

Get the viewport size.

newInstance

Method used to create a new instance of vtkOrientationMarkerWidget

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

setActor

Get the actor associated with the widget.

Argument Type Required Description
actor vtkActor Yes The actor instance.

setEnabled

Set the widget enabled status, i.e. to show the widget or not.

Argument Type Required Description
enabled Boolean Yes

setInteractor

Set the render window interactor associated with the widget.

Argument Type Required Description
interactor vtkRenderWindowInteractor Yes

setMaxPixelSize

Set the maximum side length, in pixels, for the orientation marker widget
viewport.

Argument Type Required Description
pixelSize Number Yes

setMinPixelSize

Set the minimum side length, in pixels, for the orientation marker widget
viewport.

Argument Type Required Description
pixelSize Number Yes

setParentRenderer

Sets the parent renderer

Argument Type Required Description
ren vtkRenderer Yes The parent renderer

setViewportCorner

Set which corner to put the widget’s viewport.

Argument Type Required Description
viewportCorner Corners Yes

setViewportSize

Set the viewport size.
The sizeFactor should be between 0.0 and 1.0.
It says how much of the main render window to color.

Argument Type Required Description
sizeFactor Number Yes

updateMarkerOrientation

Manually updates the marker’s orientation.

updateViewport

Updates the orientation widget viewport size.

Source

Constants.d.ts
export declare enum Corners {
TOP_LEFT = 'TOP_LEFT',
TOP_RIGHT = 'TOP_RIGHT',
BOTTOM_LEFT = 'BOTTOM_LEFT',
BOTTOM_RIGHT = 'BOTTOM_RIGHT',
}

declare const _default: {
Corners: typeof Corners;
};
export default _default;
Constants.js
export const Corners = {
TOP_LEFT: 'TOP_LEFT',
TOP_RIGHT: 'TOP_RIGHT',
BOTTOM_LEFT: 'BOTTOM_LEFT',
BOTTOM_RIGHT: 'BOTTOM_RIGHT',
};

export default { Corners };
index.d.ts
import { vtkObject } from "../../../interfaces";
import vtkActor from "../../../Rendering/Core/Actor";
import vtkRenderer from "../../../Rendering/Core/Renderer";
import vtkRenderWindowInteractor from "../../../Rendering/Core/RenderWindowInteractor";
import { Nullable } from "../../../types";
import { Corners } from "./Constants";


/**
*
*/
export interface IOrientationMarkerWidgetInitialValues {
actor?: vtkActor,
interactor?: vtkRenderWindowInteractor,
parentRenderer?: vtkRenderer,
viewportCorner?: Corners,
viewportSize?: number,
minPixelSize?: number,
maxPixelSize?: number
}

export interface vtkOrientationMarkerWidget extends vtkObject {

/**
* Get the computed viewport size.
* The format is `[left, bottom, right, top]`.
*/
computeViewport(): [number, number, number, number];

/**
* Dereference any internal object and remove any subscription.
* It gives custom class to properly detach themselves from the DOM
* or any external dependency that could prevent their deletion
* when the GC runs.
*/
delete(): void;

/**
*
*/
getActor(): vtkActor;

/**
* Gets the parent renderer, if any.
*/
getParentRenderer(): Nullable<vtkRenderer>;

/**
* Get wheter the orientation marker is enabled.
*/
getEnabled(): boolean;

/**
* Get the render window interactor associated with the widget.
*/
getInteractor(): vtkRenderWindowInteractor;

/**
* Get the maximum side length, in pixels, for the orientation marker widget
* viewport.
*/
getMaxPixelSize(): number;

/**
* Get the minimum side length, in pixels, for the orientation marker widget
* viewport.
*/
getMinPixelSize(): number;

/**
* Get the renderer associated with the widget.
*/
getRenderer(): vtkRenderer;

/**
* Get the viewport corner.
*/
getViewportCorner(): Corners;

/**
* Get the viewport size.
*/
getViewportSize(): number;

/**
* Get the actor associated with the widget.
* @param {vtkActor} actor The actor instance.
*/
setActor(actor: vtkActor): void;

/**
* Sets the parent renderer
* @param {vtkRenderer} ren The parent renderer
*/
setParentRenderer(ren: vtkRenderer): boolean;

/**
* Set the widget enabled status, i.e. to show the widget or not.
* @param {Boolean} enabled
*/
setEnabled(enabled: boolean): void;

/**
* Set the render window interactor associated with the widget.
* @param {vtkRenderWindowInteractor} interactor
*/
setInteractor(interactor: vtkRenderWindowInteractor): boolean;

/**
* Set the maximum side length, in pixels, for the orientation marker widget
* viewport.
* @param {Number} pixelSize
* @default 200
*/
setMaxPixelSize(pixelSize: number): boolean;

/**
* Set the minimum side length, in pixels, for the orientation marker widget
* viewport.
* @param {Number} pixelSize
* @default 50
*/
setMinPixelSize(pixelSize: number): boolean;

/**
* Set which corner to put the widget's viewport.
* @param {Corners} viewportCorner
* @default BOTTOM_LEFT
*/
setViewportCorner(viewportCorner: Corners): boolean;

/**
* Set the viewport size.
* The sizeFactor should be between 0.0 and 1.0.
* It says how much of the main render window to color.
* @param {Number} sizeFactor
* @default 0.2
*/
setViewportSize(sizeFactor: number): void;

/**
* Manually updates the marker's orientation.
*/
updateMarkerOrientation(): void;

/**
* Updates the orientation widget viewport size.
*/
updateViewport(): void;
}

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

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

/**
* vtkOrientationMarkerWidget is a 2D widget for manipulating a marker prop
*/
export declare const vtkOrientationMarkerWidget: {
newInstance: typeof newInstance;
extend: typeof extend;
Corners: typeof Corners;
}

export default vtkOrientationMarkerWidget;
index.js
import macro from 'vtk.js/Sources/macros';
import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer';
import Constants from 'vtk.js/Sources/Interaction/Widgets/OrientationMarkerWidget/Constants';

const { vtkErrorMacro } = macro;
const { Corners } = Constants;

// ----------------------------------------------------------------------------
// vtkOrientationMarkerWidget
// ----------------------------------------------------------------------------

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

const superClass = { ...publicAPI };

// Private variables

const previousCameraInput = [];
const selfRenderer = vtkRenderer.newInstance();
const resizeObserver = new ResizeObserver((entries) => {
publicAPI.updateViewport();
});
let onCameraChangedSub = null;
let onCameraModifiedSub = null;
let onAnimationSub = null;
let onEndAnimationSub = null;
let selfSubscription = null;

function onCameraModified() {
// If animating, marker will be updated on Animation event
if (!model._interactor.isAnimating()) {
publicAPI.updateMarkerOrientation();
}
}

model._onParentRendererChanged = () => publicAPI.updateViewport();

publicAPI.computeViewport = () => {
const parentRen =
model.parentRenderer || model._interactor.getCurrentRenderer();

const [xMin, yMin, xMax, yMax] = parentRen.getViewport();

const view = model._interactor.getView();
const canvasSize = view.getSize();
const [viewXSize, viewYSize] = view.getViewportSize(parentRen);
const minViewSize = Math.min(viewXSize, viewYSize);

let pixelSize = model.viewportSize * minViewSize;
// clamp pixel size
pixelSize = Math.max(
Math.min(model.minPixelSize, minViewSize),
Math.min(model.maxPixelSize, pixelSize)
);

const xFrac = pixelSize / canvasSize[0];
const yFrac = pixelSize / canvasSize[1];
// [left bottom right top]
switch (model.viewportCorner) {
case Corners.TOP_LEFT:
return [xMin, yMax - yFrac, xMin + xFrac, yMax];
case Corners.TOP_RIGHT:
return [xMax - xFrac, yMax - yFrac, xMax, yMax];
case Corners.BOTTOM_LEFT:
return [xMin, yMin, xMin + xFrac, yMin + yFrac];
case Corners.BOTTOM_RIGHT:
return [xMax - xFrac, yMin, xMax, yMin + yFrac];
default:
vtkErrorMacro('Invalid widget corner');
return null;
}
};

publicAPI.updateViewport = () => {
if (model.enabled) {
selfRenderer.setViewport(...publicAPI.computeViewport());
model._interactor.render();
}
};

publicAPI.updateMarkerOrientation = () => {
const ren = model.parentRenderer || model._interactor.getCurrentRenderer();
const currentCamera = ren.getActiveCamera();
if (!currentCamera) {
return;
}

const position = currentCamera.getReferenceByName('position');
const focalPoint = currentCamera.getReferenceByName('focalPoint');
const viewUp = currentCamera.getReferenceByName('viewUp');
if (
previousCameraInput[0] !== position[0] ||
previousCameraInput[1] !== position[1] ||
previousCameraInput[2] !== position[2] ||
previousCameraInput[3] !== focalPoint[0] ||
previousCameraInput[4] !== focalPoint[1] ||
previousCameraInput[5] !== focalPoint[2] ||
previousCameraInput[6] !== viewUp[0] ||
previousCameraInput[7] !== viewUp[1] ||
previousCameraInput[8] !== viewUp[2]
) {
previousCameraInput[0] = position[0];
previousCameraInput[1] = position[1];
previousCameraInput[2] = position[2];
previousCameraInput[3] = focalPoint[0];
previousCameraInput[4] = focalPoint[1];
previousCameraInput[5] = focalPoint[2];
previousCameraInput[6] = viewUp[0];
previousCameraInput[7] = viewUp[1];
previousCameraInput[8] = viewUp[2];
const activeCamera = selfRenderer.getActiveCamera();
activeCamera.setPosition(position[0], position[1], position[2]);
activeCamera.setFocalPoint(focalPoint[0], focalPoint[1], focalPoint[2]);
activeCamera.setViewUp(viewUp[0], viewUp[1], viewUp[2]);
selfRenderer.resetCamera();
}
};

/**
* Enables/Disables the orientation marker.
*/
publicAPI.setEnabled = (enabling) => {
if (enabling) {
if (model.enabled) {
return;
}

if (!model.actor) {
vtkErrorMacro('Must set actor before enabling orientation marker.');
return;
}

if (!model._interactor) {
vtkErrorMacro(
'Must set interactor before enabling orientation marker.'
);
return;
}

const ren =
model.parentRenderer || model._interactor.getCurrentRenderer();
const renderWindow = ren.getRenderWindow();
renderWindow.addRenderer(selfRenderer);
if (renderWindow.getNumberOfLayers() < 2) {
renderWindow.setNumberOfLayers(2);
}
// Highest number is foreground
selfRenderer.setLayer(renderWindow.getNumberOfLayers() - 1);
selfRenderer.setInteractive(false);

selfRenderer.addViewProp(model.actor);
model.actor.setVisibility(true);

onCameraChangedSub = ren.onEvent((event) => {
if (event.type === 'ActiveCameraEvent') {
if (onCameraModifiedSub) {
onCameraModifiedSub.unsubscribe();
}
onCameraModifiedSub = event.camera.onModified(onCameraModified);
}
});
onCameraModifiedSub = ren.getActiveCamera().onModified(onCameraModified);
onAnimationSub = model._interactor.onAnimation(
publicAPI.updateMarkerOrientation
);
onEndAnimationSub = model._interactor.onEndAnimation(
publicAPI.updateMarkerOrientation
);

resizeObserver.observe(model._interactor.getView().getCanvas());

publicAPI.updateViewport();
publicAPI.updateMarkerOrientation();

model.enabled = true;
} else {
if (!model.enabled) {
return;
}
model.enabled = false;

resizeObserver.disconnect();
onCameraChangedSub.unsubscribe();
onCameraChangedSub = null;
onCameraModifiedSub.unsubscribe();
onCameraModifiedSub = null;
onAnimationSub.unsubscribe();
onAnimationSub = null;
onEndAnimationSub.unsubscribe();
onEndAnimationSub = null;

model.actor.setVisibility(false);
selfRenderer.removeViewProp(model.actor);

const renderWindow = model._interactor
?.findPokedRenderer()
?.getRenderWindow();
if (renderWindow) {
renderWindow.removeRenderer(selfRenderer);
}
}
publicAPI.modified();
};

/**
* Sets the viewport corner.
*/
publicAPI.setViewportCorner = (corner) => {
if (corner === model.viewportCorner) {
return;
}

model.viewportCorner = corner;
publicAPI.updateViewport();
};

/**
* Sets the viewport size.
*/
publicAPI.setViewportSize = (sizeFactor) => {
const viewportSize = Math.min(1, Math.max(0, sizeFactor));
if (viewportSize === model.viewportSize) {
return;
}

model.viewportSize = viewportSize;
publicAPI.updateViewport();
};

publicAPI.setActor = (actor) => {
const previousState = model.enabled;
publicAPI.setEnabled(false);
model.actor = actor;
publicAPI.setEnabled(previousState);
};

publicAPI.getRenderer = () => selfRenderer;

publicAPI.delete = () => {
superClass.delete();
if (selfSubscription) {
selfSubscription.unsubscribe();
selfSubscription = null;
}
if (onCameraChangedSub) {
onCameraChangedSub.unsubscribe();
onCameraChangedSub = null;
}
if (onCameraModifiedSub) {
onCameraModifiedSub.unsubscribe();
onCameraModifiedSub = null;
}
if (onAnimationSub) {
onAnimationSub.unsubscribe();
onAnimationSub = null;
}
if (onEndAnimationSub) {
onEndAnimationSub.unsubscribe();
onEndAnimationSub = null;
}
resizeObserver.disconnect();
};

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

// update viewport whenever we are updated
selfSubscription = publicAPI.onModified(publicAPI.updateViewport);
}

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

export const DEFAULT_VALUES = {
// actor: null,
// _interactor: null,
viewportCorner: Constants.Corners.BOTTOM_LEFT,
viewportSize: 0.2,
minPixelSize: 50,
maxPixelSize: 200,
parentRenderer: null,
};

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

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

// Build VTK API
macro.obj(publicAPI, model);

macro.get(publicAPI, model, ['enabled', 'viewportCorner', 'viewportSize']);

// NOTE: setting these while the widget is enabled will
// not update the widget.
macro.setGet(publicAPI, model, [
'_interactor',
'minPixelSize',
'maxPixelSize',
'parentRenderer',
]);
macro.get(publicAPI, model, ['actor']);
macro.moveToProtected(publicAPI, model, ['interactor']);

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

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

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

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

export default { newInstance, extend, ...Constants };