Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Sources/Common/Core/DataArray/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { vtkObject, vtkRange } from '../../../interfaces';
import { vtkObject, vtkRange, GetStateOptions } from '../../../interfaces';
import { float, int, Nullable, Range, TypedArray } from '../../../types';

/**
Expand Down Expand Up @@ -274,9 +274,12 @@ export interface vtkDataArray extends vtkObject {

/**
* Get the state of this array.
*
* Pass `{ preserveTypedArrays: true }` to keep TypedArray values
* without converting and copying to a plain Array.
* @returns {object}
*/
getState(): object;
getState(options?: GetStateOptions): object;

/**
* Deep copy of another vtkDataArray into this one.
Expand Down
7 changes: 4 additions & 3 deletions Sources/Common/Core/DataArray/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -450,14 +450,15 @@ function vtkDataArray(publicAPI, model) {
};

// Override serialization support
publicAPI.getState = () => {
publicAPI.getState = ({ preserveTypedArrays = false } = {}) => {
if (model.deleted) {
return null;
}
const jsonArchive = { ...model, vtkClass: publicAPI.getClassName() };

// Convert typed array to regular array
jsonArchive.values = Array.from(jsonArchive.values);
if (!preserveTypedArrays) {
jsonArchive.values = Array.from(jsonArchive.values);
}
delete jsonArchive.buffer;

// Clean any empty data
Expand Down
29 changes: 29 additions & 0 deletions Sources/Common/Core/DataArray/test/testDataArray.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import test from 'tape';
import vtk from 'vtk.js/Sources/vtk';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
Expand Down Expand Up @@ -508,3 +509,31 @@ test('Test vtkDataArray resize function', (t) => {

t.end();
});

test('Test vtkDataArray getState preserveTypedArrays option', (t) => {
const values = new Uint8Array([1, 2, 3, 4, 5]);
const da = vtkDataArray.newInstance({ values });

// Default: values converted to plain Array
const state = da.getState();
t.ok(Array.isArray(state.values), 'default getState returns plain Array');

// With option: values preserved as TypedArray
const transferable = da.getState({ preserveTypedArrays: true });
t.ok(
transferable.values instanceof Uint8Array,
'TypedArray type is preserved'
);

// Round-trip via vtk() works with TypedArray values
const da2 = vtk(transferable);
t.ok(da2, 'Can reconstruct from state with TypedArray values');
t.deepEqual(
Array.from(da2.getData()),
Array.from(values),
'Values preserved after round-trip'
);
t.equal(da2.getDataType(), 'Uint8Array', 'Data type preserved');

t.end();
});
6 changes: 3 additions & 3 deletions Sources/Common/DataModel/DataSetAttributes/FieldData.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,11 @@ function vtkFieldData(publicAPI, model) {
publicAPI.getNumberOfTuples = () =>
model.arrays.length > 0 ? model.arrays[0].getNumberOfTuples() : 0;

publicAPI.getState = () => {
const result = superGetState();
publicAPI.getState = (options) => {
const result = superGetState(options);
if (result) {
result.arrays = model.arrays.map((item) => ({
data: item.data.getState(),
data: item.data.getState(options),
}));
}
return result;
Expand Down
25 changes: 21 additions & 4 deletions Sources/interfaces.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ import vtkDataArray from './Common/Core/DataArray';
import { vtkPipelineConnection } from './types';
import { EVENT_ABORT, VOID } from './macros';

export interface GetStateOptions {
/**
* When true, TypedArrays are preserved without converting and
* copying to plain Arrays. Use for structured clone / postMessage.
*
* Note: the resulting state is not JSON-safe. TypedArrays serialize
* as `{"0":v,"1":v,...}` via `JSON.stringify`, not as arrays.
* @default false
*/
preserveTypedArrays?: boolean;
}

/**
* Object returned on any subscription call
*/
Expand Down Expand Up @@ -241,15 +253,20 @@ export interface vtkObject {
* Such state can then be reused to clone or rebuild a full
* vtkObject tree using the root vtk() function.
*
* The following example will grab mapper and dataset that are
* beneath the vtkActor instance as well.
*
* ```
* const actorStr = JSON.stringify(actor.getState());
* const newActor = vtk(JSON.parse(actorStr));
* ```
*
* Pass `{ preserveTypedArrays: true }` to keep TypedArrays
* without converting and copying to plain Arrays. Useful for
* structured clone / postMessage transfers.
*
* ```
* worker.postMessage(dataset.getState({ preserveTypedArrays: true }));
* ```
*/
getState(): object;
getState(options?: GetStateOptions): object;

/**
* Used internally by JSON.stringify to get the content to serialize.
Expand Down
14 changes: 10 additions & 4 deletions Sources/macros.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,11 @@ export function obj(publicAPI = {}, model = {}) {
};

// Add serialization support
publicAPI.getState = () => {
publicAPI.getState = ({ preserveTypedArrays = false } = {}) => {
if (model.deleted) {
return null;
}
const options = { preserveTypedArrays };
const jsonArchive = { ...model, vtkClass: publicAPI.getClassName() };

// Convert every vtkObject to its serializable form
Expand All @@ -383,11 +384,16 @@ export function obj(publicAPI = {}, model = {}) {
) {
delete jsonArchive[keyName];
} else if (jsonArchive[keyName].isA) {
jsonArchive[keyName] = jsonArchive[keyName].getState();
jsonArchive[keyName] = jsonArchive[keyName].getState(options);
} else if (Array.isArray(jsonArchive[keyName])) {
jsonArchive[keyName] = jsonArchive[keyName].map(getStateArrayMapFunc);
jsonArchive[keyName] = jsonArchive[keyName].map((item) =>
item && item.isA ? item.getState(options) : item
);
} else if (isTypedArray(jsonArchive[keyName])) {
jsonArchive[keyName] = Array.from(jsonArchive[keyName]);
if (!preserveTypedArrays) {
jsonArchive[keyName] = Array.from(jsonArchive[keyName]);
}
// else: keep TypedArray as-is for structured clone / postMessage
}
});

Expand Down
Loading