import macro from 'vtk.js/Sources/macros'; import DataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper'; import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
import 'vtk.js/Sources/IO/Core/DataAccessHelper/LiteHttpDataAccessHelper';
const data = {};
function copyVector(src, srcOffset, dst, dstOffset, vectorSize) { for (let i = 0; i < vectorSize; i++) { dst[dstOffset + i] = src[srcOffset + i]; } }
function begin(splitMode) { data.splitOn = splitMode; data.pieces = []; data.v = []; data.vt = []; data.vn = []; data.f = [[]]; data.size = 0; }
function faceMap(str) { const idxs = str.split('/').map((i) => Number(i)); let vertexIdx = idxs[0] - 1; vertexIdx = vertexIdx < 0 ? vertexIdx + 1 + data.v.length / 3 : vertexIdx; let textCoordIdx = idxs[1] ? idxs[1] - 1 : vertexIdx; textCoordIdx = textCoordIdx < 0 ? textCoordIdx + 1 + data.vt.length / 2 : textCoordIdx; let vertexNormal = idxs[2] ? idxs[2] - 1 : vertexIdx; vertexNormal = vertexNormal < 0 ? vertexNormal + 1 + data.vn.length / 3 : vertexNormal; return [vertexIdx, textCoordIdx, vertexNormal]; }
function parseLine(line) { if (line[0] === '#') { return; } const tokens = line.split(/[ \t]+/); if (tokens[0] === data.splitOn) { tokens.shift(); data.pieces.push(tokens.join(' ').trim()); data.f.push([]); data.size++; } else if (tokens[0] === 'v') { data.v.push(Number(tokens[1])); data.v.push(Number(tokens[2])); data.v.push(Number(tokens[3])); } else if (tokens[0] === 'vt') { data.vt.push(Number(tokens[1])); data.vt.push(Number(tokens[2])); } else if (tokens[0] === 'vn') { data.vn.push(Number(tokens[1])); data.vn.push(Number(tokens[2])); data.vn.push(Number(tokens[3])); } else if (tokens[0] === 'f') { if (data.size === 0) { data.size++; } const cells = data.f[data.size - 1]; tokens.shift(); const faces = tokens.filter((s) => s.length > 0 && s !== '\r'); const size = faces.length; cells.push(size); for (let i = 0; i < size; i++) { cells.push(faceMap(faces[i])); } } }
function end(model) { const hasTcoords = !!data.vt.length; const hasNormals = !!data.vn.length; if (model.splitMode) { model.numberOfOutputs = data.size; for (let idx = 0; idx < data.size; idx++) { const polyIn = data.f[idx]; const nbElems = polyIn.length; const nbPoints = data.v.length / 3; const keyPointId = {}; let pointDuplicatesReferences;
if (model.trackDuplicates) { const pointKeys = []; let duplicatesCount = 0;
for (let offset = 0; offset < nbElems; ) { const cellSize = polyIn[offset++]; for (let pIdx = 0; pIdx < cellSize; pIdx++) { const [vIdx, tcIdx, nIdx] = polyIn[offset++]; const key = `${vIdx}/${tcIdx}/${nIdx}`; if (keyPointId[key] === undefined) { if (pointKeys[vIdx] === undefined) { pointKeys[vIdx] = [key]; } else { pointKeys[vIdx].push(key); ++duplicatesCount; } keyPointId[key] = vIdx; } } } pointDuplicatesReferences = new Uint16Array(nbPoints + duplicatesCount); let duplicates = 0; for (let pointId = 0; pointId < pointKeys.length; ++pointId) { const usageCount = pointKeys[pointId] ? pointKeys[pointId].length : 0; pointDuplicatesReferences[pointId] = usageCount > 1 ? nbPoints + duplicates : pointId; for (let duplicateId = 1; duplicateId < usageCount; ++duplicateId) { const finalDuplicateId = nbPoints + duplicates++; pointDuplicatesReferences[finalDuplicateId] = pointId; keyPointId[pointKeys[pointId][duplicateId]] = finalDuplicateId; } } }
const ctMapping = {}; const polydata = vtkPolyData.newInstance({ name: data.pieces[idx] }); const pts = []; const tc = []; const normals = []; const polys = [];
let offset = 0; while (offset < nbElems) { const cellSize = polyIn[offset]; polys.push(cellSize); for (let pIdx = 0; pIdx < cellSize; pIdx++) { const [vIdx, tcIdx, nIdx] = polyIn[offset + pIdx + 1]; const key = `${vIdx}/${tcIdx}/${nIdx}`; if (ctMapping[key] === undefined) { const dstOffset = model.trackDuplicates ? keyPointId[key] : pts.length / 3; ctMapping[key] = dstOffset; copyVector(data.v, vIdx * 3, pts, dstOffset * 3, 3); if (hasTcoords) { copyVector(data.vt, tcIdx * 2, tc, dstOffset * 2, 2); } if (hasNormals) { copyVector(data.vn, nIdx * 3, normals, dstOffset * 3, 3); } } polys.push(ctMapping[key]); } offset += cellSize + 1; }
polydata.getPoints().setData(Float32Array.from(pts), 3); if (model.trackDuplicates) { const duplicatesArray = vtkDataArray.newInstance({ name: 'Duplicates', values: pointDuplicatesReferences, }); polydata.getPointData().addArray(duplicatesArray); } polydata.getPolys().setData(Uint32Array.from(polys));
if (hasTcoords) { const tcoords = vtkDataArray.newInstance({ numberOfComponents: 2, values: Float32Array.from(tc), name: 'TextureCoordinates', }); polydata.getPointData().setTCoords(tcoords); }
if (hasNormals) { const normalsArray = vtkDataArray.newInstance({ numberOfComponents: 3, values: Float32Array.from(normals), name: 'Normals', }); polydata.getPointData().setNormals(normalsArray); }
model.output[idx] = polydata; } } else { model.numberOfOutputs = 1; const polydata = vtkPolyData.newInstance(); polydata.getPoints().setData(Float32Array.from(data.v), 3); if (hasTcoords && data.v.length / 3 === data.vt.length / 2) { const tcoords = vtkDataArray.newInstance({ numberOfComponents: 2, values: Float32Array.from(data.vt), name: 'TextureCoordinates', }); polydata.getPointData().setTCoords(tcoords); } if (hasNormals && data.v.length === data.vn.length) { const normalsArray = vtkDataArray.newInstance({ numberOfComponents: 3, values: Float32Array.from(data.vn), name: 'Normals', }); polydata.getPointData().setNormals(normalsArray); }
const polys = []; const polyIn = data.f[0]; const nbElems = polyIn.length; let offset = 0; while (offset < nbElems) { const cellSize = polyIn[offset]; polys.push(cellSize); for (let pIdx = 0; pIdx < cellSize; pIdx++) { const [vIdx] = polyIn[offset + pIdx + 1]; polys.push(vIdx); } offset += cellSize + 1; } polydata.getPolys().setData(Uint32Array.from(polys)); model.output[0] = polydata; } }
function getPointDuplicateIds(polyData, pointId) { const res = []; const duplicates = polyData.getPointData().getArrayByName('Duplicates'); if (duplicates == null) { return res; } const duplicatesData = duplicates.getData(); const originalPointId = Math.min(pointId, duplicatesData[pointId]); res.push(originalPointId); let duplicateId = duplicatesData[originalPointId]; if (duplicateId !== originalPointId) { while ( duplicateId < duplicatesData.length && duplicatesData[duplicateId] === originalPointId ) { res.push(duplicateId++); } } return res; }
export const STATIC = { getPointDuplicateIds, };
function vtkOBJReader(publicAPI, model) { model.classHierarchy.push('vtkOBJReader');
if (!model.dataAccessHelper) { model.dataAccessHelper = DataAccessHelper.get('http'); }
function fetchData(url, option = {}) { return model.dataAccessHelper.fetchText(publicAPI, url, option); }
publicAPI.setUrl = (url, option = {}) => { if (url.indexOf('.obj') === -1 && !option.fullpath) { model.baseURL = url; model.url = `${url}/index.obj`; } else { model.url = url;
const path = url.split('/'); path.pop(); model.baseURL = path.join('/'); }
return publicAPI.loadData(option); };
publicAPI.loadData = (option = {}) => fetchData(model.url, option).then((content) => publicAPI.isDeleted() ? false : publicAPI.parseAsText(content) );
publicAPI.parseAsText = (content) => { if (!content) { return true; } if (content !== model.parseData) { publicAPI.modified(); } model.parseData = content; model.numberOfOutputs = 0; begin(model.splitMode); content.split('\n').forEach(parseLine); end(model); return true; };
publicAPI.requestData = (inData, outData) => { publicAPI.parseAsText(model.parseData); };
publicAPI.isBusy = () => !!model.requestCount;
publicAPI.getNumberOfOutputPorts = () => model.numberOfOutputs; }
const DEFAULT_VALUES = { numberOfOutputs: 1, requestCount: 0, splitMode: null, trackDuplicates: false, };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
macro.obj(publicAPI, model); macro.get(publicAPI, model, ['url', 'baseURL']); macro.setGet(publicAPI, model, [ 'dataAccessHelper', 'splitMode', 'trackDuplicates', ]); macro.algo(publicAPI, model, 0, 1); macro.event(publicAPI, model, 'busy');
vtkOBJReader(publicAPI, model); }
export const newInstance = macro.newInstance(extend, 'vtkOBJReader');
export default { newInstance, extend, ...STATIC };
|