UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

312 lines (253 loc) 11.7 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import macro from '../../macros.js'; import vtkAbstractPicker from './AbstractPicker.js'; import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js'; import { d as dot, l as normalize, n as norm, e as distance2BetweenPoints } from '../../Common/Core/Math/index.js'; import { mat4, vec4 } from 'gl-matrix'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var vtkErrorMacro = macro.vtkErrorMacro; var vtkWarningMacro = macro.vtkWarningMacro; // ---------------------------------------------------------------------------- // vtkPicker methods // ---------------------------------------------------------------------------- function vtkPicker(publicAPI, model) { // Set our className model.classHierarchy.push('vtkPicker'); var superClass = _objectSpread({}, publicAPI); function initialize() { superClass.initialize(); model.actors = []; model.pickedPositions = []; model.mapperPosition[0] = 0.0; model.mapperPosition[1] = 0.0; model.mapperPosition[2] = 0.0; model.mapper = null; model.dataSet = null; model.globalTMin = Number.MAX_VALUE; } // Intersect data with specified ray. // Project the center point of the mapper onto the ray and determine its parametric value publicAPI.intersectWithLine = function (p1, p2, tol, mapper) { if (!mapper) { return Number.MAX_VALUE; } var center = mapper.getCenter(); var ray = []; for (var i = 0; i < 3; i++) { ray[i] = p2[i] - p1[i]; } var rayFactor = dot(ray, ray); if (rayFactor === 0.0) { return 2.0; } // Project the center point onto the ray and determine its parametric value var t = (ray[0] * (center[0] - p1[0]) + ray[1] * (center[1] - p1[1]) + ray[2] * (center[2] - p1[2])) / rayFactor; return t; }; // To be overridden in subclasses publicAPI.pick = function (selection, renderer) { if (selection.length !== 3) { vtkWarningMacro('vtkPicker::pick: selectionPt needs three components'); } var selectionX = selection[0]; var selectionY = selection[1]; var selectionZ = selection[2]; var cameraPos = []; var cameraFP = []; var displayCoords = []; var worldCoords = []; var ray = []; var cameraDOP = []; var clipRange = []; var tF; var tB; var p1World = []; var p2World = []; var viewport = []; var winSize = []; var x; var y; var windowLowerLeft = []; var windowUpperRight = []; var tol = 0.0; var props = []; var pickable = false; var p1Mapper = new Float64Array(4); var p2Mapper = new Float64Array(4); var bbox = vtkBoundingBox.newInstance(); var t = []; var hitPosition = []; var view = renderer.getRenderWindow().getViews()[0]; initialize(); model.renderer = renderer; model.selectionPoint[0] = selectionX; model.selectionPoint[1] = selectionY; model.selectionPoint[2] = selectionZ; if (!renderer) { vtkErrorMacro('Picker::Pick Must specify renderer'); return; } // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. var camera = renderer.getActiveCamera(); cameraPos = camera.getPosition(); cameraFP = camera.getFocalPoint(); var dims = view.getViewportSize(renderer); var aspect = dims[0] / dims[1]; displayCoords = renderer.worldToNormalizedDisplay(cameraFP[0], cameraFP[1], cameraFP[2], aspect); displayCoords = view.normalizedDisplayToDisplay(displayCoords[0], displayCoords[1], displayCoords[2]); selectionZ = displayCoords[2]; // Convert the selection point into world coordinates. var normalizedDisplay = view.displayToNormalizedDisplay(selectionX, selectionY, selectionZ); worldCoords = renderer.normalizedDisplayToWorld(normalizedDisplay[0], normalizedDisplay[1], normalizedDisplay[2], aspect); for (var i = 0; i < 3; i++) { model.pickPosition[i] = worldCoords[i]; } // Compute the ray endpoints. The ray is along the line running from // the camera position to the selection point, starting where this line // intersects the front clipping plane, and terminating where this // line intersects the back clipping plane. for (var _i = 0; _i < 3; _i++) { ray[_i] = model.pickPosition[_i] - cameraPos[_i]; } for (var _i2 = 0; _i2 < 3; _i2++) { cameraDOP[_i2] = cameraFP[_i2] - cameraPos[_i2]; } normalize(cameraDOP); var rayLength = dot(cameraDOP, ray); if (rayLength === 0.0) { vtkWarningMacro('Picker::Pick Cannot process points'); return; } clipRange = camera.getClippingRange(); if (camera.getParallelProjection()) { tF = clipRange[0] - rayLength; tB = clipRange[1] - rayLength; for (var _i3 = 0; _i3 < 3; _i3++) { p1World[_i3] = model.pickPosition[_i3] + tF * cameraDOP[_i3]; p2World[_i3] = model.pickPosition[_i3] + tB * cameraDOP[_i3]; } } else { tF = clipRange[0] / rayLength; tB = clipRange[1] / rayLength; for (var _i4 = 0; _i4 < 3; _i4++) { p1World[_i4] = cameraPos[_i4] + tF * ray[_i4]; p2World[_i4] = cameraPos[_i4] + tB * ray[_i4]; } } p1World[3] = 1.0; p2World[3] = 1.0; // Compute the tolerance in world coordinates. Do this by // determining the world coordinates of the diagonal points of the // window, computing the width of the window in world coordinates, and // multiplying by the tolerance. viewport = renderer.getViewport(); if (renderer.getRenderWindow()) { winSize = renderer.getRenderWindow().getViews()[0].getSize(); } x = winSize[0] * viewport[0]; y = winSize[1] * viewport[1]; var normalizedLeftDisplay = view.displayToNormalizedDisplay(x, y, selectionZ); windowLowerLeft = renderer.normalizedDisplayToWorld(normalizedLeftDisplay[0], normalizedLeftDisplay[1], normalizedLeftDisplay[2], aspect); x = winSize[0] * viewport[2]; y = winSize[1] * viewport[3]; var normalizedRightDisplay = view.displayToNormalizedDisplay(x, y, selectionZ); windowUpperRight = renderer.normalizedDisplayToWorld(normalizedRightDisplay[0], normalizedRightDisplay[1], normalizedRightDisplay[2], aspect); for (var _i5 = 0; _i5 < 3; _i5++) { tol += (windowUpperRight[_i5] - windowLowerLeft[_i5]) * (windowUpperRight[_i5] - windowLowerLeft[_i5]); } tol = Math.sqrt(tol) * model.tolerance; if (model.pickFromList) { props = model.pickList; } else { props = renderer.getActors(); } var scale = []; props.forEach(function (prop) { var mapper = prop.getMapper(); pickable = prop.getNestedPickable() && prop.getNestedVisibility(); if (prop.getProperty().getOpacity() <= 0.0) { pickable = false; } if (pickable) { model.transformMatrix = prop.getMatrix().slice(0); // Webgl need a transpose matrix but we need the untransposed one to project world points // into the right referential mat4.transpose(model.transformMatrix, model.transformMatrix); mat4.invert(model.transformMatrix, model.transformMatrix); // Extract scale var col1 = [model.transformMatrix[0], model.transformMatrix[1], model.transformMatrix[2]]; var col2 = [model.transformMatrix[4], model.transformMatrix[5], model.transformMatrix[6]]; var col3 = [model.transformMatrix[8], model.transformMatrix[9], model.transformMatrix[10]]; scale[0] = norm(col1); scale[1] = norm(col2); scale[2] = norm(col3); vec4.transformMat4(p1Mapper, p1World, model.transformMatrix); vec4.transformMat4(p2Mapper, p2World, model.transformMatrix); p1Mapper[0] /= p1Mapper[3]; p1Mapper[1] /= p1Mapper[3]; p1Mapper[2] /= p1Mapper[3]; p2Mapper[0] /= p2Mapper[3]; p2Mapper[1] /= p2Mapper[3]; p2Mapper[2] /= p2Mapper[3]; for (var _i6 = 0; _i6 < 3; _i6++) { ray[_i6] = p2Mapper[_i6] - p1Mapper[_i6]; } if (mapper) { bbox.setBounds(mapper.getBounds()); bbox.inflate(tol); } else { bbox.reset(); } if (bbox.intersectBox(p1Mapper, ray, hitPosition, t)) { t[0] = publicAPI.intersectWithLine(p1Mapper, p2Mapper, tol * 0.333 * (scale[0] + scale[1] + scale[2]), mapper); if (t[0] < Number.MAX_VALUE) { var p = []; p[0] = (1.0 - t[0]) * p1World[0] + t[0] * p2World[0]; p[1] = (1.0 - t[0]) * p1World[1] + t[0] * p2World[1]; p[2] = (1.0 - t[0]) * p1World[2] + t[0] * p2World[2]; // Check if the current actor is already in the list var actorID = -1; for (var _i7 = 0; _i7 < model.actors.length; _i7++) { if (model.actors[_i7] === prop) { actorID = _i7; break; } } if (actorID === -1) { model.actors.push(prop); model.pickedPositions.push(p); } else { var oldPoint = model.pickedPositions[actorID]; var distOld = distance2BetweenPoints(p1World, oldPoint); var distCurrent = distance2BetweenPoints(p1World, p); if (distCurrent < distOld) { model.pickedPositions[actorID] = p; } } } } } publicAPI.invokePickChange(model.pickedPositions); return 1; }); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { tolerance: 0.025, mapperPosition: [0.0, 0.0, 0.0], mapper: null, dataSet: null, actors: [], pickedPositions: [], transformMatrix: null, globalTMin: Number.MAX_VALUE }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkAbstractPicker.extend(publicAPI, model, initialValues); macro.setGet(publicAPI, model, ['tolerance']); macro.setGetArray(publicAPI, model, ['mapperPosition'], 3); macro.get(publicAPI, model, ['mapper', 'dataSet', 'actors', 'pickedPositions']); macro.event(publicAPI, model, 'pickChange'); vtkPicker(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkPicker'); // ---------------------------------------------------------------------------- var vtkPicker$1 = { newInstance: newInstance, extend: extend }; export { vtkPicker$1 as default, extend, newInstance };