UNPKG

molstar

Version:

A comprehensive macromolecular library.

426 lines (425 loc) 23.3 kB
"use strict"; /** * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> * * This code has been modified from https://github.com/mrdoob/three.js/, * copyright (c) 2010-2018 three.js authors. MIT License */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TrackballControls = exports.TrackballControlsParams = exports.DefaultTrackballBindings = void 0; var tslib_1 = require("tslib"); var linear_algebra_1 = require("../../mol-math/linear-algebra"); var util_1 = require("../camera/util"); var input_observer_1 = require("../../mol-util/input/input-observer"); var param_definition_1 = require("../../mol-util/param-definition"); var misc_1 = require("../../mol-math/misc"); var binding_1 = require("../../mol-util/binding"); var B = input_observer_1.ButtonsType; var M = input_observer_1.ModifiersKeys; var Trigger = binding_1.Binding.Trigger; exports.DefaultTrackballBindings = { dragRotate: (0, binding_1.Binding)([Trigger(1 /* B.Flag.Primary */, M.create())], 'Rotate', 'Drag using ${triggers}'), dragRotateZ: (0, binding_1.Binding)([Trigger(1 /* B.Flag.Primary */, M.create({ shift: true }))], 'Rotate around z-axis', 'Drag using ${triggers}'), dragPan: (0, binding_1.Binding)([Trigger(2 /* B.Flag.Secondary */, M.create()), Trigger(1 /* B.Flag.Primary */, M.create({ control: true }))], 'Pan', 'Drag using ${triggers}'), dragZoom: binding_1.Binding.Empty, dragFocus: (0, binding_1.Binding)([Trigger(8 /* B.Flag.Forth */, M.create())], 'Focus', 'Drag using ${triggers}'), dragFocusZoom: (0, binding_1.Binding)([Trigger(4 /* B.Flag.Auxilary */, M.create())], 'Focus and zoom', 'Drag using ${triggers}'), scrollZoom: (0, binding_1.Binding)([Trigger(4 /* B.Flag.Auxilary */, M.create())], 'Zoom', 'Scroll using ${triggers}'), scrollFocus: (0, binding_1.Binding)([Trigger(4 /* B.Flag.Auxilary */, M.create({ shift: true }))], 'Clip', 'Scroll using ${triggers}'), scrollFocusZoom: binding_1.Binding.Empty, }; exports.TrackballControlsParams = { noScroll: param_definition_1.ParamDefinition.Boolean(true, { isHidden: true }), rotateSpeed: param_definition_1.ParamDefinition.Numeric(5.0, { min: 1, max: 10, step: 1 }), zoomSpeed: param_definition_1.ParamDefinition.Numeric(7.0, { min: 1, max: 15, step: 1 }), panSpeed: param_definition_1.ParamDefinition.Numeric(1.0, { min: 0.1, max: 5, step: 0.1 }), animate: param_definition_1.ParamDefinition.MappedStatic('off', { off: param_definition_1.ParamDefinition.EmptyGroup(), spin: param_definition_1.ParamDefinition.Group({ speed: param_definition_1.ParamDefinition.Numeric(1, { min: -20, max: 20, step: 1 }), }, { description: 'Spin the 3D scene around the x-axis in view space' }), rock: param_definition_1.ParamDefinition.Group({ speed: param_definition_1.ParamDefinition.Numeric(0.3, { min: -5, max: 5, step: 0.1 }), angle: param_definition_1.ParamDefinition.Numeric(10, { min: 0, max: 90, step: 1 }, { description: 'How many degrees to rotate in each direction.' }), }, { description: 'Rock the 3D scene around the x-axis in view space' }) }), staticMoving: param_definition_1.ParamDefinition.Boolean(true, { isHidden: true }), dynamicDampingFactor: param_definition_1.ParamDefinition.Numeric(0.2, {}, { isHidden: true }), minDistance: param_definition_1.ParamDefinition.Numeric(0.01, {}, { isHidden: true }), maxDistance: param_definition_1.ParamDefinition.Numeric(1e150, {}, { isHidden: true }), gestureScaleFactor: param_definition_1.ParamDefinition.Numeric(1, {}, { isHidden: true }), maxWheelDelta: param_definition_1.ParamDefinition.Numeric(0.02, {}, { isHidden: true }), bindings: param_definition_1.ParamDefinition.Value(exports.DefaultTrackballBindings, { isHidden: true }), /** * minDistance = minDistanceFactor * boundingSphere.radius + minDistancePadding * maxDistance = max(maxDistanceFactor * boundingSphere.radius, maxDistanceMin) */ autoAdjustMinMaxDistance: param_definition_1.ParamDefinition.MappedStatic('on', { off: param_definition_1.ParamDefinition.EmptyGroup(), on: param_definition_1.ParamDefinition.Group({ minDistanceFactor: param_definition_1.ParamDefinition.Numeric(0), minDistancePadding: param_definition_1.ParamDefinition.Numeric(5), maxDistanceFactor: param_definition_1.ParamDefinition.Numeric(10), maxDistanceMin: param_definition_1.ParamDefinition.Numeric(20) }) }, { isHidden: true }) }; var TrackballControls; (function (TrackballControls) { function create(input, camera, props) { if (props === void 0) { props = {}; } var p = tslib_1.__assign(tslib_1.__assign({}, param_definition_1.ParamDefinition.getDefaultValues(exports.TrackballControlsParams)), props); var viewport = util_1.Viewport.clone(camera.viewport); var disposed = false; var dragSub = input.drag.subscribe(onDrag); var interactionEndSub = input.interactionEnd.subscribe(onInteractionEnd); var wheelSub = input.wheel.subscribe(onWheel); var pinchSub = input.pinch.subscribe(onPinch); var gestureSub = input.gesture.subscribe(onGesture); var _isInteracting = false; // For internal use var lastPosition = (0, linear_algebra_1.Vec3)(); var _eye = (0, linear_algebra_1.Vec3)(); var _rotPrev = (0, linear_algebra_1.Vec2)(); var _rotCurr = (0, linear_algebra_1.Vec2)(); var _rotLastAxis = (0, linear_algebra_1.Vec3)(); var _rotLastAngle = 0; var _zRotPrev = (0, linear_algebra_1.Vec2)(); var _zRotCurr = (0, linear_algebra_1.Vec2)(); var _zRotLastAngle = 0; var _zoomStart = (0, linear_algebra_1.Vec2)(); var _zoomEnd = (0, linear_algebra_1.Vec2)(); var _focusStart = (0, linear_algebra_1.Vec2)(); var _focusEnd = (0, linear_algebra_1.Vec2)(); var _panStart = (0, linear_algebra_1.Vec2)(); var _panEnd = (0, linear_algebra_1.Vec2)(); // Initial values for reseting var target0 = linear_algebra_1.Vec3.clone(camera.target); var position0 = linear_algebra_1.Vec3.clone(camera.position); var up0 = linear_algebra_1.Vec3.clone(camera.up); var mouseOnScreenVec2 = (0, linear_algebra_1.Vec2)(); function getMouseOnScreen(pageX, pageY) { return linear_algebra_1.Vec2.set(mouseOnScreenVec2, (pageX - viewport.x) / viewport.width, (pageY - viewport.y) / viewport.height); } var mouseOnCircleVec2 = (0, linear_algebra_1.Vec2)(); function getMouseOnCircle(pageX, pageY) { return linear_algebra_1.Vec2.set(mouseOnCircleVec2, (pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5), (viewport.height + 2 * (viewport.y - pageY)) / viewport.width // screen.width intentional ); } function getRotateFactor() { var aspectRatio = input.width / input.height; return p.rotateSpeed * input.pixelRatio * aspectRatio; } var rotAxis = (0, linear_algebra_1.Vec3)(); var rotQuat = (0, linear_algebra_1.Quat)(); var rotEyeDir = (0, linear_algebra_1.Vec3)(); var rotObjUpDir = (0, linear_algebra_1.Vec3)(); var rotObjSideDir = (0, linear_algebra_1.Vec3)(); var rotMoveDir = (0, linear_algebra_1.Vec3)(); function rotateCamera() { var dx = _rotCurr[0] - _rotPrev[0]; var dy = _rotCurr[1] - _rotPrev[1]; linear_algebra_1.Vec3.set(rotMoveDir, dx, dy, 0); var angle = linear_algebra_1.Vec3.magnitude(rotMoveDir) * getRotateFactor(); if (angle) { linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); linear_algebra_1.Vec3.normalize(rotEyeDir, _eye); linear_algebra_1.Vec3.normalize(rotObjUpDir, camera.up); linear_algebra_1.Vec3.normalize(rotObjSideDir, linear_algebra_1.Vec3.cross(rotObjSideDir, rotObjUpDir, rotEyeDir)); linear_algebra_1.Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, dy); linear_algebra_1.Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, dx); linear_algebra_1.Vec3.add(rotMoveDir, rotObjUpDir, rotObjSideDir); linear_algebra_1.Vec3.normalize(rotAxis, linear_algebra_1.Vec3.cross(rotAxis, rotMoveDir, _eye)); linear_algebra_1.Quat.setAxisAngle(rotQuat, rotAxis, angle); linear_algebra_1.Vec3.transformQuat(_eye, _eye, rotQuat); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, rotQuat); linear_algebra_1.Vec3.copy(_rotLastAxis, rotAxis); _rotLastAngle = angle; } else if (!p.staticMoving && _rotLastAngle) { _rotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor); linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); linear_algebra_1.Quat.setAxisAngle(rotQuat, _rotLastAxis, _rotLastAngle); linear_algebra_1.Vec3.transformQuat(_eye, _eye, rotQuat); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, rotQuat); } linear_algebra_1.Vec2.copy(_rotPrev, _rotCurr); } var zRotQuat = (0, linear_algebra_1.Quat)(); function zRotateCamera() { var dx = _zRotCurr[0] - _zRotPrev[0]; var dy = _zRotCurr[1] - _zRotPrev[1]; var angle = p.rotateSpeed * (-dx + dy) * -0.05; if (angle) { linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); linear_algebra_1.Quat.setAxisAngle(zRotQuat, _eye, angle); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, zRotQuat); _zRotLastAngle = angle; } else if (!p.staticMoving && _zRotLastAngle) { _zRotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor); linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); linear_algebra_1.Quat.setAxisAngle(zRotQuat, _eye, _zRotLastAngle); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, zRotQuat); } linear_algebra_1.Vec2.copy(_zRotPrev, _zRotCurr); } function zoomCamera() { var factor = 1.0 + (_zoomEnd[1] - _zoomStart[1]) * p.zoomSpeed; if (factor !== 1.0 && factor > 0.0) { linear_algebra_1.Vec3.scale(_eye, _eye, factor); } if (p.staticMoving) { linear_algebra_1.Vec2.copy(_zoomStart, _zoomEnd); } else { _zoomStart[1] += (_zoomEnd[1] - _zoomStart[1]) * p.dynamicDampingFactor; } } function focusCamera() { var factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed; if (factor !== 0.0) { var radius = Math.max(1, camera.state.radius + camera.state.radius * factor); camera.setState({ radius: radius }); } if (p.staticMoving) { linear_algebra_1.Vec2.copy(_focusStart, _focusEnd); } else { _focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor; } } var panMouseChange = (0, linear_algebra_1.Vec2)(); var panObjUp = (0, linear_algebra_1.Vec3)(); var panOffset = (0, linear_algebra_1.Vec3)(); function panCamera() { linear_algebra_1.Vec2.sub(panMouseChange, linear_algebra_1.Vec2.copy(panMouseChange, _panEnd), _panStart); if (linear_algebra_1.Vec2.squaredMagnitude(panMouseChange)) { var factor = input.pixelRatio * p.panSpeed; panMouseChange[0] *= (1 / camera.zoom) * camera.viewport.width * factor; panMouseChange[1] *= (1 / camera.zoom) * camera.viewport.height * factor; linear_algebra_1.Vec3.cross(panOffset, linear_algebra_1.Vec3.copy(panOffset, _eye), camera.up); linear_algebra_1.Vec3.setMagnitude(panOffset, panOffset, panMouseChange[0]); linear_algebra_1.Vec3.setMagnitude(panObjUp, camera.up, panMouseChange[1]); linear_algebra_1.Vec3.add(panOffset, panOffset, panObjUp); linear_algebra_1.Vec3.add(camera.position, camera.position, panOffset); linear_algebra_1.Vec3.add(camera.target, camera.target, panOffset); if (p.staticMoving) { linear_algebra_1.Vec2.copy(_panStart, _panEnd); } else { linear_algebra_1.Vec2.sub(panMouseChange, _panEnd, _panStart); linear_algebra_1.Vec2.scale(panMouseChange, panMouseChange, p.dynamicDampingFactor); linear_algebra_1.Vec2.add(_panStart, _panStart, panMouseChange); } } } /** * Ensure the distance between object and target is within the min/max distance * and not too large compared to `camera.state.radiusMax` */ function checkDistances() { var maxDistance = Math.min(Math.max(camera.state.radiusMax * 1000, 0.01), p.maxDistance); if (linear_algebra_1.Vec3.squaredMagnitude(_eye) > maxDistance * maxDistance) { linear_algebra_1.Vec3.setMagnitude(_eye, _eye, maxDistance); linear_algebra_1.Vec3.add(camera.position, camera.target, _eye); linear_algebra_1.Vec2.copy(_zoomStart, _zoomEnd); linear_algebra_1.Vec2.copy(_focusStart, _focusEnd); } if (linear_algebra_1.Vec3.squaredMagnitude(_eye) < p.minDistance * p.minDistance) { linear_algebra_1.Vec3.setMagnitude(_eye, _eye, p.minDistance); linear_algebra_1.Vec3.add(camera.position, camera.target, _eye); linear_algebra_1.Vec2.copy(_zoomStart, _zoomEnd); linear_algebra_1.Vec2.copy(_focusStart, _focusEnd); } } function outsideViewport(x, y) { x *= input.pixelRatio; y *= input.pixelRatio; return (x > viewport.x + viewport.width || input.height - y > viewport.y + viewport.height || x < viewport.x || input.height - y < viewport.y); } var lastUpdated = -1; /** Update the object's position, direction and up vectors */ function update(t) { if (lastUpdated === t) return; if (lastUpdated > 0) { if (p.animate.name === 'spin') spin(t - lastUpdated); else if (p.animate.name === 'rock') rock(t - lastUpdated); } linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); rotateCamera(); zRotateCamera(); zoomCamera(); focusCamera(); panCamera(); linear_algebra_1.Vec3.add(camera.position, camera.target, _eye); checkDistances(); if (linear_algebra_1.Vec3.squaredDistance(lastPosition, camera.position) > linear_algebra_1.EPSILON) { linear_algebra_1.Vec3.copy(lastPosition, camera.position); } lastUpdated = t; } /** Reset object's vectors and the target vector to their initial values */ function reset() { linear_algebra_1.Vec3.copy(camera.target, target0); linear_algebra_1.Vec3.copy(camera.position, position0); linear_algebra_1.Vec3.copy(camera.up, up0); linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); linear_algebra_1.Vec3.copy(lastPosition, camera.position); } // listeners function onDrag(_a) { var x = _a.x, y = _a.y, pageX = _a.pageX, pageY = _a.pageY, buttons = _a.buttons, modifiers = _a.modifiers, isStart = _a.isStart; var isOutside = outsideViewport(x, y); if (isStart && isOutside) return; if (!isStart && !_isInteracting) return; _isInteracting = true; resetRock(); // start rocking from the center after interactions var dragRotate = binding_1.Binding.match(p.bindings.dragRotate, buttons, modifiers); var dragRotateZ = binding_1.Binding.match(p.bindings.dragRotateZ, buttons, modifiers); var dragPan = binding_1.Binding.match(p.bindings.dragPan, buttons, modifiers); var dragZoom = binding_1.Binding.match(p.bindings.dragZoom, buttons, modifiers); var dragFocus = binding_1.Binding.match(p.bindings.dragFocus, buttons, modifiers); var dragFocusZoom = binding_1.Binding.match(p.bindings.dragFocusZoom, buttons, modifiers); getMouseOnCircle(pageX, pageY); getMouseOnScreen(pageX, pageY); if (isStart) { if (dragRotate) { linear_algebra_1.Vec2.copy(_rotCurr, mouseOnCircleVec2); linear_algebra_1.Vec2.copy(_rotPrev, _rotCurr); } if (dragRotateZ) { linear_algebra_1.Vec2.copy(_zRotCurr, mouseOnCircleVec2); linear_algebra_1.Vec2.copy(_zRotPrev, _zRotCurr); } if (dragZoom || dragFocusZoom) { linear_algebra_1.Vec2.copy(_zoomStart, mouseOnScreenVec2); linear_algebra_1.Vec2.copy(_zoomEnd, _zoomStart); } if (dragFocus) { linear_algebra_1.Vec2.copy(_focusStart, mouseOnScreenVec2); linear_algebra_1.Vec2.copy(_focusEnd, _focusStart); } if (dragPan) { linear_algebra_1.Vec2.copy(_panStart, mouseOnScreenVec2); linear_algebra_1.Vec2.copy(_panEnd, _panStart); } } if (dragRotate) linear_algebra_1.Vec2.copy(_rotCurr, mouseOnCircleVec2); if (dragRotateZ) linear_algebra_1.Vec2.copy(_zRotCurr, mouseOnCircleVec2); if (dragZoom || dragFocusZoom) linear_algebra_1.Vec2.copy(_zoomEnd, mouseOnScreenVec2); if (dragFocus) linear_algebra_1.Vec2.copy(_focusEnd, mouseOnScreenVec2); if (dragFocusZoom) { var dist = linear_algebra_1.Vec3.distance(camera.state.position, camera.state.target); camera.setState({ radius: dist / 5 }); } if (dragPan) linear_algebra_1.Vec2.copy(_panEnd, mouseOnScreenVec2); } function onInteractionEnd() { _isInteracting = false; } function onWheel(_a) { var x = _a.x, y = _a.y, spinX = _a.spinX, spinY = _a.spinY, dz = _a.dz, buttons = _a.buttons, modifiers = _a.modifiers; if (outsideViewport(x, y)) return; var delta = (0, misc_1.absMax)(spinX * 0.075, spinY * 0.075, dz * 0.0001); if (delta < -p.maxWheelDelta) delta = -p.maxWheelDelta; else if (delta > p.maxWheelDelta) delta = p.maxWheelDelta; if (binding_1.Binding.match(p.bindings.scrollZoom, buttons, modifiers)) { _zoomEnd[1] += delta; } if (binding_1.Binding.match(p.bindings.scrollFocus, buttons, modifiers)) { _focusEnd[1] += delta; } } function onPinch(_a) { var fractionDelta = _a.fractionDelta, buttons = _a.buttons, modifiers = _a.modifiers; if (binding_1.Binding.match(p.bindings.scrollZoom, buttons, modifiers)) { _isInteracting = true; _zoomEnd[1] += p.gestureScaleFactor * fractionDelta; } } function onGesture(_a) { var deltaScale = _a.deltaScale; _isInteracting = true; _zoomEnd[1] += p.gestureScaleFactor * deltaScale; } function dispose() { if (disposed) return; disposed = true; dragSub.unsubscribe(); wheelSub.unsubscribe(); pinchSub.unsubscribe(); gestureSub.unsubscribe(); interactionEndSub.unsubscribe(); } var _spinSpeed = linear_algebra_1.Vec2.create(0.005, 0); function spin(deltaT) { if (p.animate.name !== 'spin' || p.animate.params.speed === 0 || _isInteracting) return; var frameSpeed = p.animate.params.speed / 1000; _spinSpeed[0] = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed; linear_algebra_1.Vec2.add(_rotCurr, _rotPrev, _spinSpeed); } var _rockPhase = 0; var _rockSpeed = linear_algebra_1.Vec2.create(0.005, 0); function rock(deltaT) { if (p.animate.name !== 'rock' || p.animate.params.speed === 0 || _isInteracting) return; var dt = deltaT / 1000 * p.animate.params.speed; var maxAngle = (0, misc_1.degToRad)(p.animate.params.angle) / getRotateFactor(); var angleA = Math.sin(_rockPhase * Math.PI * 2) * maxAngle; var angleB = Math.sin((_rockPhase + dt) * Math.PI * 2) * maxAngle; _rockSpeed[0] = angleB - angleA; linear_algebra_1.Vec2.add(_rotCurr, _rotPrev, _rockSpeed); _rockPhase += dt; if (_rockPhase >= 1) { _rockPhase = 0; } } function resetRock() { _rockPhase = 0; } function start(t) { lastUpdated = -1; update(t); } return { viewport: viewport, get isAnimating() { return p.animate.name !== 'off'; }, get props() { return p; }, setProps: function (props) { var _a; if (((_a = props.animate) === null || _a === void 0 ? void 0 : _a.name) === 'rock' && p.animate.name !== 'rock') { resetRock(); // start rocking from the center } Object.assign(p, props); }, start: start, update: update, reset: reset, dispose: dispose }; } TrackballControls.create = create; })(TrackballControls || (TrackballControls = {})); exports.TrackballControls = TrackballControls;