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

dataChange

Call this method when the underlying data has changed
This method calls modified()
For example, when you need to modify chunks of the array, it is faster
to get the underlying array with getData(), modify it, and then call
dataChange().

deepCopy

Deep copy of another vtkDataArray into this one.

Argument Type Required Description
other vtkDataArray Yes

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: {}) Must pass a number > 0 for size except if empty: true is also passed or a non-empty typed array for values.

fastComputeRange

Compute range of a given array, it only supports 1D arrays.

Argument Type Required Description
values Array. Yes Array to go through to extract the range from
offset Number Yes offset index to select the desired component in the tuple
numberOfComponents Number Yes size of tuple in a multi-channel array

findTuple

Convenient method to search the index of the first matching tuple in the array.
This is a naïve search, consider using a “locator” instead.

Argument Type Required Description
tupleToSearch Array. or TypedArray Yes
precision Number Yes (1e-6 by default)

Returns

Type Description
Number the index of the tuple if found, -1 otherwise.

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

Get the data type of this array as a string.

Returns

Type Description
String

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

Get the name of the array.

Returns

Type Description
String

getNumberOfComponents

Get the dimension (n) of the components.

Returns

Type Description
Number

getNumberOfTuples

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

Returns

Type Description
Number

getNumberOfValues

Get the actual number of values in the array, which is equal to getNumberOfTuples() * getNumberOfComponents().

Returns

Type Description
Number

getRange

Get the range of the given component.

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

getState

Get the state of this array.

Returns

Type Description
object

getTuple

Get the tuple at the given index.

For performance reasons, it is advised to pass a ‘tupleToFill’:
const x = [];
for (int i = 0; i < N; ++i) { dataArray.getTuple(idx, x); instead of:for (int i = 0; i < N; ++i) {
const x = dataArray.getTuple(idx);
...

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

Returns

Type Description
Array. or TypedArray

getTupleLocation

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

Returns

Type Description
Number

getTuples

Get the tuples between fromId (inclusive) and toId (exclusive).

If fromId or toId is negative, it refers to a tuple index from the
end of the underlying typedArray.
If the range between fromId and toId is invalid, getTuples returns
null.

NOTE: Any changes to the returned TypedArray will result in changes to
this DataArray’s underlying TypedArray.

Argument Type Required Description
fromId Number No (default: 0)
toId Number No (default: publicAPI.getNumberOfTuples())

Returns

Type Description
Nullable.

initialize

Reset this array.
NOTE: This won’t touch the actual memory of the underlying typedArray.

insertNextTuple

Insert the given tuple at the next available slot and return the index of the insertion.
NOTE: May resize the data values array. “Safe” version of setTuple.

Argument Type Required Description
tuple Array. or TypedArray Yes

Returns

Type Description
Number Index of the inserted tuple.

insertNextTuples

Convenience function to insert an array of tuples with insertNextTuple.
NOTE: tuples.length must be a multiple of getNumberOfComponents.

Argument Type Required Description
tuples Array. or TypedArray Yes

Returns

Type Description
The index of the last inserted tuple

insertTuple

Insert the given tuple at the given index.
NOTE: May resize the data values array. “Safe” version of setTuple.

A typical usage is when vtkDataArray is initialized with
initialValues = { size: 0, values: new Uint8Array(1000) }, where
an empty but pre-allocated array with 1’000 components is created.
The component values can then be inserted with insertTuple() or
insertNextTuple() without requiring new memory allocation until
the size of 1’000 is exceeded (e.g. after inserting the 250th
4-component tuple).

insertTuple increases the number of tuples (getNumberOfTuples()).

Argument Type Required Description
idx Number Yes
tuple Array. or TypedArray Yes

Returns

Type Description
Number Index of the inserted tuple

insertTuples

Insert tuples starting at the given idx.

Argument Type Required Description
idx Number Yes
tuples Array. or TypedArray Yes Flat array of tuples to insert

Returns

Type Description
The index of the last inserted tuple

interpolateTuple

Interpolate between the tuples retrieved from source1
and source2 with the resp. indices and set the
resulting tuple to the idx of this DataArray.

Argument Type Required Description
idx, int Yes
source1, vtkDataArray Yes
source1Idx, int Yes
source2, vtkDataArray Yes
source2Idx, int Yes
t float Yes

newClone

Return a clone of this array.

Returns

Type Description
vtkDataArray

newInstance

Method use to create a new instance of vtkDataArray

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

resize

Resize the array to the requested number of tuples and preserve data.
Increasing the array size may allocate extra memory beyond what was
requested.
Decreasing the array size will trim memory to the requested size.
model.size WILL be modified according ot the new size.
If requestedNumTuples > getNumberOfTuples(),
it creates a new typed array and copies the old values to the new array.
If requestedNumTuples < getNumberOfTuples(), the typed array is untouched,
only model.size is modified.

Argument Type Required Description
requestedNumTuples Number Yes Final expected number of tuples; must be >= 0

Returns

Type Description
Boolean True if a resize occured, false otherwise

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

Set the data of this array.
Optionally pass ´numberOfComponents´ to overwrite this dataArray’s
numberOfComponents.
If this dataArray’s numberOfComponents doesn’t divide the given array’s
length, this dataArray’s numberOfComponents is set to 1.

Argument Type Required Description
typedArray Array. or TypedArray Yes The Array value.
numberOfComponents Number No

setName

Set the name of this array.

Argument Type Required Description
name String Yes

Returns

Type Description
Boolean

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

Set the given tuple at the given index.

Argument Type Required Description
idx Number Yes
tuple Array. or TypedArray Yes

setTuples

Set the given tuples starting at the given index.

Argument Type Required Description
idx Number Yes
tuples Array. or TypedArray 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',
UNSIGNED_CHAR_CLAMPED: 'Uint8ClampedArray', // should be used for VTK.js internal purpose only
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 { float, int, Nullable, Range, 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;
}

/**
* The inital values of a vtkDataArray.
*/
export interface IDataArrayInitialValues {
dataType?: string;
empty?: boolean;
name?: string;
numberOfComponents?: number;
rangeTuple?: Range;
size?: number;
values?: Array<number> | TypedArray;
}

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(): number[] | TypedArray;

/**
* Call this method when the underlying data has changed
* This method calls `modified()`
* For example, when you need to modify chunks of the array, it is faster
* to get the underlying array with `getData()`, modify it, and then call
* `dataChange()`.
*/
dataChange(): void;

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

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

/**
* Set the given tuple at the given index.
* @param {Number} idx
* @param {Array<Number>|TypedArray} tuple
*/
setTuple(idx: number, tuple: Array<number> | TypedArray): void;

/**
* Set the given tuples starting at the given index.
* @param {Number} idx
* @param {Array<Number>|TypedArray} tuples
*/
setTuples(idx: number, tuples: Array<number> | TypedArray): void;

/**
* Get the tuple at the given index.
*
* For performance reasons, it is advised to pass a 'tupleToFill':
* `const x = [];`
* `for (int i = 0; i < N; ++i) {
* ` dataArray.getTuple(idx, x);`
* ` ...`
* instead of:
* `for (int i = 0; i < N; ++i) {
* ` const x = dataArray.getTuple(idx);`
* `...`
* @param {Number} idx
* @param {Number[]|TypedArray} [tupleToFill] (default [])
* @returns {Number[]|TypedArray}
*/
getTuple(
idx: number,
tupleToFill?: number[] | TypedArray
): number[] | TypedArray;

/**
* Get the tuples between fromId (inclusive) and toId (exclusive).
*
* If fromId or toId is negative, it refers to a tuple index from the
* end of the underlying typedArray.
* If the range between fromId and toId is invalid, getTuples returns
* null.
*
* NOTE: Any changes to the returned TypedArray will result in changes to
* this DataArray's underlying TypedArray.
*
* @param {Number} [fromId] (default: 0)
* @param {Number} [toId] (default: publicAPI.getNumberOfTuples())
* @returns {Nullable<TypedArray>}
*/
getTuples(fromId?: number, toId?: number): Nullable<TypedArray>;

/**
* Insert the given tuple at the given index.
* NOTE: May resize the data values array. "Safe" version of setTuple.
*
* A typical usage is when `vtkDataArray` is initialized with
* `initialValues = { size: 0, values: new Uint8Array(1000) }`, where
* an empty but pre-allocated array with 1'000 components is created.
* The component values can then be inserted with `insertTuple()` or
* `insertNextTuple()` without requiring new memory allocation until
* the size of 1'000 is exceeded (e.g. after inserting the 250th
* 4-component tuple).
*
* `insertTuple` increases the number of tuples (`getNumberOfTuples()`).
*
* @see insertNextTuple
* @see getNumberOfTuples
*
* @param {Number} idx
* @param {Array<Number>|TypedArray} tuple
* @returns {Number} Index of the inserted tuple
*/
insertTuple(idx: number, tuple: Array<number> | TypedArray): number;

/**
* Insert tuples starting at the given idx.
*
* @param {Number} idx
* @param {Array<Number>|TypedArray} tuples Flat array of tuples to insert
* @returns The index of the last inserted tuple
*/
insertTuples(idx: number, tuples: Array<number> | TypedArray): number;

/**
* Insert the given tuple at the next available slot and return the index of the insertion.
* NOTE: May resize the data values array. "Safe" version of setTuple.
*
* @see insertTuple
*
* @param {Array<Number>|TypedArray} tuple
* @returns {Number} Index of the inserted tuple.
*/
insertNextTuple(tuple: Array<number> | TypedArray): number;

/**
* Convenience function to insert an array of tuples with insertNextTuple.
* NOTE: tuples.length must be a multiple of `getNumberOfComponents`.
* @param {Array<Number>|TypedArray} tuples
* @returns The index of the last inserted tuple
*/
insertNextTuples(tuples: Array<number> | TypedArray): number;

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

/**
* Get the dimension (n) of the components.
* @returns {Number}
*/
getNumberOfComponents(): number;

/**
* Get the actual number of values in the array, which is equal to `getNumberOfTuples() * getNumberOfComponents()`.
* @returns {Number}
*/
getNumberOfValues(): number;

/**
* Get the actual number of complete tuples (a component group) in the array.
* @returns {Number}
*/
getNumberOfTuples(): number;

/**
* Convenient method to search the index of the first matching tuple in the array.
* This is a naïve search, consider using a "locator" instead.
* @param {Array<Number>|TypedArray} tupleToSearch
* @param {Number} precision (1e-6 by default)
* @returns {Number} the index of the tuple if found, -1 otherwise.
*/
findTuple(
tupleToSearch: Array<number> | TypedArray,
precision?: number
): number;

/**
* Get the data type of this array as a string.
* @returns {String}
*/
getDataType(): string;

/**
* Return a clone of this array.
* @returns {vtkDataArray}
*/
newClone(): vtkDataArray;

/**
* Get the name of the array.
* @returns {String}
*/
getName(): string;

/**
* Set the data of this array.
* Optionally pass ´numberOfComponents´ to overwrite this dataArray's
* numberOfComponents.
* If this dataArray's numberOfComponents doesn't divide the given array's
* length, this dataArray's numberOfComponents is set to 1.
*
* @param {Number[]|TypedArray} typedArray The Array value.
* @param {Number} [numberOfComponents]
*/
setData(typedArray: number[] | TypedArray, numberOfComponents?: number): void;

/**
* Get the state of this array.
* @returns {object}
*/
getState(): object;

/**
* Deep copy of another vtkDataArray into this one.
* @param {vtkDataArray} other
*/
deepCopy(other: vtkDataArray): void;

/**
* Interpolate between the tuples retrieved from source1
* and source2 with the resp. indices and set the
* resulting tuple to the idx of this DataArray.
*
* @param {int} idx,
* @param {vtkDataArray} source1,
* @param {int} source1Idx,
* @param {vtkDataArray} source2,
* @param {int} source2Idx,
* @param {float} t
*/
interpolateTuple(
idx: int,
source1: vtkDataArray,
source1Idx: int,
source2: vtkDataArray,
source2Idx: int,
t: float
): void;

/**
* Resize the array to the requested number of tuples and preserve data.
* Increasing the array size may allocate extra memory beyond what was
* requested.
* Decreasing the array size will trim memory to the requested size.
* model.size WILL be modified according ot the new size.
* If requestedNumTuples > getNumberOfTuples(),
* it creates a new typed array and copies the old values to the new array.
* If requestedNumTuples < getNumberOfTuples(), the typed array is untouched,
* only model.size is modified.
* @param {Number} requestedNumTuples Final expected number of tuples; must be >= 0
* @returns {Boolean} True if a resize occured, false otherwise
* @see insertNextTuple
* @see insertNextTuples
* @see initialize
*/
resize(requestedNumTuples: number): boolean;

/**
* Reset this array.
* NOTE: This won't touch the actual memory of the underlying typedArray.
* @see insertNextTuple
* @see insertNextTuples
*/
initialize(): void;

// --- via macro --

/**
* Set the name of this array.
* @param {String} name
* @returns {Boolean}
*/
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: ArrayLike<number>,
component?: number,
numberOfComponents?: number
): vtkRange;

/**
* Compute range of a given array, it only supports 1D arrays.
*
* @param {Number[]} values Array to go through to extract the range from
* @param {Number} offset offset index to select the desired component in the tuple
* @param {Number} numberOfComponents size of tuple in a multi-channel array
*/
export function fastComputeRange(
values: ArrayLike<number>,
offset: number,
numberOfComponents: number
): vtkRange;

/**
* @deprecated please use `fastComputeRange` instead
* 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: {}) Must pass a number > 0 for `size` except if `empty: true` is also passed or a non-empty typed array for `values`.
*/
export function extend(
publicAPI: object,
model: object,
initialValues?: IDataArrayInitialValues
): 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;
fastComputeRange: typeof fastComputeRange;
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 { vtkErrorMacro } = macro;
const { DefaultDataType } = Constants;

// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------
const EPSILON = 1e-6;

// Original source from https://www.npmjs.com/package/compute-range
// Modified to accept type arrays
function fastComputeRange(arr, offset, numberOfComponents) {
const len = arr.length;
let min = Number.MAX_VALUE;
let max = -Number.MAX_VALUE;
let x;
let i;

// find first non-NaN value
for (i = offset; i < len; i += numberOfComponents) {
if (!Number.isNaN(arr[i])) {
min = arr[i];
max = min;
break;
}
}

for (; i < len; i += numberOfComponents) {
x = arr[i];
if (x < min) {
min = x;
} else if (x > max) {
max = x;
}
}
return { min, max };
}

/**
* @deprecated please use fastComputeRange instead
*/
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) {
if (component < 0 && numberOfComponents > 1) {
// Compute magnitude
const size = values.length;
const numberOfValues = size / numberOfComponents;
const data = new Float64Array(numberOfValues);
for (let i = 0, j = 0; i < numberOfValues; ++i) {
for (let nextJ = j + numberOfComponents; j < nextJ; ++j) {
data[i] += values[j] * values[j];
}
data[i] **= 0.5;
}
return fastComputeRange(data, 0, 1);
}

return fastComputeRange(
values,
component < 0 ? 0 : component,
numberOfComponents
);
}

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;
const tuple = new Array(numComps);
for (let i = 0; i < normArray.getNumberOfTuples(); ++i) {
normArray.getTuple(i, tuple);
const norm = vtkMath.norm(tuple, numComps);
if (norm > maxNorm) {
maxNorm = norm;
}
}
return maxNorm;
}

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

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

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

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

/**
* Resize model.values and copy the old values to the new array.
* @param {Number} requestedNumTuples Final expected number of tuples; must be >= 0
* @returns {Boolean} True if a resize occured, false otherwise
*/
function resize(requestedNumTuples) {
if (requestedNumTuples < 0) {
return false;
}

const numComps = publicAPI.getNumberOfComponents();
const curNumTuples = model.values.length / (numComps > 0 ? numComps : 1);
if (requestedNumTuples === curNumTuples) {
return true;
}

if (requestedNumTuples > curNumTuples) {
// Requested size is bigger than current size. Allocate enough
// memory to fit the requested size and be more than double the
// currently allocated memory.
const oldValues = model.values;
model.values = macro.newTypedArray(
model.dataType,
(requestedNumTuples + curNumTuples) * numComps
);
model.values.set(oldValues);
return true;
}

// Requested size is smaller than currently allocated size
if (model.size > requestedNumTuples * numComps) {
model.size = requestedNumTuples * numComps;
publicAPI.dataChange();
}

return true;
}

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

publicAPI.resize = (requestedNumTuples) => {
resize(requestedNumTuples);
const newSize = requestedNumTuples * publicAPI.getNumberOfComponents();
if (model.size !== newSize) {
model.size = newSize;
publicAPI.dataChange();
return true;
}
return false;
};

// FIXME, to rename into "clear()" or "reset()"
publicAPI.initialize = () => {
publicAPI.resize(0);
};

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;
publicAPI.dataChange();
}
};

publicAPI.getValue = (valueIdx) => {
const idx = valueIdx / model.numberOfComponents;
const comp = valueIdx % model.numberOfComponents;
return publicAPI.getComponent(idx, comp);
};

publicAPI.setValue = (valueIdx, value) => {
const idx = valueIdx / model.numberOfComponents;
const comp = valueIdx % model.numberOfComponents;
publicAPI.setComponent(idx, comp, value);
};

publicAPI.getData = () =>
model.size === model.values.length
? model.values
: model.values.subarray(0, model.size);

publicAPI.getRange = (componentIndex = -1) => {
let rangeIdx = componentIndex;
if (rangeIdx < 0) {
// If scalar data, then store in slot 0 (same as componentIndex = 0).
// If vector data, then store in last slot.
rangeIdx = model.numberOfComponents === 1 ? 0 : model.numberOfComponents;
}

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(
publicAPI.getData(),
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.setTuples = (idx, tuples) => {
let i = idx * model.numberOfComponents;
const last = Math.min(tuples.length, model.size - i);
for (let j = 0; j < last; ) {
model.values[i++] = tuples[j++];
}
};

publicAPI.insertTuple = (idx, tuple) => {
if (model.size <= idx * model.numberOfComponents) {
model.size = (idx + 1) * model.numberOfComponents;
resize(idx + 1);
}
publicAPI.setTuple(idx, tuple);
return idx;
};

publicAPI.insertTuples = (idx, tuples) => {
const end = idx + tuples.length / model.numberOfComponents;
if (model.size < end * model.numberOfComponents) {
model.size = end * model.numberOfComponents;
resize(end);
}
publicAPI.setTuples(idx, tuples);
return end;
};

publicAPI.insertNextTuple = (tuple) => {
const idx = model.size / model.numberOfComponents;
return publicAPI.insertTuple(idx, tuple);
};

publicAPI.insertNextTuples = (tuples) => {
const idx = model.size / model.numberOfComponents;
return publicAPI.insertTuples(idx, tuples);
};

publicAPI.findTuple = (tuple, precision = EPSILON) => {
for (let i = 0; i < model.size; i += model.numberOfComponents) {
if (Math.abs(tuple[0] - model.values[i]) <= precision) {
let match = true;
for (let j = 1; j < model.numberOfComponents; ++j) {
if (Math.abs(tuple[j] - model.values[i + j]) > precision) {
match = false;
break;
}
}
if (match) {
return i / model.numberOfComponents;
}
}
}
return -1;
};

publicAPI.getTuple = (idx, tupleToFill = []) => {
const numberOfComponents = model.numberOfComponents || 1;
const offset = idx * numberOfComponents;
// Check most common component sizes first
// to avoid doing a for loop if possible
switch (numberOfComponents) {
case 4:
tupleToFill[3] = model.values[offset + 3];
// eslint-disable-next-line no-fallthrough
case 3:
tupleToFill[2] = model.values[offset + 2];
// eslint-disable-next-line no-fallthrough
case 2:
tupleToFill[1] = model.values[offset + 1];
// eslint-disable-next-line no-fallthrough
case 1:
tupleToFill[0] = model.values[offset];
break;
default:
for (let i = numberOfComponents - 1; i >= 0; --i) {
tupleToFill[i] = model.values[offset + i];
}
}
return tupleToFill;
};

publicAPI.getTuples = (fromId, toId) => {
const from = (fromId ?? 0) * model.numberOfComponents;
const to =
(toId ?? publicAPI.getNumberOfTuples()) * model.numberOfComponents;
const arr = publicAPI.getData().subarray(from, to);
return arr.length > 0 ? arr : null;
};

publicAPI.getTupleLocation = (idx = 1) => idx * model.numberOfComponents;
publicAPI.getNumberOfComponents = () => model.numberOfComponents;
publicAPI.getNumberOfValues = () => model.size;
publicAPI.getNumberOfTuples = () => model.size / 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;
}
publicAPI.dataChange();
};

// Override serialization support
publicAPI.getState = () => {
if (model.deleted) {
return null;
}
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;
};

publicAPI.deepCopy = (other) => {
// Retain current dataType and array reference before shallowCopy call.
const currentType = publicAPI.getDataType();
const currentArray = model.values;
publicAPI.shallowCopy(other);

// Avoid array reallocation if size already sufficient
// and dataTypes match.
if (
currentArray?.length >= other.getNumberOfValues() &&
currentType === other.getDataType()
) {
currentArray.set(other.getData());
model.values = currentArray;
publicAPI.dataChange();
} else {
publicAPI.setData(other.getData().slice());
}
};

publicAPI.interpolateTuple = (
idx,
source1,
source1Idx,
source2,
source2Idx,
t
) => {
const numberOfComponents = model.numberOfComponents || 1;
if (
numberOfComponents !== source1.getNumberOfComponents() ||
numberOfComponents !== source2.getNumberOfComponents()
) {
vtkErrorMacro('numberOfComponents must match');
}

const tuple1 = source1.getTuple(source1Idx);
const tuple2 = source2.getTuple(source2Idx);
const out = [];
out.length = numberOfComponents;

// Check most common component sizes first
// to avoid doing a for loop if possible
switch (numberOfComponents) {
case 4:
out[3] = tuple1[3] + (tuple2[3] - tuple1[3]) * t;
// eslint-disable-next-line no-fallthrough
case 3:
out[2] = tuple1[2] + (tuple2[2] - tuple1[2]) * t;
// eslint-disable-next-line no-fallthrough
case 2:
out[1] = tuple1[1] + (tuple2[1] - tuple1[1]) * t;
// eslint-disable-next-line no-fallthrough
case 1:
out[0] = tuple1[0] + (tuple2[0] - tuple1[0]) * t;
break;
default:
for (let i = 0; i < numberOfComponents; i++) {
out[i] = tuple1[i] + (tuple2[i] - tuple1[i]) * t;
}
}

return publicAPI.insertTuple(idx, out);
};
}

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

// size: The current size of the dataArray.
// NOTE: The underlying typed array may be larger than 'size'.
const DEFAULT_VALUES = {
name: '',
numberOfComponents: 1,
dataType: DefaultDataType,
rangeTuple: [0, 0],
// size: undefined,
// 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) {
// Takes the size if provided (can be lower than `model.values`) otherwise the actual length of `values`.
model.size = model.size ?? model.values.length;
model.dataType = getDataType(model.values);
}

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

if (model.size % model.numberOfComponents !== 0) {
throw new RangeError(
'model.size is not a multiple of model.numberOfComponents'
);
}

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

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

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

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

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