All files / Sources/IO/XML/XMLWriter index.js

35.29% Statements 24/68
10% Branches 2/20
60% Functions 6/10
34.37% Lines 22/64

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197                                                                                                                                                                                              3x     3x 1x                     3x   3x         2x 2x                 2x 14x 14x         2x   2x           2x     3x                                   3x                 1x                 3x     3x 3x 3x 3x     3x            
import { create } from 'xmlbuilder2';
import { zlibSync } from 'fflate';
 
import macro from 'vtk.js/Sources/macros';
import { fromArrayBuffer } from 'vtk.js/Sources/Common/Core/Base64';
import {
  FormatTypes,
  TYPED_ARRAY,
} from 'vtk.js/Sources/IO/XML/XMLWriter/Constants';
 
// ----------------------------------------------------------------------------
// 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);
      Iif (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,
};
 
// ----------------------------------------------------------------------------
 
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 };