@kitware/vtk.js
Version:
Visualization Toolkit for the Web
146 lines (129 loc) • 6.21 kB
JavaScript
import { create } from 'xmlbuilder2';
import { zlibSync } from 'fflate';
import { m as macro } from '../../macros2.js';
import { fromArrayBuffer } from '../../Common/Core/Base64.js';
import { FormatTypes, TYPED_ARRAY } from './XMLWriter/Constants.js';
// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------
function compressBlock(uncompressed) {
return zlibSync(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 fromArrayBuffer(headerUint8.buffer) + fromArrayBuffer(uint8.buffer);
}
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 overridden in subclass
publicAPI.create = dataObject => create().ele('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()
}).txt(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
};
// ----------------------------------------------------------------------------
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);
}
// ----------------------------------------------------------------------------
var vtkXMLWriter$1 = {
extend,
compressBlock,
processDataArray,
FormatTypes
};
export { vtkXMLWriter$1 as default, extend };