XMLWriter

Source

Constants.js
export const FormatTypes = {
ASCII: 'ascii',
BINARY: 'binary',
APPENDED: 'appended',
};

export const TYPED_ARRAY = {
Int8Array: 'Int8',
Uint8Array: 'UInt8',
Int16Array: 'Int16',
Uint16Array: 'UInt16',
Int32Array: 'Int32',
Uint32Array: 'UInt32',
Float32Array: 'Float32',
Float64Array: 'Float64',
};

export default {
FormatTypes,
};
index.js
import builder from 'xmlbuilder';
import { fromByteArray } from 'base64-js';
import pako from 'pako';

import macro from 'vtk.js/Sources/macro';
import {
FormatTypes,
TYPED_ARRAY,
} from 'vtk.js/Sources/IO/XML/XMLWriter/Constants';

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

function compressBlock(uncompressed) {
return pako.deflate(uncompressed);
}

function processDataArray(
dataArray,
format,
blockSize,
compressor = 'vtkZLibDataCompressor'
) {
if (format === FormatTypes.ASCII) {
return dataArray.getData().join(' ');
}
if (format === FormatTypes.BINARY) {
if (compressor === 'vtkZLibDataCompressor') {
// ----------------------------------------------------------------------
// Layout of the data
// header[N, s1, s1, blockSize1, ..., blockSizeN], [padding???], block[compressedData], ..., block[compressedData]
// [header] N, s1 and s2 are uint 32 or 64 (defined by header_type="UInt64" attribute on the root node)
// [header] s1: uncompress size of each block except the last one
// [header] s2: uncompress size of the last blocks
// [header] blockSize: size of the block in compressed space that represent to bloc to inflate in zlib. (This also give the offset to the next block)
// ----------------------------------------------------------------------

const componentUint8Size = dataArray.getElementComponentSize();
const uncompressedUint8Size =
dataArray.getNumberOfValues() * componentUint8Size;
const blockUint8Size = blockSize;
const nbFullBlocks = Math.trunc(uncompressedUint8Size / blockUint8Size);
const lastBlockUint8Size = uncompressedUint8Size % blockUint8Size;
const nbBlocks = nbFullBlocks + (lastBlockUint8Size ? 1 : 0);
const header = new Uint32Array(3 + nbBlocks);
header[0] = nbBlocks; // N
header[1] = blockUint8Size; // s1
header[2] = lastBlockUint8Size; // s2

let totalUint8Length = 0;
const blocks = [];
let dataOffset = 0;
const lastBlockId = nbBlocks - 1;
for (let blockId = 0; blockId < nbBlocks; ++blockId) {
const currentBlockUint8Size =
lastBlockUint8Size === 0 || blockId < lastBlockId
? blockUint8Size
: header[2];
const uncompressedBlock = new Uint8Array(
dataArray.getData().buffer,
dataOffset,
currentBlockUint8Size
);
dataOffset += blockUint8Size;
const compressedUint8Block = compressBlock(uncompressedBlock);
blocks.push(compressedUint8Block);
header[3 + blockId] = compressedUint8Block.length;
totalUint8Length += compressedUint8Block.length;
}
const uint8 = new Uint8Array(totalUint8Length);
let uint8Offset = 0;
const headerUint8 = new Uint8Array(header.buffer);
for (let blockId = 0; blockId < nbBlocks; ++blockId) {
uint8.set(blocks[blockId], uint8Offset);
uint8Offset += header[3 + blockId];
}
return fromByteArray(headerUint8) + fromByteArray(uint8);
}
throw new Error('Only vtkZLibDataCompressor is supported');
}
if (format === FormatTypes.APPENDED) {
throw new Error('Appended format is not supported');
}
throw new Error('Format is not supported');
}

// ----------------------------------------------------------------------------
// vtkXMLWriter methods
// ----------------------------------------------------------------------------

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

// Can be overriden in subclass
publicAPI.create = (dataObject) =>
builder
.create('VTKFile')
.att('type', model.dataType)
.att('version', '0.1')
.att('byte_order', 'LittleEndian')
.att('header_type', 'UInt32')
.att(
'compressor',
model.format === FormatTypes.ASCII ? '' : 'vtkZLibDataCompressor'
);

publicAPI.write = (object) => publicAPI.create(object).end({ pretty: true });

publicAPI.processDataSetAttributes = (
parentElement,
name,
datasetAttributes
) => {
const activeAttributes = {};
const attrTypes = [
'Scalars',
'Vectors',
'Normals',
'TCoords',
'Tensors',
'GlobalIds',
'PedigreeIds',
];
attrTypes.forEach((attrType) => {
const activeAttribute = datasetAttributes.getActiveAttribute(attrType);
if (activeAttribute) {
activeAttributes[attrType] = activeAttribute.getName();
}
});

const datasetAttributesEle = parentElement.ele(name, activeAttributes);

for (let i = 0; i < datasetAttributes.getNumberOfArrays(); ++i) {
publicAPI.processDataArray(
datasetAttributesEle,
datasetAttributes.getArrayByIndex(i)
);
}
return datasetAttributesEle;
};

publicAPI.processDataArray = (parentEle, scalars) =>
parentEle.ele(
'DataArray',
{
type: TYPED_ARRAY[scalars.getDataType()],
Name: scalars.getName(),
format: publicAPI.getFormat(),
RangeMin: scalars.getRange()[0],
RangeMax: scalars.getRange()[1],
NumberOfComponents: scalars.getNumberOfComponents(),
},
processDataArray(scalars, publicAPI.getFormat(), publicAPI.getBlockSize())
);

publicAPI.requestData = (inData, outData) => {
model.file = publicAPI.write(inData);
};
}

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

const DEFAULT_VALUES = {
blockSize: 1024,
// file: null,
format: FormatTypes.BINARY,
};

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

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

// Build VTK API
macro.obj(publicAPI, model);
macro.setGet(publicAPI, model, ['blockSize', 'format']);
macro.get(publicAPI, model, ['file']);
macro.algo(publicAPI, model, 1, 0);

// vtkXMLWriter methods
vtkXMLWriter(publicAPI, model);
}

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

export default { extend, compressBlock, processDataArray, FormatTypes };