TextActor

Introduction

vtkTextActor can be used to place text annotation into a window.

Methods

extend

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

getProperty

Get the property object that controls this actors properties.

Returns

Type Description
vtkTextProperty The vtkTextProperty instance.

makeProperty

Create a new property suitable for use with this type of TextActor.

Argument Type Required Description
initialValues ITextPropertyInitialValues No (default: {})

newInstance

Method used to create a new instance of vtkTextActor

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

setInput

Set the text to be displayed by the actor.

Argument Type Required Description
input Yes The text to be displayed by the actor.

setProperty

Set the property object that controls this actors properties.

Argument Type Required Description
property vtkTextProperty Yes The vtkTextProperty instance.

Source

index.d.ts
import vtkActor2D, { IActor2DInitialValues } from '../Actor2D';
import vtkTextProperty, { ITextPropertyInitialValues } from '../TextProperty';

export interface ITextActorInitialValues extends IActor2DInitialValues {
property?: vtkTextProperty;
}

export interface vtkTextActor extends vtkActor2D {
/**
* Get the property object that controls this actors properties.
* @returns {vtkTextProperty} The vtkTextProperty instance.
*/
getProperty(): vtkTextProperty;

/**
* Create a new property suitable for use with this type of TextActor.
* @param {ITextPropertyInitialValues} [initialValues] (default: {})
* @return {vtkTextProperty} A new vtkTextProperty instance.
*/
makeProperty(initialValues?: ITextPropertyInitialValues): vtkTextProperty;

/**
* Set the text to be displayed by the actor.
* @param input The text to be displayed by the actor.
*/
setInput(input: string): boolean;

/**
* Set the property object that controls this actors properties.
* @param {vtkTextProperty} property The vtkTextProperty instance.
*/
setProperty(property: vtkTextProperty): boolean;
}

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

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

/**
* vtkTextActor can be used to place text annotation into a window.
*/
export declare const vtkTextActor: {
newInstance: typeof newInstance;
extend: typeof extend;
};
export default vtkTextActor;
index.js
import macro from 'vtk.js/Sources/macros';
import vtkPlaneSource from 'vtk.js/Sources/Filters/Sources/PlaneSource';
import vtkTexture from 'vtk.js/Sources/Rendering/Core/Texture';
import vtkActor2D from 'vtk.js/Sources/Rendering/Core/Actor2D';
import vtkMapper2D from 'vtk.js/Sources/Rendering/Core/Mapper2D';
import vtkTextProperty from 'vtk.js/Sources/Rendering/Core/TextProperty';
import ImageHelper from 'vtk.js/Sources/Common/Core/ImageHelper';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';

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

publicAPI.makeProperty = vtkTextProperty.newInstance;

const texture = vtkTexture.newInstance({
resizable: true,
});
const canvas = new OffscreenCanvas(1, 1);
const mapper = vtkMapper2D.newInstance();
const plane = vtkPlaneSource.newInstance({
xResolution: 1,
yResolution: 1,
});

function createImageData(text) {
const fontSizeScale = publicAPI.getProperty().getFontSizeScale();
const fontStyle = publicAPI.getProperty().getFontStyle();
const fontFamily = publicAPI.getProperty().getFontFamily();
const fontColor = publicAPI.getProperty().getFontColor();
const shadowColor = publicAPI.getProperty().getShadowColor();
const shadowOffset = publicAPI.getProperty().getShadowOffset();
const shadowBlur = publicAPI.getProperty().getShadowBlur();
const resolution = publicAPI.getProperty().getResolution();
const backgroundColor = publicAPI.getProperty().getBackgroundColor();

const dpr = Math.max(window.devicePixelRatio || 1, 1);
const ctx = canvas.getContext('2d');

// Set the text properties to measure
const textSize = fontSizeScale(resolution) * dpr;

ctx.font = `${fontStyle} ${textSize}px "${fontFamily}"`;
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';

// Measure the text
const metrics = ctx.measureText(text);
const textWidth = metrics.width / dpr;

const {
actualBoundingBoxLeft,
actualBoundingBoxRight,
actualBoundingBoxAscent,
actualBoundingBoxDescent,
} = metrics;
const hAdjustment = (actualBoundingBoxLeft - actualBoundingBoxRight) / 2;
const vAdjustment =
(actualBoundingBoxAscent - actualBoundingBoxDescent) / 2;

const textHeight = textSize / dpr - vAdjustment;

// Update canvas size to fit text and ensure it is at least 1x1 pixel
const width = Math.max(Math.round(textWidth * dpr), 1);
const height = Math.max(Math.round(textHeight * dpr), 1);

canvas.width = width;
canvas.height = height;

// Vertical flip
ctx.translate(0, height);
ctx.scale(1, -1);

// Clear the canvas
ctx.clearRect(0, 0, width, height);

if (backgroundColor) {
ctx.fillStyle = vtkMath.floatRGB2HexCode(backgroundColor);
ctx.fillRect(0, 0, width, height);
}

// Reset context after resize and prepare for rendering
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.font = `${fontStyle} ${textSize}px "${fontFamily}"`;
ctx.fillStyle = vtkMath.floatRGB2HexCode(fontColor);
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';

// Set shadow
if (shadowColor) {
ctx.shadowColor = vtkMath.floatRGB2HexCode(shadowColor);
ctx.shadowOffsetX = shadowOffset[0];
ctx.shadowOffsetY = shadowOffset[1];
ctx.shadowBlur = shadowBlur;
}

// Draw the text
ctx.fillText(text, width / 2 + hAdjustment, height / 2 + vAdjustment);

// Update plane dimensions to match text size
plane.set({
point1: [width, 0, 0],
point2: [0, height, 0],
});

return ImageHelper.canvasToImageData(canvas);
}

mapper.setInputConnection(plane.getOutputPort());

publicAPI.setMapper(mapper);
publicAPI.addTexture(texture);

model._onInputChanged = (_publicAPI, _model, value) => {
const image = createImageData(value);
texture.setInputData(image, 0);
};
}

// Default property values
const DEFAULT_VALUES = {
mapper: null,
property: null,
};

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

// Inheritance
vtkActor2D.extend(publicAPI, model, initialValues);

// Build VTK API
macro.setGet(publicAPI, model, ['input']);

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

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

export default { newInstance, extend };