import macro from 'vtk.js/Sources/macros'; import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; import vtkArrow2DSource from 'vtk.js/Sources/Filters/Sources/Arrow2DSource/'; import vtkGlyphRepresentation from 'vtk.js/Sources/Widgets/Representations/GlyphRepresentation'; import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'; import vtkPixelSpaceCallbackMapper from 'vtk.js/Sources/Rendering/Core/PixelSpaceCallbackMapper'; import vtkConeSource from 'vtk.js/Sources/Filters/Sources/ConeSource'; import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource'; import vtkCircleSource from 'vtk.js/Sources/Filters/Sources/CircleSource'; import vtkCubeSource from 'vtk.js/Sources/Filters/Sources/CubeSource'; import vtkViewFinderSource from 'vtk.js/Sources/Filters/Sources/ViewFinderSource';
import Constants from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget/Constants'; import { vec3, mat3, mat4 } from 'gl-matrix'; import { RenderingTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants'; import { OrientationModes } from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper/Constants'; import { allocateArray } from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation';
const { ShapeType, Shapes2D, ShapesOrientable } = Constants;
function vtkArrowHandleRepresentation(publicAPI, model) { model.classHierarchy.push('vtkArrowHandleRepresentation');
const superClass = { ...publicAPI };
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); }
model.displayMapper = vtkPixelSpaceCallbackMapper.newInstance(); model.displayActor = vtkActor.newInstance({ parentProp: publicAPI }); model.displayActor.setMapper(model.displayMapper); model.displayMapper.setInputConnection(publicAPI.getOutputPort()); publicAPI.addActor(model.displayActor);
model.alwaysVisibleActors = [model.displayActor];
publicAPI.setGlyphResolution = macro.chain( publicAPI.setGlyphResolution, (r) => model._pipeline.glyph.setPhiResolution(r), (r) => model._pipeline.glyph.setThetaResolution(r) );
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);
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; }
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) => { 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 ); }; }
function defaultValues(initialValues) { return { faceCamera: null, orientation: [1, 0, 0], shape: ShapeType.SPHERE, viewMatrix: mat4.identity(new Float64Array(16)), ...initialValues, }; }
export 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']); vtkArrowHandleRepresentation(publicAPI, model); }
export const newInstance = macro.newInstance( extend, 'vtkArrowHandleRepresentation' );
export default { newInstance, extend };
|