UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

638 lines (520 loc) 23.9 kB
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import macro from '../../macro.js'; import { r as radiansFromDegrees, k as add, j as cross } from '../../Common/Core/Math/index.js'; import { i as identity, r as rotate, t as translate, g as invert, j as transpose, s as scale, m as multiply$1, d as copy, l as lookAt, o as ortho, k as fromQuat } from '../../vendor/gl-matrix/esm/mat4.js'; import { t as transformMat4 } from '../../vendor/gl-matrix/esm/vec4.js'; import { t as transformMat4$1, s as set, a as subtract, n as normalize } from '../../vendor/gl-matrix/esm/vec3.js'; import { f as fromValues, c as create, a as conjugate, m as multiply, s as setAxisAngle } from '../../vendor/gl-matrix/esm/quat.js'; var vtkDebugMacro = macro.vtkDebugMacro; /* eslint-disable new-cap */ /* * Convenience function to access elements of a gl-matrix. If it turns * out I have rows and columns swapped everywhere, then I'll just change * the order of 'row' and 'col' parameters in this function */ // function getMatrixElement(matrix, row, col) { // const idx = (row * 4) + col; // return matrix[idx]; // } // ---------------------------------------------------------------------------- // vtkCamera methods // ---------------------------------------------------------------------------- function vtkCamera(publicAPI, model) { // Set our className model.classHierarchy.push('vtkCamera'); // Set up private variables and methods var origin = new Float64Array(3); var dopbasis = new Float64Array([0.0, 0.0, -1.0]); var upbasis = new Float64Array([0.0, 1.0, 0.0]); var tmpMatrix = identity(new Float64Array(16)); var tmpvec1 = new Float64Array(3); var tmpvec2 = new Float64Array(3); var tmpvec3 = new Float64Array(3); var rotateMatrix = identity(new Float64Array(16)); var trans = identity(new Float64Array(16)); var newPosition = new Float64Array(3); var newFocalPoint = new Float64Array(3); // Internal Functions that don't need to be public function computeViewPlaneNormal() { // VPN is -DOP model.viewPlaneNormal[0] = -model.directionOfProjection[0]; model.viewPlaneNormal[1] = -model.directionOfProjection[1]; model.viewPlaneNormal[2] = -model.directionOfProjection[2]; } publicAPI.orthogonalizeViewUp = function () { var vt = publicAPI.getViewMatrix(); model.viewUp[0] = vt[4]; model.viewUp[1] = vt[5]; model.viewUp[2] = vt[6]; publicAPI.modified(); }; publicAPI.setPosition = function (x, y, z) { if (x === model.position[0] && y === model.position[1] && z === model.position[2]) { return; } model.position[0] = x; model.position[1] = y; model.position[2] = z; // recompute the focal distance publicAPI.computeDistance(); publicAPI.modified(); }; publicAPI.setFocalPoint = function (x, y, z) { if (x === model.focalPoint[0] && y === model.focalPoint[1] && z === model.focalPoint[2]) { return; } model.focalPoint[0] = x; model.focalPoint[1] = y; model.focalPoint[2] = z; // recompute the focal distance publicAPI.computeDistance(); publicAPI.modified(); }; publicAPI.setDistance = function (d) { if (model.distance === d) { return; } model.distance = d; if (model.distance < 1e-20) { model.distance = 1e-20; vtkDebugMacro('Distance is set to minimum.'); } // we want to keep the camera pointing in the same direction var vec = model.directionOfProjection; // recalculate FocalPoint model.focalPoint[0] = model.position[0] + vec[0] * model.distance; model.focalPoint[1] = model.position[1] + vec[1] * model.distance; model.focalPoint[2] = model.position[2] + vec[2] * model.distance; publicAPI.modified(); }; //---------------------------------------------------------------------------- // This method must be called when the focal point or camera position changes publicAPI.computeDistance = function () { var dx = model.focalPoint[0] - model.position[0]; var dy = model.focalPoint[1] - model.position[1]; var dz = model.focalPoint[2] - model.position[2]; model.distance = Math.sqrt(dx * dx + dy * dy + dz * dz); if (model.distance < 1e-20) { model.distance = 1e-20; vtkDebugMacro('Distance is set to minimum.'); var vec = model.directionOfProjection; // recalculate FocalPoint model.focalPoint[0] = model.position[0] + vec[0] * model.distance; model.focalPoint[1] = model.position[1] + vec[1] * model.distance; model.focalPoint[2] = model.position[2] + vec[2] * model.distance; } model.directionOfProjection[0] = dx / model.distance; model.directionOfProjection[1] = dy / model.distance; model.directionOfProjection[2] = dz / model.distance; computeViewPlaneNormal(); }; //---------------------------------------------------------------------------- // Move the position of the camera along the view plane normal. Moving // towards the focal point (e.g., > 1) is a dolly-in, moving away // from the focal point (e.g., < 1) is a dolly-out. publicAPI.dolly = function (amount) { if (amount <= 0.0) { return; } // dolly moves the camera towards the focus var d = model.distance / amount; publicAPI.setPosition(model.focalPoint[0] - d * model.directionOfProjection[0], model.focalPoint[1] - d * model.directionOfProjection[1], model.focalPoint[2] - d * model.directionOfProjection[2]); }; publicAPI.roll = function (angle) { var eye = model.position; var at = model.focalPoint; var up = model.viewUp; var viewUpVec4 = new Float64Array([up[0], up[1], up[2], 0.0]); identity(rotateMatrix); var viewDir = new Float64Array([at[0] - eye[0], at[1] - eye[1], at[2] - eye[2]]); rotate(rotateMatrix, rotateMatrix, radiansFromDegrees(angle), viewDir); transformMat4(viewUpVec4, viewUpVec4, rotateMatrix); model.viewUp[0] = viewUpVec4[0]; model.viewUp[1] = viewUpVec4[1]; model.viewUp[2] = viewUpVec4[2]; publicAPI.modified(); }; publicAPI.azimuth = function (angle) { var fp = model.focalPoint; identity(trans); // translate the focal point to the origin, // rotate about view up, // translate back again translate(trans, trans, fp); rotate(trans, trans, radiansFromDegrees(angle), model.viewUp); translate(trans, trans, [-fp[0], -fp[1], -fp[2]]); // apply the transform to the position transformMat4$1(newPosition, model.position, trans); publicAPI.setPosition(newPosition[0], newPosition[1], newPosition[2]); }; publicAPI.yaw = function (angle) { var position = model.position; identity(trans); // translate the camera to the origin, // rotate about axis, // translate back again translate(trans, trans, position); rotate(trans, trans, radiansFromDegrees(angle), model.viewUp); translate(trans, trans, [-position[0], -position[1], -position[2]]); // apply the transform to the position transformMat4$1(newFocalPoint, model.focalPoint, trans); publicAPI.setFocalPoint(newFocalPoint[0], newFocalPoint[1], newFocalPoint[2]); }; publicAPI.elevation = function (angle) { var fp = model.focalPoint; // get the eye / camera position from the viewMatrix var vt = publicAPI.getViewMatrix(); var axis = [-vt[0], -vt[1], -vt[2]]; identity(trans); // translate the focal point to the origin, // rotate about view up, // translate back again translate(trans, trans, fp); rotate(trans, trans, radiansFromDegrees(angle), axis); translate(trans, trans, [-fp[0], -fp[1], -fp[2]]); // apply the transform to the position transformMat4$1(newPosition, model.position, trans); publicAPI.setPosition(newPosition[0], newPosition[1], newPosition[2]); }; publicAPI.pitch = function (angle) { var position = model.position; var vt = publicAPI.getViewMatrix(); var axis = [vt[0], vt[1], vt[2]]; identity(trans); // translate the camera to the origin, // rotate about axis, // translate back again translate(trans, trans, position); rotate(trans, trans, radiansFromDegrees(angle), axis); translate(trans, trans, [-position[0], -position[1], -position[2]]); // apply the transform to the focal point transformMat4$1(newFocalPoint, model.focalPoint, trans); publicAPI.setFocalPoint.apply(publicAPI, _toConsumableArray(newFocalPoint)); }; publicAPI.zoom = function (factor) { if (factor <= 0) { return; } if (model.parallelProjection) { model.parallelScale /= factor; } else { model.viewAngle /= factor; } publicAPI.modified(); }; publicAPI.translate = function (x, y, z) { var offset = [x, y, z]; add(model.position, offset, model.position); add(model.focalPoint, offset, model.focalPoint); publicAPI.computeDistance(); publicAPI.modified(); }; publicAPI.applyTransform = function (transformMat4$1) { var vuOld = [].concat(_toConsumableArray(model.viewUp), [1.0]); var posNew = []; var fpNew = []; var vuNew = []; vuOld[0] += model.position[0]; vuOld[1] += model.position[1]; vuOld[2] += model.position[2]; transformMat4(posNew, [].concat(_toConsumableArray(model.position), [1.0]), transformMat4$1); transformMat4(fpNew, [].concat(_toConsumableArray(model.focalPoint), [1.0]), transformMat4$1); transformMat4(vuNew, vuOld, transformMat4$1); vuNew[0] -= posNew[0]; vuNew[1] -= posNew[1]; vuNew[2] -= posNew[2]; publicAPI.setPosition.apply(publicAPI, _toConsumableArray(posNew.slice(0, 3))); publicAPI.setFocalPoint.apply(publicAPI, _toConsumableArray(fpNew.slice(0, 3))); publicAPI.setViewUp.apply(publicAPI, _toConsumableArray(vuNew.slice(0, 3))); }; publicAPI.getThickness = function () { return model.clippingRange[1] - model.clippingRange[0]; }; publicAPI.setThickness = function (thickness) { var t = thickness; if (t < 1e-20) { t = 1e-20; vtkDebugMacro('Thickness is set to minimum.'); } publicAPI.setClippingRange(model.clippingRange[0], model.clippingRange[0] + t); }; publicAPI.setThicknessFromFocalPoint = function (thickness) { var t = thickness; if (t < 1e-20) { t = 1e-20; vtkDebugMacro('Thickness is set to minimum.'); } publicAPI.setClippingRange(model.distance - t / 2, model.distance + t / 2); }; // Unimplemented functions publicAPI.setRoll = function (angle) {}; // dependency on GetOrientation() and a model.ViewTransform object, see https://github.com/Kitware/VTK/blob/master/Common/Transforms/vtkTransform.cxx and https://vtk.org/doc/nightly/html/classvtkTransform.html publicAPI.getRoll = function () {}; publicAPI.setObliqueAngles = function (alpha, beta) {}; publicAPI.getOrientation = function () {}; publicAPI.getOrientationWXYZ = function () {}; publicAPI.getFrustumPlanes = function (aspect) {// Return array of 24 params (4 params for each of 6 plane equations) }; publicAPI.getCameraLightTransformMatrix = function () {}; publicAPI.deepCopy = function (sourceCamera) {}; publicAPI.physicalOrientationToWorldDirection = function (ori) { // push the x axis through the orientation quat var oriq = fromValues(ori[0], ori[1], ori[2], ori[3]); var coriq = create(); var qdir = fromValues(0.0, 0.0, 1.0, 0.0); conjugate(coriq, oriq); // rotate the z axis by the quat multiply(qdir, oriq, qdir); multiply(qdir, qdir, coriq); // return the z axis in world coords return [qdir[0], qdir[1], qdir[2]]; }; publicAPI.getPhysicalToWorldMatrix = function (result) { publicAPI.getWorldToPhysicalMatrix(result); invert(result, result); }; publicAPI.getWorldToPhysicalMatrix = function (result) { identity(result); // now the physical to vtk world rotation tform var physVRight = [3]; cross(model.physicalViewNorth, model.physicalViewUp, physVRight); result[0] = physVRight[0]; result[1] = physVRight[1]; result[2] = physVRight[2]; result[4] = model.physicalViewUp[0]; result[5] = model.physicalViewUp[1]; result[6] = model.physicalViewUp[2]; result[8] = -model.physicalViewNorth[0]; result[9] = -model.physicalViewNorth[1]; result[10] = -model.physicalViewNorth[2]; transpose(result, result); set(tmpvec1, 1 / model.physicalScale, 1 / model.physicalScale, 1 / model.physicalScale); scale(result, result, tmpvec1); translate(result, result, model.physicalTranslation); }; publicAPI.computeViewParametersFromViewMatrix = function (vmat) { // invert to get view to world invert(tmpMatrix, vmat); // note with glmatrix operations happen in // the reverse order // mat.scale // mat.translate // will result in the translation then the scale // mat.mult(a,b) // results in perform the B transformation then A // then extract the params position, orientation // push 0,0,0 through to get a translation transformMat4$1(tmpvec1, origin, tmpMatrix); publicAPI.computeDistance(); var oldDist = model.distance; publicAPI.setPosition(tmpvec1[0], tmpvec1[1], tmpvec1[2]); // push basis vectors to get orientation transformMat4$1(tmpvec2, dopbasis, tmpMatrix); subtract(tmpvec2, tmpvec2, tmpvec1); normalize(tmpvec2, tmpvec2); publicAPI.setDirectionOfProjection(tmpvec2[0], tmpvec2[1], tmpvec2[2]); transformMat4$1(tmpvec3, upbasis, tmpMatrix); subtract(tmpvec3, tmpvec3, tmpvec1); normalize(tmpvec3, tmpvec3); publicAPI.setViewUp(tmpvec3[0], tmpvec3[1], tmpvec3[2]); publicAPI.setDistance(oldDist); }; // the provided matrix should include // translation and orientation only // mat is physical to view publicAPI.computeViewParametersFromPhysicalMatrix = function (mat) { // get the WorldToPhysicalMatrix publicAPI.getWorldToPhysicalMatrix(tmpMatrix); // first convert the physical -> view matrix to be // world -> view multiply$1(tmpMatrix, mat, tmpMatrix); publicAPI.computeViewParametersFromViewMatrix(tmpMatrix); }; publicAPI.setViewMatrix = function (mat) { model.viewMatrix = mat; if (model.viewMatrix) { copy(tmpMatrix, model.viewMatrix); publicAPI.computeViewParametersFromViewMatrix(tmpMatrix); transpose(model.viewMatrix, model.viewMatrix); } }; publicAPI.getViewMatrix = function () { if (model.viewMatrix) { return model.viewMatrix; } lookAt(tmpMatrix, model.position, // eye model.focalPoint, // at model.viewUp // up ); transpose(tmpMatrix, tmpMatrix); var result = new Float64Array(16); copy(result, tmpMatrix); return result; }; publicAPI.setProjectionMatrix = function (mat) { model.projectionMatrix = mat; }; publicAPI.getProjectionMatrix = function (aspect, nearz, farz) { var result = new Float64Array(16); identity(result); if (model.projectionMatrix) { var scale$1 = 1 / model.physicalScale; set(tmpvec1, scale$1, scale$1, scale$1); copy(result, model.projectionMatrix); scale(result, result, tmpvec1); transpose(result, result); return result; } identity(tmpMatrix); // FIXME: Not sure what to do about adjust z buffer here // adjust Z-buffer range // this->ProjectionTransform->AdjustZBuffer( -1, +1, nearz, farz ); var cWidth = model.clippingRange[1] - model.clippingRange[0]; var cRange = [model.clippingRange[0] + (nearz + 1) * cWidth / 2.0, model.clippingRange[0] + (farz + 1) * cWidth / 2.0]; if (model.parallelProjection) { // set up a rectangular parallelipiped var width = model.parallelScale * aspect; var height = model.parallelScale; var xmin = (model.windowCenter[0] - 1.0) * width; var xmax = (model.windowCenter[0] + 1.0) * width; var ymin = (model.windowCenter[1] - 1.0) * height; var ymax = (model.windowCenter[1] + 1.0) * height; ortho(tmpMatrix, xmin, xmax, ymin, ymax, cRange[0], cRange[1]); transpose(tmpMatrix, tmpMatrix); } else if (model.useOffAxisProjection) { throw new Error('Off-Axis projection is not supported at this time'); } else { var tmp = Math.tan(radiansFromDegrees(model.viewAngle) / 2.0); var _width; var _height; if (model.useHorizontalViewAngle === true) { _width = model.clippingRange[0] * tmp; _height = model.clippingRange[0] * tmp / aspect; } else { _width = model.clippingRange[0] * tmp * aspect; _height = model.clippingRange[0] * tmp; } var _xmin = (model.windowCenter[0] - 1.0) * _width; var _xmax = (model.windowCenter[0] + 1.0) * _width; var _ymin = (model.windowCenter[1] - 1.0) * _height; var _ymax = (model.windowCenter[1] + 1.0) * _height; var znear = cRange[0]; var zfar = cRange[1]; tmpMatrix[0] = 2.0 * znear / (_xmax - _xmin); tmpMatrix[5] = 2.0 * znear / (_ymax - _ymin); tmpMatrix[2] = (_xmin + _xmax) / (_xmax - _xmin); tmpMatrix[6] = (_ymin + _ymax) / (_ymax - _ymin); tmpMatrix[10] = -(znear + zfar) / (zfar - znear); tmpMatrix[14] = -1.0; tmpMatrix[11] = -2.0 * znear * zfar / (zfar - znear); tmpMatrix[15] = 0.0; } copy(result, tmpMatrix); return result; }; publicAPI.getCompositeProjectionMatrix = function (aspect, nearz, farz) { var vMat = publicAPI.getViewMatrix(); var pMat = publicAPI.getProjectionMatrix(aspect, nearz, farz); // mats are transposed so the order is A then B // we reuse pMat as it is a copy so we can do what we want with it multiply$1(pMat, vMat, pMat); return pMat; }; publicAPI.setDirectionOfProjection = function (x, y, z) { if (model.directionOfProjection[0] === x && model.directionOfProjection[1] === y && model.directionOfProjection[2] === z) { return; } model.directionOfProjection[0] = x; model.directionOfProjection[1] = y; model.directionOfProjection[2] = z; var vec = model.directionOfProjection; // recalculate FocalPoint model.focalPoint[0] = model.position[0] + vec[0] * model.distance; model.focalPoint[1] = model.position[1] + vec[1] * model.distance; model.focalPoint[2] = model.position[2] + vec[2] * model.distance; computeViewPlaneNormal(); }; // used to handle convert js device orientation angles // when you use this method the camera will adjust to the // device orientation such that the physicalViewUp you set // in world coordinates looks up, and the physicalViewNorth // you set in world coorindates will (maybe) point north // // NOTE WARNING - much of the documentation out there on how // orientation works is seriously wrong. Even worse the Chrome // device orientation simulator is completely wrong and should // never be used. OMG it is so messed up. // // how it seems to work on iOS is that the device orientation // is specified in extrinsic angles with a alpha, beta, gamma // convention with axes of Z, X, Y (the code below substitutes // the physical coordinate system for these axes to get the right // modified coordinate system. publicAPI.setDeviceAngles = function (alpha, beta, gamma, screen) { var physVRight = [3]; cross(model.physicalViewNorth, model.physicalViewUp, physVRight); // phone to physical coordinates var rotmat = identity(new Float64Array(16)); rotate(rotmat, rotmat, radiansFromDegrees(alpha), model.physicalViewUp); rotate(rotmat, rotmat, radiansFromDegrees(beta), physVRight); rotate(rotmat, rotmat, radiansFromDegrees(gamma), model.physicalViewNorth); rotate(rotmat, rotmat, radiansFromDegrees(-screen), model.physicalViewUp); var dop = new Float64Array([-model.physicalViewUp[0], -model.physicalViewUp[1], -model.physicalViewUp[2]]); var vup = new Float64Array(model.physicalViewNorth); transformMat4$1(dop, dop, rotmat); transformMat4$1(vup, vup, rotmat); publicAPI.setDirectionOfProjection(dop[0], dop[1], dop[2]); publicAPI.setViewUp(vup[0], vup[1], vup[2]); publicAPI.modified(); }; publicAPI.setOrientationWXYZ = function (degrees, x, y, z) { var quatMat = identity(new Float64Array(16)); if (degrees !== 0.0 && (x !== 0.0 || y !== 0.0 || z !== 0.0)) { // convert to radians var angle = radiansFromDegrees(degrees); var q = create(); setAxisAngle(q, [x, y, z], angle); fromQuat(quatMat, q); } var newdop = new Float64Array(3); transformMat4$1(newdop, [0.0, 0.0, -1.0], quatMat); var newvup = new Float64Array(3); transformMat4$1(newvup, [0.0, 1.0, 0.0], quatMat); publicAPI.setDirectionOfProjection.apply(publicAPI, _toConsumableArray(newdop)); publicAPI.setViewUp.apply(publicAPI, _toConsumableArray(newvup)); publicAPI.modified(); }; publicAPI.computeClippingRange = function (bounds) { var vn = null; var position = null; vn = model.viewPlaneNormal; position = model.position; var a = -vn[0]; var b = -vn[1]; var c = -vn[2]; var d = -(a * position[0] + b * position[1] + c * position[2]); // Set the max near clipping plane and the min far clipping plane var range = [a * bounds[0] + b * bounds[2] + c * bounds[4] + d, 1e-18]; // Find the closest / farthest bounding box vertex for (var k = 0; k < 2; k++) { for (var j = 0; j < 2; j++) { for (var i = 0; i < 2; i++) { var dist = a * bounds[i] + b * bounds[2 + j] + c * bounds[4 + k] + d; range[0] = dist < range[0] ? dist : range[0]; range[1] = dist > range[1] ? dist : range[1]; } } } return range; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { position: [0, 0, 1], focalPoint: [0, 0, 0], viewUp: [0, 1, 0], directionOfProjection: [0, 0, -1], parallelProjection: false, useHorizontalViewAngle: false, viewAngle: 30, parallelScale: 1, clippingRange: [0.01, 1000.01], windowCenter: [0, 0], viewPlaneNormal: [0, 0, 1], useOffAxisProjection: false, screenBottomLeft: [-0.5, -0.5, -0.5], screenBottomRight: [0.5, -0.5, -0.5], screenTopRight: [0.5, 0.5, -0.5], freezeFocalPoint: false, projectionMatrix: null, viewMatrix: null, // used for world to physical transformations physicalTranslation: [0, 0, 0], physicalScale: 1.0, physicalViewUp: [0, 1, 0], physicalViewNorth: [0, 0, -1] }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API macro.obj(publicAPI, model); macro.get(publicAPI, model, ['distance']); macro.setGet(publicAPI, model, ['parallelProjection', 'useHorizontalViewAngle', 'viewAngle', 'parallelScale', 'useOffAxisProjection', 'freezeFocalPoint', 'physicalScale']); macro.getArray(publicAPI, model, ['directionOfProjection', 'viewPlaneNormal', 'position', 'focalPoint']); macro.setGetArray(publicAPI, model, ['clippingRange', 'windowCenter'], 2); macro.setGetArray(publicAPI, model, ['viewUp', 'screenBottomLeft', 'screenBottomRight', 'screenTopRight', 'physicalTranslation', 'physicalViewUp', 'physicalViewNorth'], 3); // Object methods vtkCamera(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkCamera'); // ---------------------------------------------------------------------------- var vtkCamera$1 = { newInstance: newInstance, extend: extend }; export default vtkCamera$1; export { DEFAULT_VALUES, extend, newInstance };