DataArray

Introduction

vtkDataArray is an abstract superclass for data array objects containing
numeric data.

Methods

computeRange

Compute range of a given array. The array could be composed of tuples and
individual component range could be computed as well as magnitude.

const array = [x0, y0, z0, x1, y1, z1, ..., xn, yn, zn];
const { min: yMin, max: yMax } = computeRange(array, 1, 3);
const { min: minMagnitude, max: maxMagnitude } = computeRange(array, -1, 3);
Argument Type Required Description
values Array. Yes Array to go through to extract the range from
component Number No (default: 0) indice to use inside tuple size
numberOfComponents Number No (default: 1) size of the tuple

createRangeHelper

Create helper object that can be used to gather min, max, count, sum of
a set of values.

extend

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

getComponent

Get the component for a given tupleIdx.

Argument Type Required Description
tupleIdx Number Yes
componentIndex Number No (default: 0)

getData

getDataType

Return the name of a typed array

const isFloat32 = ('Float32Array' === getDataType(array));
const clone = new macro.TYPED_ARRAYS[getDataType(array)](array.length);
Argument Type Required Description
typedArray Yes to extract its type from

getDataType

getElementComponentSize

Get the size, in bytes, of the lowest-level element of an array.

getMaxNorm

Return the max norm of a given vtkDataArray

Argument Type Required Description
dataArray Yes to process

getName

getNumberOfComponents

Get the dimension (n) of the components.

getNumberOfTuples

Get the number of complete tuples (a component group) in the array.

getNumberOfValues

Get the total number of values in the array.

getRange

Get the range of the given component.

Argument Type Required Description
componentIndex Number Yes (default: -1)

getState

getTuple

Argument Type Required Description
idx Number Yes
tupleToFill Array. No (default [])

getTupleLocation

Argument Type Required Description
idx Number No (default: 1)

newClone

newInstance

Method use to create a new instance of vtkDataArray

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

setComponent

Set the component value for a given tupleIdx and componentIndex.

Argument Type Required Description
tupleIdx Number Yes
componentIndex Number Yes
value Number Yes

setData

Argument Type Required Description
typedArray TypedArray Yes
numberOfComponents Number No

setName

setNumberOfComponents

Set the dimension (n) of the components.

Argument Type Required Description
numberOfComponents Number Yes

setRange

Argument Type Required Description
rangeValue vtkRange Yes
componentIndex Number Yes

setTuple

Argument Type Required Description
idx Number Yes
tuple Array. Yes

Source

Constants.js
export const DataTypeByteSize = {
Int8Array: 1,
Uint8Array: 1,
Uint8ClampedArray: 1,
Int16Array: 2,
Uint16Array: 2,
Int32Array: 4,
Uint32Array: 4,
Float32Array: 4,
Float64Array: 8,
};

export const VtkDataTypes = {
VOID: '', // not sure to know what that should be
CHAR: 'Int8Array',
SIGNED_CHAR: 'Int8Array',
UNSIGNED_CHAR: 'Uint8Array',
SHORT: 'Int16Array',
UNSIGNED_SHORT: 'Uint16Array',
INT: 'Int32Array',
UNSIGNED_INT: 'Uint32Array',
FLOAT: 'Float32Array',
DOUBLE: 'Float64Array',
};

export const DefaultDataType = VtkDataTypes.FLOAT;

export default {
DefaultDataType,
DataTypeByteSize,
VtkDataTypes,
};
index.d.ts
import { vtkObject, vtkRange } from "../../../interfaces";
import { TypedArray } from "../../../types";


/**
* Output of the rangeHelper instance
*/
interface VtkStatisticInformation {
min: number;
max: number;
count: number;
sum: number;
mean: number;
}

/**
* Helper class used to compute data range of a set of numbers
*/
interface vtkRangeHelper {
add(value: number): void;
get(): VtkStatisticInformation;
getRange(): vtkRange;
}

export interface vtkDataArray extends vtkObject {

/**
* Get the size, in bytes, of the lowest-level element of an array.
*/
getElementComponentSize(): number;

/**
* Get the component for a given tupleIdx.
* @param {Number} tupleIdx
* @param {Number} [componentIndex] (default: 0)
*/
getComponent(tupleIdx: number, componentIndex?: number): number;

/**
* Set the component value for a given tupleIdx and componentIndex.
* @param {Number} tupleIdx
* @param {Number} componentIndex
* @param {Number} value
*/
setComponent(tupleIdx: number, componentIndex: number, value: number): void;

/**
*
*/
getData(): TypedArray;

/**
* Get the range of the given component.
*
* @param {Number} componentIndex (default: -1)
*/
getRange(componentIndex?: number): vtkRange;

/**
*
* @param {vtkRange} rangeValue
* @param {Number} componentIndex
*/
setRange(rangeValue: vtkRange, componentIndex: number): [number, number];

/**
*
* @param {Number} idx
* @param {Number[]} tuple
*/
setTuple(idx: number, tuple: number[]): void;

/**
*
* @param {Number} idx
* @param {Number[]} [tupleToFill] (default [])
*/
getTuple(idx: number, tupleToFill?: number[]): number[];

/**
*
* @param {Number} [idx] (default: 1)
*/
getTupleLocation(idx?: number): number;

/**
* Get the dimension (n) of the components.
*/
getNumberOfComponents(): number;

/**
* Get the total number of values in the array.
*/
getNumberOfValues(): number;

/**
* Get the number of complete tuples (a component group) in the array.
*/
getNumberOfTuples(): number;

/**
*
*/
getDataType(): string;

/**
*
*/
newClone(): vtkDataArray;

/**
*
*/
getName(): string;

/**
*
* @param {TypedArray} typedArray
* @param {Number} [numberOfComponents]
*/
setData(typedArray: TypedArray, numberOfComponents?: number): void;

/**
*
*/
getState(): object;

// --- via macro --

/**
*
*/
setName(name: string): boolean;

/**
* Set the dimension (n) of the components.
* @param {Number} numberOfComponents
*/
setNumberOfComponents(numberOfComponents: number): boolean;
}

// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------

/**
* Compute range of a given array. The array could be composed of tuples and
* individual component range could be computed as well as magnitude.
*
* ```js
* const array = [x0, y0, z0, x1, y1, z1, ..., xn, yn, zn];
* const { min: yMin, max: yMax } = computeRange(array, 1, 3);
* const { min: minMagnitude, max: maxMagnitude } = computeRange(array, -1, 3);
* ```
*
* @param {Number[]} values Array to go through to extract the range from
* @param {Number} [component] (default: 0) indice to use inside tuple size
* @param {Number} [numberOfComponents] (default: 1) size of the tuple
*/
export function computeRange(values: number[], component?: number, numberOfComponents?: number): vtkRange;

/**
* Create helper object that can be used to gather min, max, count, sum of
* a set of values.
*/
export function createRangeHelper(): vtkRangeHelper

/**
* Return the name of a typed array
*
* ```js
* const isFloat32 = ('Float32Array' === getDataType(array));
* const clone = new macro.TYPED_ARRAYS[getDataType(array)](array.length);
* ```
*
* @param typedArray to extract its type from
*/
export function getDataType(typedArray: TypedArray): string

/**
* Return the max norm of a given vtkDataArray
*
* @param dataArray to process
*/
export function getMaxNorm(dataArray: vtkDataArray): number

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

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

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

/**
* Constants capturing the number of bytes per element based on its data type.
*/
export enum DataTypeByteSize {
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array,
}

/**
* Constants capturing the various VTK data types.
*/
export enum VtkDataTypes {
VOID,
CHAR,
SIGNED_CHAR,
UNSIGNED_CHAR,
SHORT,
UNSIGNED_SHORT,
INT,
UNSIGNED_INT,
FLOAT,
DOUBLE,
}

/**
* vtkDataArray is an abstract superclass for data array objects containing
* numeric data.
*/
export declare const vtkDataArray: {
newInstance: typeof newInstance,
extend: typeof extend,
// static
computeRange: typeof computeRange,
createRangeHelper: typeof createRangeHelper,
getDataType: typeof getDataType,
getMaxNorm: typeof getMaxNorm,
// constants
DataTypeByteSize: typeof DataTypeByteSize,
VtkDataTypes: typeof VtkDataTypes,
DefaultDataType: VtkDataTypes,
};

export default vtkDataArray;
index.js
import Constants from 'vtk.js/Sources/Common/Core/DataArray/Constants';
import * as macro from 'vtk.js/Sources/macros';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';

const { DefaultDataType } = Constants;
const TUPLE_HOLDER = [];

// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------

function createRangeHelper() {
let min = Number.MAX_VALUE;
let max = -Number.MAX_VALUE;
let count = 0;
let sum = 0;

return {
add(value) {
if (min > value) {
min = value;
}
if (max < value) {
max = value;
}
count++;
sum += value;
},
get() {
return { min, max, count, sum, mean: sum / count };
},
getRange() {
return { min, max };
},
};
}

function computeRange(values, component = 0, numberOfComponents = 1) {
const helper = createRangeHelper();
const size = values.length;
let value = 0;

if (component < 0 && numberOfComponents > 1) {
// Compute magnitude
for (let i = 0; i < size; i += numberOfComponents) {
value = 0;
for (let j = 0; j < numberOfComponents; j++) {
value += values[i + j] * values[i + j];
}
value **= 0.5;
helper.add(value);
}
return helper.getRange();
}

const offset = component < 0 ? 0 : component;
for (let i = offset; i < size; i += numberOfComponents) {
helper.add(values[i]);
}

return helper.getRange();
}

function ensureRangeSize(rangeArray, size = 0) {
const ranges = rangeArray || [];
// Pad ranges with null value to get the
while (ranges.length <= size) {
ranges.push(null);
}
return ranges;
}

function getDataType(typedArray) {
// Expects toString() to return "[object ...Array]"
return Object.prototype.toString.call(typedArray).slice(8, -1);
}

function getMaxNorm(normArray) {
const numComps = normArray.getNumberOfComponents();
let maxNorm = 0.0;
for (let i = 0; i < normArray.getNumberOfTuples(); ++i) {
const norm = vtkMath.norm(normArray.getTuple(i), numComps);
if (norm > maxNorm) {
maxNorm = norm;
}
}
return maxNorm;
}

// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------

export const STATIC = {
computeRange,
createRangeHelper,
getDataType,
getMaxNorm,
};

// ----------------------------------------------------------------------------
// vtkDataArray methods
// ----------------------------------------------------------------------------

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

function dataChange() {
model.ranges = null;
publicAPI.modified();
}

publicAPI.getElementComponentSize = () => model.values.BYTES_PER_ELEMENT;

// Description:
// Return the data component at the location specified by tupleIdx and
// compIdx.
publicAPI.getComponent = (tupleIdx, compIdx = 0) =>
model.values[tupleIdx * model.numberOfComponents + compIdx];

// Description:
// Set the data component at the location specified by tupleIdx and compIdx
// to value.
// Note that i is less than NumberOfTuples and j is less than
// NumberOfComponents. Make sure enough memory has been allocated
// (use SetNumberOfTuples() and SetNumberOfComponents()).
publicAPI.setComponent = (tupleIdx, compIdx, value) => {
if (value !== model.values[tupleIdx * model.numberOfComponents + compIdx]) {
model.values[tupleIdx * model.numberOfComponents + compIdx] = value;
dataChange();
}
};

publicAPI.getData = () => model.values;

publicAPI.getRange = (componentIndex = -1) => {
const rangeIdx =
componentIndex < 0 ? model.numberOfComponents : componentIndex;
let range = null;

if (!model.ranges) {
model.ranges = ensureRangeSize(model.ranges, model.numberOfComponents);
}
range = model.ranges[rangeIdx];

if (range) {
model.rangeTuple[0] = range.min;
model.rangeTuple[1] = range.max;
return model.rangeTuple;
}

// Need to compute ranges...
range = computeRange(
model.values,
componentIndex,
model.numberOfComponents
);
model.ranges[rangeIdx] = range;
model.rangeTuple[0] = range.min;
model.rangeTuple[1] = range.max;
return model.rangeTuple;
};

publicAPI.setRange = (rangeValue, componentIndex) => {
if (!model.ranges) {
model.ranges = ensureRangeSize(model.ranges, model.numberOfComponents);
}
const range = { min: rangeValue.min, max: rangeValue.max };

model.ranges[componentIndex] = range;
model.rangeTuple[0] = range.min;
model.rangeTuple[1] = range.max;

return model.rangeTuple;
};

publicAPI.setTuple = (idx, tuple) => {
const offset = idx * model.numberOfComponents;
for (let i = 0; i < model.numberOfComponents; i++) {
model.values[offset + i] = tuple[i];
}
};

publicAPI.getTuple = (idx, tupleToFill = TUPLE_HOLDER) => {
const numberOfComponents = model.numberOfComponents || 1;
if (tupleToFill.length !== numberOfComponents) {
tupleToFill.length = numberOfComponents;
}
const offset = idx * numberOfComponents;
// Check most common component sizes first
// to avoid doing a for loop if possible
if (numberOfComponents === 1) {
tupleToFill[0] = model.values[offset];
} else if (numberOfComponents === 2) {
tupleToFill[0] = model.values[offset];
tupleToFill[1] = model.values[offset + 1];
} else if (numberOfComponents === 3) {
tupleToFill[0] = model.values[offset];
tupleToFill[1] = model.values[offset + 1];
tupleToFill[2] = model.values[offset + 2];
} else if (numberOfComponents === 4) {
tupleToFill[0] = model.values[offset];
tupleToFill[1] = model.values[offset + 1];
tupleToFill[2] = model.values[offset + 2];
tupleToFill[3] = model.values[offset + 3];
} else {
for (let i = 0; i < numberOfComponents; i++) {
tupleToFill[i] = model.values[offset + i];
}
}
return tupleToFill;
};

publicAPI.getTupleLocation = (idx = 1) => idx * model.numberOfComponents;
publicAPI.getNumberOfComponents = () => model.numberOfComponents;
publicAPI.getNumberOfValues = () => model.values.length;
publicAPI.getNumberOfTuples = () =>
model.values.length / model.numberOfComponents;
publicAPI.getDataType = () => model.dataType;
/* eslint-disable no-use-before-define */
publicAPI.newClone = () =>
newInstance({
empty: true,
name: model.name,
dataType: model.dataType,
numberOfComponents: model.numberOfComponents,
});
/* eslint-enable no-use-before-define */

publicAPI.getName = () => {
if (!model.name) {
publicAPI.modified();
model.name = `vtkDataArray${publicAPI.getMTime()}`;
}
return model.name;
};

publicAPI.setData = (typedArray, numberOfComponents) => {
model.values = typedArray;
model.size = typedArray.length;
model.dataType = getDataType(typedArray);
if (numberOfComponents) {
model.numberOfComponents = numberOfComponents;
}
if (model.size % model.numberOfComponents !== 0) {
model.numberOfComponents = 1;
}
dataChange();
};

// Override serialization support
publicAPI.getState = () => {
const jsonArchive = { ...model, vtkClass: publicAPI.getClassName() };

// Convert typed array to regular array
jsonArchive.values = Array.from(jsonArchive.values);
delete jsonArchive.buffer;

// Clean any empty data
Object.keys(jsonArchive).forEach((keyName) => {
if (!jsonArchive[keyName]) {
delete jsonArchive[keyName];
}
});

// Sort resulting object by key name
const sortedObj = {};
Object.keys(jsonArchive)
.sort()
.forEach((name) => {
sortedObj[name] = jsonArchive[name];
});

// Remove mtime
if (sortedObj.mtime) {
delete sortedObj.mtime;
}

return sortedObj;
};
}

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

const DEFAULT_VALUES = {
name: '',
numberOfComponents: 1,
size: 0,
dataType: DefaultDataType,
rangeTuple: [0, 0],
// values: null,
// ranges: null,
};

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

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

if (!model.empty && !model.values && !model.size) {
throw new TypeError(
'Cannot create vtkDataArray object without: size > 0, values'
);
}

if (!model.values) {
model.values = macro.newTypedArray(model.dataType, model.size);
} else if (Array.isArray(model.values)) {
model.values = macro.newTypedArrayFrom(model.dataType, model.values);
}

if (model.values) {
model.size = model.values.length;
model.dataType = getDataType(model.values);
}

// Object methods
macro.obj(publicAPI, model);
macro.set(publicAPI, model, ['name', 'numberOfComponents']);

// Object specific methods
vtkDataArray(publicAPI, model);
}

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

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

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

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