UNPKG

@acransac/vtk.js

Version:

Visualization Toolkit for the Web

465 lines (407 loc) 13.8 kB
import { mat4, quat, vec3 } from 'gl-matrix'; import macro from 'vtk.js/Sources/macro'; import vtkBufferObject from 'vtk.js/Sources/Rendering/OpenGL/BufferObject'; import { ObjectType } from 'vtk.js/Sources/Rendering/OpenGL/BufferObject/Constants'; import { Representation } from 'vtk.js/Sources/Rendering/Core/Property/Constants'; const { vtkErrorMacro } = macro; // ---------------------------------------------------------------------------- // Static functions // ---------------------------------------------------------------------------- function computeInverseShiftAndScaleMatrix(coordShift, coordScale) { const inverseScale = vec3.create(); vec3.inverse(inverseScale, coordScale); const matrix = mat4.create(); mat4.fromRotationTranslationScale( matrix, quat.create(), coordShift, inverseScale ); return matrix; } 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) => { 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) { for (let i = 0; i < numPoints; ++i) { addAPoint(cellPts[offset + i]); } }, linesToWireframe(numPoints, cellPts, offset) { // for lines we add a bunch of segments for (let i = 0; i < numPoints - 1; ++i) { addAPoint(cellPts[offset + i]); addAPoint(cellPts[offset + i + 1]); } }, polysToWireframe(numPoints, cellPts, offset) { // 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]); addAPoint(cellPts[offset + ((i + 1) % numPoints)]); } } }, stripsToWireframe(numPoints, cellPts, offset) { 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]); addAPoint(cellPts[offset + i + 1]); } for (let i = 0; i < numPoints - 2; i++) { addAPoint(cellPts[offset + i]); addAPoint(cellPts[offset + i + 2]); } } }, polysToSurface(npts, cellPts, offset) { for (let i = 0; i < npts - 2; i++) { addAPoint(cellPts[offset + 0]); addAPoint(cellPts[offset + i + 1]); addAPoint(cellPts[offset + i + 2]); } }, stripsToSurface(npts, cellPts, offset) { for (let i = 0; i < npts - 2; i++) { addAPoint(cellPts[offset + i]); addAPoint(cellPts[offset + i + 1 + (i % 2)]); addAPoint(cellPts[offset + i + 1 + ((i + 1) % 2)]); } }, }; 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 // Compute squares of diagonal size and distance from the origin let diagSq = 0.0; let distSq = 0.0; for (let i = 0; i < 3; ++i) { const range = options.points.getRange(i); const delta = range[1] - range[0]; diagSq += delta * delta; const distShift = 0.5 * (range[1] + range[0]); distSq += distShift * distShift; } const useShiftAndScale = diagSq > 0 && (Math.abs(distSq) / diagSq > 1.0e6 || // If data is far from the origin relative to its size Math.abs(Math.log10(diagSq)) > 3.0 || // If the size is huge when not far from the origin (diagSq === 0 && distSq > 1.0e6)); // If data is a point, but far from the origin if (useShiftAndScale) { // Compute shift and scale vectors const coordShift = vec3.create(); const coordScale = vec3.create(); for (let i = 0; i < 3; ++i) { const range = options.points.getRange(i); const delta = range[1] - range[0]; coordShift[i] = 0.5 * (range[1] + range[0]); coordScale[i] = delta > 0 ? 1.0 / delta : 1.0; } publicAPI.setCoordShiftAndScale(coordShift, coordScale); } else if (model.coordShiftAndScaleEnabled === true) { // Make sure to reset publicAPI.setCoordShiftAndScale(null, null); } addAPoint = function addAPointFunc(i) { // Vertices pointIdx = i * 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 = i * 3; } packedVBO[vboidx++] = normalData[normalIdx++]; packedVBO[vboidx++] = normalData[normalIdx++]; packedVBO[vboidx++] = normalData[normalIdx++]; } model.customData.forEach((attr) => { custIdx = i * attr.components; for (let j = 0; j < attr.components; ++j) { packedVBO[vboidx++] = attr.data[custIdx++]; } }); if (tcoordData !== null) { tcoordIdx = i * 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 = i * colorComponents; } packedUCVBO[ucidx++] = colorData[colorIdx++]; packedUCVBO[ucidx++] = colorData[colorIdx++]; packedUCVBO[ucidx++] = colorData[colorIdx++]; packedUCVBO[ucidx++] = colorComponents === 4 ? colorData[colorIdx++] : 255; } }; for (let index = 0; index < size; ) { func(array[index], array, index + 1); index += array[index] + 1; cellCount++; } 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 !== Float32Array || coordShift.length !== 3) ) { vtkErrorMacro('Wrong type for coordShift, expected vec3 or null'); return; } if ( coordScale !== null && (coordScale.constructor !== Float32Array || 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, }; // ---------------------------------------------------------------------------- export 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); } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance(extend); // ---------------------------------------------------------------------------- export default { newInstance, extend };