UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

384 lines (360 loc) 13.9 kB
import { vec3 } from 'gl-matrix'; import { m as macro } from '../../macros2.js'; import vtkBufferObject from './BufferObject.js'; import { ObjectType } from './BufferObject/Constants.js'; import { Representation } from '../Core/Property/Constants.js'; import { computeCoordShiftAndScale, computeInverseShiftAndScaleMatrix } from './CellArrayBufferObject/helpers.js'; const { vtkErrorMacro } = macro; // ---------------------------------------------------------------------------- // Static functions // ---------------------------------------------------------------------------- function shouldApplyCoordShiftAndScale(coordShift, coordScale) { if (coordShift === null || coordScale === null) { return false; } return !(vec3.exactEquals(coordShift, [0, 0, 0]) && vec3.exactEquals(coordScale, [1, 1, 1])); } // ---------------------------------------------------------------------------- // vtkOpenGLCellArrayBufferObject methods // ---------------------------------------------------------------------------- function vtkOpenGLCellArrayBufferObject(publicAPI, model) { // Set our className model.classHierarchy.push('vtkOpenGLCellArrayBufferObject'); publicAPI.setType(ObjectType.ARRAY_BUFFER); publicAPI.createVBO = (cellArray, inRep, outRep, options, selectionMaps = null) => { if (!cellArray.getData() || !cellArray.getData().length) { model.elementCount = 0; return 0; } // Figure out how big each block will be, currently 6 or 7 floats. model.blockSize = 3; model.vertexOffset = 0; model.normalOffset = 0; model.tCoordOffset = 0; model.tCoordComponents = 0; model.colorComponents = 0; model.colorOffset = 0; model.customData = []; const pointData = options.points.getData(); let normalData = null; let tcoordData = null; let colorData = null; const colorComponents = options.colors ? options.colors.getNumberOfComponents() : 0; const textureComponents = options.tcoords ? options.tcoords.getNumberOfComponents() : 0; // the values of 4 below are because floats are 4 bytes if (options.normals) { model.normalOffset = 4 * model.blockSize; model.blockSize += 3; normalData = options.normals.getData(); } if (options.customAttributes) { options.customAttributes.forEach(a => { if (a) { model.customData.push({ data: a.getData(), offset: 4 * model.blockSize, components: a.getNumberOfComponents(), name: a.getName() }); model.blockSize += a.getNumberOfComponents(); } }); } if (options.tcoords) { model.tCoordOffset = 4 * model.blockSize; model.tCoordComponents = textureComponents; model.blockSize += textureComponents; tcoordData = options.tcoords.getData(); } if (options.colors) { model.colorComponents = options.colors.getNumberOfComponents(); model.colorOffset = 0; colorData = options.colors.getData(); if (!model.colorBO) { model.colorBO = vtkBufferObject.newInstance(); } model.colorBO.setOpenGLRenderWindow(model._openGLRenderWindow); } else { model.colorBO = null; } model.stride = 4 * model.blockSize; let pointIdx = 0; let normalIdx = 0; let tcoordIdx = 0; let colorIdx = 0; let custIdx = 0; let cellCount = 0; let addAPoint; const cellBuilders = { // easy, every input point becomes an output point anythingToPoints(numPoints, cellPts, offset, cellId) { for (let i = 0; i < numPoints; ++i) { addAPoint(cellPts[offset + i], cellId); } }, linesToWireframe(numPoints, cellPts, offset, cellIdx) { // for lines we add a bunch of segments for (let i = 0; i < numPoints - 1; ++i) { addAPoint(cellPts[offset + i], cellIdx); addAPoint(cellPts[offset + i + 1], cellIdx); } }, polysToWireframe(numPoints, cellPts, offset, cellIdx) { // for polys we add a bunch of segments and close it if (numPoints > 2) { for (let i = 0; i < numPoints; ++i) { addAPoint(cellPts[offset + i], cellIdx); addAPoint(cellPts[offset + (i + 1) % numPoints], cellIdx); } } }, stripsToWireframe(numPoints, cellPts, offset, cellIdx) { if (numPoints > 2) { // for strips we add a bunch of segments and close it for (let i = 0; i < numPoints - 1; ++i) { addAPoint(cellPts[offset + i], cellIdx); addAPoint(cellPts[offset + i + 1], cellIdx); } for (let i = 0; i < numPoints - 2; i++) { addAPoint(cellPts[offset + i], cellIdx); addAPoint(cellPts[offset + i + 2], cellIdx); } } }, polysToSurface(npts, cellPts, offset, cellIdx) { for (let i = 0; i < npts - 2; i++) { addAPoint(cellPts[offset + 0], cellIdx); addAPoint(cellPts[offset + i + 1], cellIdx); addAPoint(cellPts[offset + i + 2], cellIdx); } }, stripsToSurface(npts, cellPts, offset, cellIdx) { for (let i = 0; i < npts - 2; i++) { addAPoint(cellPts[offset + i], cellIdx); addAPoint(cellPts[offset + i + 1 + i % 2], cellIdx); addAPoint(cellPts[offset + i + 1 + (i + 1) % 2], cellIdx); } } }; const cellCounters = { // easy, every input point becomes an output point anythingToPoints(numPoints, cellPts) { return numPoints; }, linesToWireframe(numPoints, cellPts) { if (numPoints > 1) { return (numPoints - 1) * 2; } return 0; }, polysToWireframe(numPoints, cellPts) { if (numPoints > 2) { return numPoints * 2; } return 0; }, stripsToWireframe(numPoints, cellPts) { if (numPoints > 2) { return numPoints * 4 - 6; } return 0; }, polysToSurface(npts, cellPts) { if (npts > 2) { return (npts - 2) * 3; } return 0; }, stripsToSurface(npts, cellPts, offset) { if (npts > 2) { return (npts - 2) * 3; } return 0; } }; let func = null; let countFunc = null; if (outRep === Representation.POINTS || inRep === 'verts') { func = cellBuilders.anythingToPoints; countFunc = cellCounters.anythingToPoints; } else if (outRep === Representation.WIREFRAME || inRep === 'lines') { func = cellBuilders[`${inRep}ToWireframe`]; countFunc = cellCounters[`${inRep}ToWireframe`]; } else { func = cellBuilders[`${inRep}ToSurface`]; countFunc = cellCounters[`${inRep}ToSurface`]; } const array = cellArray.getData(); const size = array.length; let caboCount = 0; for (let index = 0; index < size;) { caboCount += countFunc(array[index], array); index += array[index] + 1; } let packedUCVBO = null; const packedVBO = new Float32Array(caboCount * model.blockSize); if (colorData) { packedUCVBO = new Uint8Array(caboCount * 4); } let vboidx = 0; let ucidx = 0; // Find out if shift scale should be used const { useShiftAndScale, coordShift, coordScale } = computeCoordShiftAndScale(options.points); if (useShiftAndScale) { publicAPI.setCoordShiftAndScale(coordShift, coordScale); } else if (model.coordShiftAndScaleEnabled === true) { // Make sure to reset publicAPI.setCoordShiftAndScale(null, null); } // Initialize the structures used to keep track of point ids and cell ids for selectors if (selectionMaps) { if (!selectionMaps.points && !selectionMaps.cells) { selectionMaps.points = new Int32Array(caboCount); selectionMaps.cells = new Int32Array(caboCount); } else { const newPoints = new Int32Array(caboCount + selectionMaps.points.length); newPoints.set(selectionMaps.points); selectionMaps.points = newPoints; const newCells = new Int32Array(caboCount + selectionMaps.cells.length); newCells.set(selectionMaps.cells); selectionMaps.cells = newCells; } } let pointCount = options.vertexOffset; addAPoint = function addAPointFunc(pointId, cellId) { // Keep track of original point and cell ids, for selection if (selectionMaps) { selectionMaps.points[pointCount] = pointId; selectionMaps.cells[pointCount] = cellCount + options.cellOffset; } ++pointCount; // Vertices pointIdx = pointId * 3; if (!model.coordShiftAndScaleEnabled) { packedVBO[vboidx++] = pointData[pointIdx++]; packedVBO[vboidx++] = pointData[pointIdx++]; packedVBO[vboidx++] = pointData[pointIdx++]; } else { // Apply shift and scale packedVBO[vboidx++] = (pointData[pointIdx++] - model.coordShift[0]) * model.coordScale[0]; packedVBO[vboidx++] = (pointData[pointIdx++] - model.coordShift[1]) * model.coordScale[1]; packedVBO[vboidx++] = (pointData[pointIdx++] - model.coordShift[2]) * model.coordScale[2]; } if (normalData !== null) { if (options.haveCellNormals) { normalIdx = (cellCount + options.cellOffset) * 3; } else { normalIdx = pointId * 3; } packedVBO[vboidx++] = normalData[normalIdx++]; packedVBO[vboidx++] = normalData[normalIdx++]; packedVBO[vboidx++] = normalData[normalIdx++]; } model.customData.forEach(attr => { custIdx = pointId * attr.components; for (let j = 0; j < attr.components; ++j) { packedVBO[vboidx++] = attr.data[custIdx++]; } }); if (tcoordData !== null) { if (options.useTCoordsPerCell) { tcoordIdx = cellId * textureComponents; } else { tcoordIdx = pointId * textureComponents; } for (let j = 0; j < textureComponents; ++j) { packedVBO[vboidx++] = tcoordData[tcoordIdx++]; } } if (colorData !== null) { if (options.haveCellScalars) { colorIdx = (cellCount + options.cellOffset) * colorComponents; } else { colorIdx = pointId * colorComponents; } packedUCVBO[ucidx++] = colorData[colorIdx++]; packedUCVBO[ucidx++] = colorData[colorIdx++]; packedUCVBO[ucidx++] = colorData[colorIdx++]; packedUCVBO[ucidx++] = colorComponents === 4 ? colorData[colorIdx++] : 255; } }; // Browse the cell array: the index is at the beginning of a cell // The value of 'array' at the position 'index' is the number of points in the cell for (let index = 0; index < size; index += array[index] + 1, cellCount++) { func(array[index], array, index + 1, cellCount + options.cellOffset); } model.elementCount = caboCount; publicAPI.upload(packedVBO, ObjectType.ARRAY_BUFFER); if (model.colorBO) { model.colorBOStride = 4; model.colorBO.upload(packedUCVBO, ObjectType.ARRAY_BUFFER); } return cellCount; }; publicAPI.setCoordShiftAndScale = (coordShift, coordScale) => { if (coordShift !== null && (coordShift.constructor !== Float64Array || coordShift.length !== 3)) { vtkErrorMacro('Wrong type for coordShift, expected vec3 or null'); return; } if (coordScale !== null && (coordScale.constructor !== Float64Array || coordScale.length !== 3)) { vtkErrorMacro('Wrong type for coordScale, expected vec3 or null'); return; } if (model.coordShift === null || coordShift === null || !vec3.equals(coordShift, model.coordShift)) { model.coordShift = coordShift; } if (model.coordScale === null || coordScale === null || !vec3.equals(coordScale, model.coordScale)) { model.coordScale = coordScale; } model.coordShiftAndScaleEnabled = shouldApplyCoordShiftAndScale(model.coordShift, model.coordScale); if (model.coordShiftAndScaleEnabled) { model.inverseShiftAndScaleMatrix = computeInverseShiftAndScaleMatrix(model.coordShift, model.coordScale); } else { model.inverseShiftAndScaleMatrix = null; } }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { elementCount: 0, stride: 0, colorBOStride: 0, vertexOffset: 0, normalOffset: 0, tCoordOffset: 0, tCoordComponents: 0, colorOffset: 0, colorComponents: 0, tcoordBO: null, customData: [], coordShift: null, coordScale: null, coordShiftAndScaleEnabled: false, inverseShiftAndScaleMatrix: null }; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkBufferObject.extend(publicAPI, model, initialValues); macro.setGet(publicAPI, model, ['colorBO', 'elementCount', 'stride', 'colorBOStride', 'vertexOffset', 'normalOffset', 'tCoordOffset', 'tCoordComponents', 'colorOffset', 'colorComponents', 'customData']); macro.get(publicAPI, model, ['coordShift', 'coordScale', 'coordShiftAndScaleEnabled', 'inverseShiftAndScaleMatrix']); // Object specific methods vtkOpenGLCellArrayBufferObject(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend); // ---------------------------------------------------------------------------- var vtkCellArrayBufferObject = { newInstance, extend }; export { vtkCellArrayBufferObject as default, extend, newInstance };