UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

301 lines (277 loc) 10.8 kB
import { m as macro } from '../../macros2.js'; import vtkActor from '../../Rendering/Core/Actor.js'; import vtkArrow2DSource from '../../Filters/Sources/Arrow2DSource.js'; import vtkGlyphRepresentation from './GlyphRepresentation.js'; import vtkMatrixBuilder from '../../Common/Core/MatrixBuilder.js'; import vtkPixelSpaceCallbackMapper from '../../Rendering/Core/PixelSpaceCallbackMapper.js'; import vtkConeSource from '../../Filters/Sources/ConeSource.js'; import vtkSphereSource from '../../Filters/Sources/SphereSource.js'; import vtkCircleSource from '../../Filters/Sources/CircleSource.js'; import vtkCubeSource from '../../Filters/Sources/CubeSource.js'; import vtkViewFinderSource from '../../Filters/Sources/ViewFinderSource.js'; import Constants from '../Widgets3D/LineWidget/Constants.js'; import { mat4, mat3, vec3 } from 'gl-matrix'; import { RenderingTypes } from '../Core/WidgetManager/Constants.js'; import { OrientationModes } from '../../Rendering/Core/Glyph3DMapper/Constants.js'; import { allocateArray } from './WidgetRepresentation.js'; const { ShapeType, Shapes2D, ShapesOrientable } = Constants; // ---------------------------------------------------------------------------- // vtkArrowHandleRepresentation methods // ---------------------------------------------------------------------------- function vtkArrowHandleRepresentation(publicAPI, model) { // Set our className model.classHierarchy.push('vtkArrowHandleRepresentation'); const superClass = { ...publicAPI }; // -------------------------------------------------------------------------- // Internal polydata dataset // -------------------------------------------------------------------------- /** * Set the shape for the glyph according to lineWidget state inputs */ function createGlyph(shape) { const representationToSource = { [ShapeType.STAR]: { builder: vtkArrow2DSource, initialValues: { shape: 'star', height: 0.6 } }, [ShapeType.ARROWHEAD3]: { builder: vtkArrow2DSource, initialValues: { shape: 'triangle' } }, [ShapeType.ARROWHEAD4]: { builder: vtkArrow2DSource, initialValues: { shape: 'arrow4points' } }, [ShapeType.ARROWHEAD6]: { builder: vtkArrow2DSource, initialValues: { shape: 'arrow6points' } }, [ShapeType.CONE]: { builder: vtkConeSource, initialValues: { direction: [0, 1, 0] } }, [ShapeType.SPHERE]: { builder: vtkSphereSource }, [ShapeType.CUBE]: { builder: vtkCubeSource, initialValues: { xLength: 0.8, yLength: 0.8, zLength: 0.8 } }, [ShapeType.DISK]: { builder: vtkCircleSource, initialValues: { resolution: 30, radius: 0.5, direction: [0, 0, 1], lines: false, face: true } }, [ShapeType.CIRCLE]: { builder: vtkCircleSource, initialValues: { resolution: 30, radius: 0.5, direction: [0, 0, 1], lines: true, face: false } }, [ShapeType.VIEWFINDER]: { builder: vtkViewFinderSource, initialValues: { radius: 0.1, spacing: 0.3, width: 1.4 } }, [ShapeType.NONE]: { builder: vtkSphereSource } }; const rep = representationToSource[shape]; return rep.builder.newInstance(rep.initialValues); } // -------------------------------------------------------------------------- // Generic rendering pipeline // -------------------------------------------------------------------------- // displayActors and displayMappers are used to render objects in HTML, // allowing objects to be 'rendered' internally in a VTK scene without // being visible on the final output. model.displayMapper = vtkPixelSpaceCallbackMapper.newInstance(); model.displayActor = vtkActor.newInstance({ parentProp: publicAPI }); // model.displayActor.getProperty().setOpacity(0); // don't show in 3D model.displayActor.setMapper(model.displayMapper); model.displayMapper.setInputConnection(publicAPI.getOutputPort()); publicAPI.addActor(model.displayActor); model.alwaysVisibleActors = [model.displayActor]; // -------------------------------------------------------------------------- const superSetGlyphResolution = publicAPI.setGlyphResolution; publicAPI.setGlyphResolution = r => { const res = superSetGlyphResolution(r); model._pipeline.glyph.setPhiResolution(r); model._pipeline.glyph.setThetaResolution(r); return res; }; // -------------------------------------------------------------------------- function callbackProxy(coords) { if (model.displayCallback) { const filteredList = []; const states = publicAPI.getRepresentationStates(); for (let i = 0; i < states.length; i++) { if (states[i].getActive()) { filteredList.push(coords[i]); } } if (filteredList.length) { model.displayCallback(filteredList); return; } } model.displayCallback(); } publicAPI.setDisplayCallback = callback => { model.displayCallback = callback; model.displayMapper.setCallback(callback ? callbackProxy : null); }; // -------------------------------------------------------------------------- publicAPI.is2DShape = () => Shapes2D.includes(model.shape); publicAPI.isOrientableShape = () => ShapesOrientable.includes(model.shape); /** * Returns the orientation matrix to align glyph on model.orientation. * */ function getOrientationRotation(viewMatrixInv) { const displayOrientation = new Float64Array(3); const baseDir = [0, 1, 0]; vec3.transformMat3(displayOrientation, model.orientation, viewMatrixInv); displayOrientation[2] = 0; const displayMatrix = vtkMatrixBuilder.buildFromDegree().rotateFromDirections(baseDir, displayOrientation).getMatrix(); const displayRotation = new Float64Array(9); mat3.fromMat4(displayRotation, displayMatrix); return displayRotation; } function getCameraFacingRotation(scale3, displayRotation, viewMatrix) { const rotation = new Float64Array(9); mat3.multiply(rotation, viewMatrix, displayRotation); vec3.transformMat3(scale3, scale3, rotation); return rotation; } /** * Computes the rotation matrix of the glyph. There are 2 rotations: * - a first rotation to be oriented along model.rotation * - an optional second rotation to face the camera * @param {vec3} scale3 Scale of the glyph, rotated when glyph is rotated. */ function getGlyphRotation(scale3) { const shouldFaceCamera = model.faceCamera === true || model.faceCamera == null && publicAPI.is2DShape(); const viewMatrix = new Float64Array(9); mat3.fromMat4(viewMatrix, model.viewMatrix); const viewMatrixInv = mat3.identity(new Float64Array(9)); if (shouldFaceCamera) { mat3.invert(viewMatrixInv, viewMatrix); } let orientationRotation = null; if (publicAPI.isOrientableShape()) { orientationRotation = getOrientationRotation(viewMatrixInv); } else { orientationRotation = mat3.identity(new Float64Array(9)); } if (shouldFaceCamera) { orientationRotation = getCameraFacingRotation(scale3, orientationRotation, viewMatrix); } return orientationRotation; } function applyOrientation(polyData, states) { model._pipeline.mapper.setOrientationArray('orientation'); model._pipeline.mapper.setOrientationMode(OrientationModes.MATRIX); const orientation = allocateArray(polyData, 'orientation', states.length, 'Float32Array', 9).getData(); const defaultScale3 = [1, 1, 1]; for (let i = 0; i < states.length; ++i) { const scale3 = states[i].getScale3?.() ?? defaultScale3; const rotation = getGlyphRotation(scale3); orientation.set(rotation, 9 * i); } } publicAPI.setDirection(applyOrientation); publicAPI.setNoOrientation(applyOrientation); publicAPI.requestData = (inData, outData) => { // FIXME: shape should NOT be mixin, but a representation property. const shape = publicAPI.getRepresentationStates(inData[0])[0]?.getShape(); let shouldCreateGlyph = model._pipeline.glyph == null; if (model.shape !== shape && Object.values(ShapeType).includes(shape)) { model.shape = shape; shouldCreateGlyph = true; } if (shouldCreateGlyph && model.shape) { model._pipeline.glyph = createGlyph(model.shape); model._pipeline.mapper.setInputConnection(model._pipeline.glyph.getOutputPort(), 1); } return superClass.requestData(inData, outData); }; publicAPI.updateActorVisibility = (renderingType = RenderingTypes.FRONT_BUFFER, ctxVisible = true, handleVisible = true) => { const hasValidState = publicAPI.getRepresentationStates().length > 0; superClass.updateActorVisibility(renderingType, ctxVisible, handleVisible && hasValidState); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- /** * 'shape' default value is used first time 'shape' mixin is invalid. * 'faceCamera' controls wether the glyph should face camera or not: * - null or undefined to leave it to shape type (i.e. 2D are facing camera, * 3D are not) * - true to face camera * - false to not face camera */ function defaultValues(initialValues) { return { faceCamera: null, orientation: [1, 0, 0], shape: ShapeType.SPHERE, viewMatrix: mat4.identity(new Float64Array(16)), ...initialValues }; } // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, defaultValues(initialValues)); vtkGlyphRepresentation.extend(publicAPI, model, initialValues); macro.setGetArray(publicAPI, model, ['visibilityFlagArray'], 2); macro.setGetArray(publicAPI, model, ['orientation'], 3); macro.setGetArray(publicAPI, model, ['viewMatrix'], 16); macro.setGet(publicAPI, model, ['faceCamera']); // Object specific methods vtkArrowHandleRepresentation(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkArrowHandleRepresentation'); // ---------------------------------------------------------------------------- var vtkArrowHandleRepresentation$1 = { newInstance, extend }; export { vtkArrowHandleRepresentation$1 as default, extend, newInstance };