UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

359 lines (281 loc) 11.2 kB
import BinaryHelper from '../Core/BinaryHelper.js'; import DataAccessHelper from '../Core/DataAccessHelper.js'; import macro from '../../macros.js'; import vtkDataArray from '../../Common/Core/DataArray.js'; import vtkMatrixBuilder from '../../Common/Core/MatrixBuilder.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js'; // import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + zip // import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper'; // html + base64 + zip // import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; // zip var vtkErrorMacro = macro.vtkErrorMacro; function parseHeader(headerString) { var headerSubStr = headerString.split(' '); var fieldValues = headerSubStr.filter(function (e) { return e.indexOf('=') > -1; }); var header = {}; for (var i = 0; i < fieldValues.length; ++i) { var fieldValueStr = fieldValues[i]; var fieldValueSubStr = fieldValueStr.split('='); if (fieldValueSubStr.length === 2) { header[fieldValueSubStr[0]] = fieldValueSubStr[1]; } } return header; } function addValuesToArray(src, dst) { for (var i = 0; i < src.length; i++) { dst.push(src[i]); } } // facet normal ni nj nk // outer loop // vertex v1x v1y v1z // vertex v2x v2y v2z // vertex v3x v3y v3z // endloop // endfacet function readTriangle(lines, offset, points, cellArray, cellNormals) { var normalLine = lines[offset]; if (normalLine === undefined) { return -1; } if (normalLine.indexOf('endfacet') !== -1) { return offset + 1; } if (normalLine.indexOf('facet') === -1) { return offset + 1; // Move to next line } var nbVertex = 0; var nbConsumedLines = 2; var firstVertexIndex = points.length / 3; var normal = normalLine.split(/[ \t]+/).filter(function (i) { return i; }).slice(-3).map(Number); addValuesToArray(normal, cellNormals); while (lines[offset + nbConsumedLines].indexOf('vertex') !== -1) { var line = lines[offset + nbConsumedLines]; var coords = line.split(/[ \t]+/).filter(function (i) { return i; }).slice(-3).map(Number); addValuesToArray(coords, points); nbVertex++; nbConsumedLines++; } cellArray.push(nbVertex); for (var i = 0; i < nbVertex; i++) { cellArray.push(firstVertexIndex + i); } while (lines[offset + nbConsumedLines] && lines[offset + nbConsumedLines].indexOf('endfacet') !== -1) { nbConsumedLines++; } // +1 (endfacet) +1 (next facet) return offset + nbConsumedLines + 2; } // ---------------------------------------------------------------------------- // vtkSTLReader methods // ---------------------------------------------------------------------------- function vtkSTLReader(publicAPI, model) { // Set our className model.classHierarchy.push('vtkSTLReader'); // Create default dataAccessHelper if not available if (!model.dataAccessHelper) { model.dataAccessHelper = DataAccessHelper.get('http'); } // Internal method to fetch Array function fetchData(url) { var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var compression = option.compression !== undefined ? option.compression : model.compression; var progressCallback = option.progressCallback !== undefined ? option.progressCallback : model.progressCallback; if (option.binary) { return model.dataAccessHelper.fetchBinary(url, { compression: compression, progressCallback: progressCallback }); } return model.dataAccessHelper.fetchText(publicAPI, url, { compression: compression, progressCallback: progressCallback }); } // Set DataSet url publicAPI.setUrl = function (url) { var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { binary: true }; model.url = url; // Remove the file in the URL var path = url.split('/'); path.pop(); model.baseURL = path.join('/'); // Fetch metadata return publicAPI.loadData(option); }; // Fetch the actual data arrays publicAPI.loadData = function () { var option = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var promise = fetchData(model.url, option); promise.then(publicAPI.parse); return promise; }; publicAPI.parse = function (content) { if (typeof content === 'string') { publicAPI.parseAsText(content); } else { publicAPI.parseAsArrayBuffer(content); } }; publicAPI.parseAsArrayBuffer = function (content) { if (!content) { return; } if (content !== model.parseData) { publicAPI.modified(); } else { return; } model.parseData = content; // ascii/binary detection var isBinary = false; // 80=STL header, 4=uint32 of num of triangles (le) var dview = new DataView(content, 0, 80 + 4); var numTriangles = dview.getUint32(80, true); // 50 bytes per triangle isBinary = 84 + numTriangles * 50 === content.byteLength; // Check if ascii format if (!isBinary) { publicAPI.parseAsText(BinaryHelper.arrayBufferToString(content)); return; } // Binary parsing // Header var headerData = content.slice(0, 80); var headerStr = BinaryHelper.arrayBufferToString(headerData); var header = parseHeader(headerStr); // Data var dataView = new DataView(content, 84); // global.dataview = dataView; var nbFaces = (content.byteLength - 84) / 50; var pointValues = new Float32Array(nbFaces * 9); var normalValues = new Float32Array(nbFaces * 3); var cellValues = new Uint32Array(nbFaces * 4); var cellDataValues = new Uint16Array(nbFaces); var cellOffset = 0; for (var faceIdx = 0; faceIdx < nbFaces; faceIdx++) { var offset = faceIdx * 50; normalValues[faceIdx * 3 + 0] = dataView.getFloat32(offset + 0, true); normalValues[faceIdx * 3 + 1] = dataView.getFloat32(offset + 4, true); normalValues[faceIdx * 3 + 2] = dataView.getFloat32(offset + 8, true); pointValues[faceIdx * 9 + 0] = dataView.getFloat32(offset + 12, true); pointValues[faceIdx * 9 + 1] = dataView.getFloat32(offset + 16, true); pointValues[faceIdx * 9 + 2] = dataView.getFloat32(offset + 20, true); pointValues[faceIdx * 9 + 3] = dataView.getFloat32(offset + 24, true); pointValues[faceIdx * 9 + 4] = dataView.getFloat32(offset + 28, true); pointValues[faceIdx * 9 + 5] = dataView.getFloat32(offset + 32, true); pointValues[faceIdx * 9 + 6] = dataView.getFloat32(offset + 36, true); pointValues[faceIdx * 9 + 7] = dataView.getFloat32(offset + 40, true); pointValues[faceIdx * 9 + 8] = dataView.getFloat32(offset + 44, true); cellValues[cellOffset++] = 3; cellValues[cellOffset++] = faceIdx * 3 + 0; cellValues[cellOffset++] = faceIdx * 3 + 1; cellValues[cellOffset++] = faceIdx * 3 + 2; cellDataValues[faceIdx] = dataView.getUint16(offset + 48, true); } // Rotate points var orientationField = 'SPACE'; if (orientationField in header && header[orientationField] !== 'LPS') { var XYZ = header[orientationField]; var mat4 = new Float32Array(16); mat4[15] = 1; switch (XYZ[0]) { case 'L': mat4[0] = 1; break; case 'R': mat4[0] = -1; break; default: vtkErrorMacro("Can not convert STL file from ".concat(XYZ, " to LPS space: ") + "permutations not supported. Use itk.js STL reader instead."); return; } switch (XYZ[1]) { case 'P': mat4[5] = 1; break; case 'A': mat4[5] = -1; break; default: vtkErrorMacro("Can not convert STL file from ".concat(XYZ, " to LPS space: ") + "permutations not supported. Use itk.js STL reader instead."); return; } switch (XYZ[2]) { case 'S': mat4[10] = 1; break; case 'I': mat4[10] = -1; break; default: vtkErrorMacro("Can not convert STL file from ".concat(XYZ, " to LPS space: ") + "permutations not supported. Use itk.js STL reader instead."); return; } vtkMatrixBuilder.buildFromDegree().setMatrix(mat4).apply(pointValues).apply(normalValues); } var polydata = vtkPolyData.newInstance(); polydata.getPoints().setData(pointValues, 3); polydata.getPolys().setData(cellValues); polydata.getCellData().setScalars(vtkDataArray.newInstance({ name: 'Attribute', values: cellDataValues })); polydata.getCellData().setNormals(vtkDataArray.newInstance({ name: 'Normals', values: normalValues, numberOfComponents: 3 })); // Add new output model.output[0] = polydata; }; publicAPI.parseAsText = function (content) { if (!content) { return; } if (content !== model.parseData) { publicAPI.modified(); } else { return; } model.parseData = content; var lines = content.split('\n'); var offset = 1; var points = []; var cellArray = []; var cellNormals = []; while (offset !== -1) { offset = readTriangle(lines, offset, points, cellArray, cellNormals); } var polydata = vtkPolyData.newInstance(); polydata.getPoints().setData(Float32Array.from(points), 3); polydata.getPolys().setData(Uint32Array.from(cellArray)); polydata.getCellData().setNormals(vtkDataArray.newInstance({ name: 'Normals', values: Float32Array.from(cellNormals), numberOfComponents: 3 })); // Add new output model.output[0] = polydata; }; publicAPI.requestData = function (inData, outData) { publicAPI.parse(model.parseData); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = {// baseURL: null, // dataAccessHelper: null, // url: null, }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API macro.obj(publicAPI, model); macro.get(publicAPI, model, ['url', 'baseURL']); macro.setGet(publicAPI, model, ['dataAccessHelper']); macro.algo(publicAPI, model, 0, 1); // vtkSTLReader methods vtkSTLReader(publicAPI, model); // To support destructuring if (!model.compression) { model.compression = null; } if (!model.progressCallback) { model.progressCallback = null; } } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkSTLReader'); // ---------------------------------------------------------------------------- var vtkSTLReader$1 = { extend: extend, newInstance: newInstance }; export { vtkSTLReader$1 as default, extend, newInstance };