UNPKG

@acransac/vtk.js

Version:

Visualization Toolkit for the Web

251 lines (217 loc) 6.7 kB
import JSZip from 'jszip'; import pako from 'pako'; import macro from 'vtk.js/Sources/macro'; import Endian from 'vtk.js/Sources/Common/Core/Endian'; import { DataTypeByteSize } from 'vtk.js/Sources/Common/Core/DataArray/Constants'; const { vtkErrorMacro, vtkDebugMacro } = macro; function toMimeType(url) { const ext = url.split('.').pop().toLowerCase(); if (ext === 'jpg') { return 'jpeg'; } return ext; } function handleUint8Array(array, compression, done) { return (uint8array) => { array.buffer = new ArrayBuffer(uint8array.length); // copy uint8array to buffer const view = new Uint8Array(array.buffer); view.set(uint8array); if (compression) { if (array.dataType === 'string' || array.dataType === 'JSON') { array.buffer = pako.inflate(new Uint8Array(array.buffer), { to: 'string', }); } else { array.buffer = pako.inflate(new Uint8Array(array.buffer)).buffer; } } if (array.ref.encode === 'JSON') { array.values = JSON.parse(array.buffer); } else { if (Endian.ENDIANNESS !== array.ref.encode && Endian.ENDIANNESS) { // Need to swap bytes vtkDebugMacro(`Swap bytes of ${array.name}`); Endian.swapBytes(array.buffer, DataTypeByteSize[array.dataType]); } array.values = new window[array.dataType](array.buffer); } if (array.values.length !== array.size) { vtkErrorMacro( `Error in FetchArray: ${array.name} does not have the proper array size. Got ${array.values.length}, instead of ${array.size}` ); } done(); }; } function handleString(array, compression, done) { return (string) => { if (compression) { array.values = JSON.parse(pako.inflate(string, { to: 'string' })); } else { array.values = JSON.parse(string); } done(); }; } const handlers = { uint8array: handleUint8Array, string: handleString, }; function removeLeadingSlash(str) { return str[0] === '/' ? str.substr(1) : str; } function normalizePath(str) { return new URL(str, 'http://any').pathname; } function cleanUpPath(str) { return removeLeadingSlash(normalizePath(str)); } function create(createOptions) { let ready = false; let requestCount = 0; const zip = new JSZip(); let zipRoot = zip; zip.loadAsync(createOptions.zipContent).then(() => { ready = true; // Find root index.json const metaFiles = []; zip.forEach((relativePath, zipEntry) => { if (relativePath.indexOf('index.json') !== -1) { metaFiles.push(relativePath); } }); metaFiles.sort((a, b) => a.length - b.length); const fullRootPath = metaFiles[0].split('/'); while (fullRootPath.length > 1) { const dirName = fullRootPath.shift(); zipRoot = zipRoot.folder(dirName); } if (createOptions.callback) { createOptions.callback(zip); } }); return { fetchArray(instance = {}, baseURL, array, options = {}) { return new Promise((resolve, reject) => { if (!ready) { vtkErrorMacro('ERROR!!! zip not ready...'); } const url = cleanUpPath( [ baseURL, array.ref.basepath, options.compression ? `${array.ref.id}.gz` : array.ref.id, ].join('/') ); if (++requestCount === 1 && instance.invokeBusy) { instance.invokeBusy(true); } function doneCleanUp() { // Done with the ref and work delete array.ref; if (--requestCount === 0 && instance.invokeBusy) { instance.invokeBusy(false); } if (instance.modified) { instance.modified(); } resolve(array); } const asyncType = array.dataType === 'string' && !options.compression ? 'string' : 'uint8array'; const asyncCallback = handlers[asyncType]( array, options.compression, doneCleanUp ); zipRoot.file(url).async(asyncType).then(asyncCallback); }); }, fetchJSON(instance = {}, url, options = {}) { const path = cleanUpPath(url); if (!ready) { vtkErrorMacro('ERROR!!! zip not ready...'); } if (options.compression) { if (options.compression === 'gz') { return zipRoot .file(path) .async('uint8array') .then((uint8array) => { const str = pako.inflate(uint8array, { to: 'string' }); return Promise.resolve(JSON.parse(str)); }); } return Promise.reject(new Error('Invalid compression')); } return zipRoot .file(path) .async('string') .then((str) => Promise.resolve(JSON.parse(str))); }, fetchText(instance = {}, url, options = {}) { const path = cleanUpPath(url); if (!ready) { vtkErrorMacro('ERROR!!! zip not ready...'); } if (options.compression) { if (options.compression === 'gz') { return zipRoot .file(path) .async('uint8array') .then((uint8array) => { const str = pako.inflate(uint8array, { to: 'string' }); return Promise.resolve(str); }); } return Promise.reject(new Error('Invalid compression')); } return zipRoot .file(path) .async('string') .then((str) => Promise.resolve(str)); }, fetchImage(instance = {}, url, options = {}) { const path = cleanUpPath(url); if (!ready) { vtkErrorMacro('ERROR!!! zip not ready...'); } return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = reject; zipRoot .file(path) .async('base64') .then((str) => { img.src = `data:image/${toMimeType(path)};base64,${str}`; }); }); }, fetchBinary(instance = {}, url, options = {}) { const path = cleanUpPath(url); if (!ready) { vtkErrorMacro('ERROR!!! zip not ready...'); } if (options.compression) { if (options.compression === 'gz') { return zipRoot.file(path).then((data) => { const array = pako.inflate(data).buffer; return Promise.resolve(array); }); } return Promise.reject(new Error('Invalid compression')); } return zipRoot .file(path) .async('arraybuffer') .then((data) => Promise.resolve(data)); }, }; } export default { create, };