import macro from 'vtk.js/Sources/macros'; import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; import vtkGlyph3DMapper from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper'; import vtkHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/HandleRepresentation'; import vtkContextRepresentation from 'vtk.js/Sources/Widgets/Representations/ContextRepresentation'; import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
import { ScalarMode } from 'vtk.js/Sources/Rendering/Core/Mapper/Constants'; import { getPixelWorldHeightAtCoord } from 'vtk.js/Sources/Widgets/Core/WidgetManager';
import vtkWidgetRepresentation, { allocateArray, } from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation'; import { Behavior } from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation/Constants'; import { OrientationModes } from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper/Constants';
export function origin(publicAPI, model) { return (polyData, states) => { const points = allocateArray(polyData, 'points', states.length).getData(); let j = 0; for (let i = 0; i < states.length; ++i) { const coord = states[i].getOrigin( model.scaleInPixels && model.displayScaleParams ); points[j++] = coord[0]; points[j++] = coord[1]; points[j++] = coord[2]; } }; } export function noPosition(publicAPI, model) { return (polyData, states) => { allocateArray(polyData, 'points', 0); }; } export function color3(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setColorByArrayName('color'); const colorArray = allocateArray( polyData, 'color', states.length, 'Uint8Array', 4 ); const colors = colorArray.getData(); let j = 0; for (let i = 0; i < states.length; ++i) { let c3 = states[i].getColor3(); if (states[i].getActive() && model.useActiveColor) { c3 = model.activeColor; } colors[j++] = c3[0]; colors[j++] = c3[1]; colors[j++] = c3[2]; colors[j++] = states[i].getOpacity(); } colorArray.dataChange(); }; } export function color(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setColorByArrayName('color'); const colors = allocateArray(polyData, 'color', states.length).getData(); for (let i = 0; i < states.length; ++i) { let c = states[i].getColor(); if (states[i].getActive() && model.useActiveColor) { c = model.activeColor; } colors[i] = c; } }; } export function noColor(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setColorByArrayName(null); }; } export function scale3(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setScaleArray('scale'); model._pipeline.mapper.setScaleFactor(1); model._pipeline.mapper.setScaling(true); model._pipeline.mapper.setScaleMode( vtkGlyph3DMapper.ScaleModes.SCALE_BY_COMPONENTS ); const scales = allocateArray( polyData, 'scale', states.length, 'Float32Array', 3 ).getData(); let j = 0; for (let i = 0; i < states.length; ++i) { const state = states[i]; let scaleFactor = state.getActive() ? model.activeScaleFactor : 1; if (publicAPI.getScaleInPixels()) { scaleFactor *= getPixelWorldHeightAtCoord( state.getOrigin(), model.displayScaleParams ); } const scale = state.getScale3?.() ?? model.defaultScale; scales[j++] = scaleFactor * scale[0]; scales[j++] = scaleFactor * scale[1]; scales[j++] = scaleFactor * scale[2]; } }; } export function scale1(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setScaleArray('scale'); model._pipeline.mapper.setScaleFactor(1); model._pipeline.mapper.setScaling(true); const scales = allocateArray(polyData, 'scale', states.length).getData(); for (let i = 0; i < states.length; ++i) { const state = states[i]; let scaleFactor = state.getActive() ? model.activeScaleFactor : 1; if (publicAPI.getScaleInPixels()) { scaleFactor *= getPixelWorldHeightAtCoord( state.getOrigin(), model.displayScaleParams ); } const scale = state.getScale1?.() ?? model.defaultScale; scales[i] = scaleFactor * scale; } }; } export function noScale(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setScaleArray(null); model._pipeline.mapper.setScaleFactor(model.defaultScale); model._pipeline.mapper.setScaling(model.defaultScale !== 1); }; } export function direction(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setOrientationArray('orientation'); model._pipeline.mapper.setOrientationMode(OrientationModes.MATRIX); const orientation = allocateArray( polyData, 'orientation', states.length, 'Float64Array', 9 ).getData(); for (let i = 0; i < states.length; ++i) { const state = states[i]; const right = state.getRight ? state.getRight() : [1, 0, 0]; const up = state.getUp ? state.getUp() : [0, 1, 0]; const dir = state.getDirection ? state.getDirection() : [0, 0, 1]; orientation.set(right, 9 * i); orientation.set(up, 9 * i + 3); orientation.set(dir, 9 * i + 6); } }; } export function noOrientation(publicAPI, model) { return (polyData, states) => { model._pipeline.mapper.setOrientationArray(null); }; }
function vtkGlyphRepresentation(publicAPI, model) { model.classHierarchy.push('vtkGlyphRepresentation'); const superClass = { ...publicAPI }; const internalPolyData = vtkPolyData.newInstance({ mtime: 0 });
function hasMixin(states, ...requiredMixins) { return requiredMixins.every( (requiredMixin) => states[0]?.[`get${macro.capitalize(requiredMixin)}`]?.() != null ); }
publicAPI.getRepresentationStates = (input = model.inputData[0]) => superClass .getRepresentationStates(input) .filter((state) => state.getOrigin?.() && (state.isVisible?.() ?? true));
publicAPI.getMixins = (states) => { const glyphProperties = {}; if (hasMixin(states, 'origin')) { glyphProperties.position = model.applyMixin.origin; } else { glyphProperties.position = model.applyMixin.noPosition; }
if (hasMixin(states, 'color3')) { glyphProperties.color = model.applyMixin.color3; } else if (hasMixin(states, 'color')) { glyphProperties.color = model.applyMixin.color; } else { glyphProperties.color = model.applyMixin.noColor; }
if (hasMixin(states, 'scale3')) { glyphProperties.scale = model.applyMixin.scale3; } else if (hasMixin(states, 'scale1')) { glyphProperties.scale = model.applyMixin.scale1; } else { glyphProperties.scale = model.applyMixin.noScale; }
if (hasMixin(states, 'direction')) { glyphProperties.orientation = model.applyMixin.direction; } else { glyphProperties.orientation = model.applyMixin.noOrientation; } return glyphProperties; };
publicAPI.requestData = (inData, outData) => { const states = publicAPI.getRepresentationStates(inData[0]); outData[0] = internalPolyData;
const glyphProperties = publicAPI.getMixins(states);
Object.values(glyphProperties).forEach((property) => property(internalPolyData, states) );
internalPolyData.getPoints().modified(); internalPolyData.modified(); };
vtkWidgetRepresentation.connectPipeline(model._pipeline); publicAPI.addActor(model._pipeline.actor); }
function defaultValues(publicAPI, model, initialValues) { return { defaultScale: 1, ...initialValues, _pipeline: { source: initialValues._pipeline?.source ?? publicAPI, glyph: initialValues._pipeline?.glyph ?? vtkSphereSource.newInstance({ phiResolution: 8, thetaResolution: 8, }), mapper: initialValues._pipeline?.mapper ?? vtkGlyph3DMapper.newInstance({ scalarMode: ScalarMode.USE_POINT_FIELD_DATA, }), actor: initialValues._pipeline?.actor ?? vtkActor.newInstance({ parentProp: publicAPI }), ...initialValues._pipeline, }, applyMixin: { origin: initialValues.applyMixin?.origin ?? origin(publicAPI, model), noPosition: initialValues.applyMixin?.noPosition ?? noPosition(publicAPI, model), color3: initialValues.applyMixin?.color3 ?? color3(publicAPI, model), color: initialValues.applyMixin?.color ?? color(publicAPI, model), noColor: initialValues.applyMixin?.noColor ?? noColor(publicAPI, model), scale3: initialValues.applyMixin?.scale3 ?? scale3(publicAPI, model), scale1: initialValues.applyMixin?.scale1 ?? scale1(publicAPI, model), noScale: initialValues.applyMixin?.noScale ?? noScale(publicAPI, model), direction: initialValues.applyMixin?.direction ?? direction(publicAPI, model), noOrientation: initialValues.applyMixin?.noOrientation ?? noOrientation(publicAPI, model), ...initialValues.applyMixin, }, }; }
export function extend(publicAPI, model, initialValues = {}) { if (initialValues.behavior === Behavior.CONTEXT) { vtkContextRepresentation.extend( publicAPI, model, defaultValues(publicAPI, model, initialValues) ); } else { vtkHandleRepresentation.extend( publicAPI, model, defaultValues(publicAPI, model, initialValues) ); } if ('lighting' in initialValues) { model._pipeline.actor.getProperty().setLighting(initialValues.lighting); }
macro.setGet(publicAPI, model._pipeline, ['defaultScale']); macro.get(publicAPI, model._pipeline, ['glyph', 'mapper', 'actor']); macro.setGet(publicAPI, model.applyMixin, Object.keys(model.applyMixin));
vtkGlyphRepresentation(publicAPI, model); }
export const newInstance = macro.newInstance(extend, 'vtkGlyphRepresentation');
export default { newInstance, extend };
|