UNPKG

molstar

Version:

A comprehensive macromolecular library.

813 lines (812 loc) 43.3 kB
"use strict"; /** * Copyright (c) 2018-2023 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> * @author Herman Bergwerf <post@hbergwerf.nl> * * 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; const linear_algebra_1 = require("../../mol-math/linear-algebra"); const util_1 = require("../camera/util"); const input_observer_1 = require("../../mol-util/input/input-observer"); const param_definition_1 = require("../../mol-util/param-definition"); const misc_1 = require("../../mol-math/misc"); const binding_1 = require("../../mol-util/binding"); const B = input_observer_1.ButtonsType; const M = input_observer_1.ModifiersKeys; const Trigger = binding_1.Binding.Trigger; const Key = binding_1.Binding.TriggerKey; exports.DefaultTrackballBindings = { dragRotate: (0, binding_1.Binding)([Trigger(B.Flag.Primary, M.create())], 'Rotate', 'Drag using ${triggers}'), dragRotateZ: (0, binding_1.Binding)([Trigger(B.Flag.Primary, M.create({ shift: true, control: true }))], 'Rotate around z-axis (roll)', 'Drag using ${triggers}'), dragPan: (0, binding_1.Binding)([ Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true })) ], 'Pan', 'Drag using ${triggers}'), dragZoom: binding_1.Binding.Empty, dragFocus: (0, binding_1.Binding)([Trigger(B.Flag.Forth, M.create())], 'Focus', 'Drag using ${triggers}'), dragFocusZoom: (0, binding_1.Binding)([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom', 'Drag using ${triggers}'), scrollZoom: (0, binding_1.Binding)([Trigger(B.Flag.Auxilary, M.create())], 'Zoom', 'Scroll using ${triggers}'), scrollFocus: (0, binding_1.Binding)([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Clip', 'Scroll using ${triggers}'), scrollFocusZoom: binding_1.Binding.Empty, keyMoveForward: (0, binding_1.Binding)([Key('KeyW')], 'Move forward', 'Press ${triggers}'), keyMoveBack: (0, binding_1.Binding)([Key('KeyS')], 'Move back', 'Press ${triggers}'), keyMoveLeft: (0, binding_1.Binding)([Key('KeyA')], 'Move left', 'Press ${triggers}'), keyMoveRight: (0, binding_1.Binding)([Key('KeyD')], 'Move right', 'Press ${triggers}'), keyMoveUp: (0, binding_1.Binding)([Key('KeyR')], 'Move up', 'Press ${triggers}'), keyMoveDown: (0, binding_1.Binding)([Key('KeyF')], 'Move down', 'Press ${triggers}'), keyRollLeft: (0, binding_1.Binding)([Key('KeyQ')], 'Roll left', 'Press ${triggers}'), keyRollRight: (0, binding_1.Binding)([Key('KeyE')], 'Roll right', 'Press ${triggers}'), keyPitchUp: (0, binding_1.Binding)([Key('ArrowUp', M.create({ shift: true }))], 'Pitch up', 'Press ${triggers}'), keyPitchDown: (0, binding_1.Binding)([Key('ArrowDown', M.create({ shift: true }))], 'Pitch down', 'Press ${triggers}'), keyYawLeft: (0, binding_1.Binding)([Key('ArrowLeft', M.create({ shift: true }))], 'Yaw left', 'Press ${triggers}'), keyYawRight: (0, binding_1.Binding)([Key('ArrowRight', M.create({ shift: true }))], 'Yaw right', 'Press ${triggers}'), boostMove: (0, binding_1.Binding)([Key('ShiftLeft')], 'Boost move', 'Press ${triggers}'), enablePointerLock: (0, binding_1.Binding)([Key('Space', M.create({ control: true }))], 'Enable pointer lock', 'Press ${triggers}'), }; 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 }), moveSpeed: param_definition_1.ParamDefinition.Numeric(0.75, { min: 0.1, max: 3, step: 0.1 }), boostMoveFactor: param_definition_1.ParamDefinition.Numeric(5.0, { min: 0.1, max: 10, step: 0.1 }), flyMode: param_definition_1.ParamDefinition.Boolean(false), 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: 'Rotation speed in radians per second' }), }, { 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, scene, props = {}) { const p = { ...param_definition_1.ParamDefinition.getDefaultValues(exports.TrackballControlsParams), ...props, // include default bindings for backwards state compatibility bindings: { ...exports.DefaultTrackballBindings, ...props.bindings } }; const b = p.bindings; const viewport = util_1.Viewport.clone(camera.viewport); let disposed = false; const dragSub = input.drag.subscribe(onDrag); const interactionEndSub = input.interactionEnd.subscribe(onInteractionEnd); const wheelSub = input.wheel.subscribe(onWheel); const pinchSub = input.pinch.subscribe(onPinch); const keyDownSub = input.keyDown.subscribe(onKeyDown); const keyUpSub = input.keyUp.subscribe(onKeyUp); const moveSub = input.move.subscribe(onMove); const lockSub = input.lock.subscribe(onLock); const leaveSub = input.leave.subscribe(onLeave); let _isInteracting = false; // For internal use const lastPosition = (0, linear_algebra_1.Vec3)(); const _eye = (0, linear_algebra_1.Vec3)(); const _rotPrev = (0, linear_algebra_1.Vec2)(); const _rotCurr = (0, linear_algebra_1.Vec2)(); const _rotLastAxis = (0, linear_algebra_1.Vec3)(); let _rotLastAngle = 0; const _rollPrev = (0, linear_algebra_1.Vec2)(); const _rollCurr = (0, linear_algebra_1.Vec2)(); let _rollLastAngle = 0; let _pitchLastAngle = 0; let _yawLastAngle = 0; const _zoomStart = (0, linear_algebra_1.Vec2)(); const _zoomEnd = (0, linear_algebra_1.Vec2)(); const _focusStart = (0, linear_algebra_1.Vec2)(); const _focusEnd = (0, linear_algebra_1.Vec2)(); const _panStart = (0, linear_algebra_1.Vec2)(); const _panEnd = (0, linear_algebra_1.Vec2)(); // Initial values for reseting const target0 = linear_algebra_1.Vec3.clone(camera.target); const position0 = linear_algebra_1.Vec3.clone(camera.position); const up0 = linear_algebra_1.Vec3.clone(camera.up); const 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); } const 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 // viewport.width intentional ); } function getRotateFactor() { const aspectRatio = (input.width / input.height) || 1; return p.rotateSpeed * input.pixelRatio * aspectRatio; } const rotAxis = (0, linear_algebra_1.Vec3)(); const rotQuat = (0, linear_algebra_1.Quat)(); const rotEyeDir = (0, linear_algebra_1.Vec3)(); const rotObjUpDir = (0, linear_algebra_1.Vec3)(); const rotObjSideDir = (0, linear_algebra_1.Vec3)(); const rotMoveDir = (0, linear_algebra_1.Vec3)(); function rotateCamera() { const dx = _rotCurr[0] - _rotPrev[0]; const dy = _rotCurr[1] - _rotPrev[1]; linear_algebra_1.Vec3.set(rotMoveDir, dx, dy, 0); const 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); } const rollQuat = (0, linear_algebra_1.Quat)(); const rollDir = (0, linear_algebra_1.Vec3)(); function rollCamera() { const k = (keyState.rollRight - keyState.rollLeft) / 45; const dx = (_rollCurr[0] - _rollPrev[0]) * -Math.sign(_rollCurr[1]); const dy = (_rollCurr[1] - _rollPrev[1]) * -Math.sign(_rollCurr[0]); const angle = -p.rotateSpeed * (-dx + dy) + k; if (angle) { linear_algebra_1.Vec3.normalize(rollDir, _eye); linear_algebra_1.Quat.setAxisAngle(rollQuat, rollDir, angle); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, rollQuat); _rollLastAngle = angle; } else if (!p.staticMoving && _rollLastAngle) { _rollLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor); linear_algebra_1.Vec3.normalize(rollDir, _eye); linear_algebra_1.Quat.setAxisAngle(rollQuat, rollDir, _rollLastAngle); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, rollQuat); } linear_algebra_1.Vec2.copy(_rollPrev, _rollCurr); } const pitchQuat = (0, linear_algebra_1.Quat)(); const pitchDir = (0, linear_algebra_1.Vec3)(); function pitchCamera() { const m = (keyState.pitchUp - keyState.pitchDown) / (p.flyMode ? 360 : 90); const angle = -p.rotateSpeed * m; if (angle) { linear_algebra_1.Vec3.cross(pitchDir, _eye, camera.up); linear_algebra_1.Vec3.normalize(pitchDir, pitchDir); linear_algebra_1.Quat.setAxisAngle(pitchQuat, pitchDir, angle); linear_algebra_1.Vec3.transformQuat(_eye, _eye, pitchQuat); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, pitchQuat); _pitchLastAngle = angle; } else if (!p.staticMoving && _pitchLastAngle) { _pitchLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor); linear_algebra_1.Vec3.cross(pitchDir, _eye, camera.up); linear_algebra_1.Vec3.normalize(pitchDir, pitchDir); linear_algebra_1.Quat.setAxisAngle(pitchQuat, pitchDir, _pitchLastAngle); linear_algebra_1.Vec3.transformQuat(_eye, _eye, pitchQuat); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, pitchQuat); } } const yawQuat = (0, linear_algebra_1.Quat)(); const yawDir = (0, linear_algebra_1.Vec3)(); function yawCamera() { const m = (keyState.yawRight - keyState.yawLeft) / (p.flyMode ? 360 : 90); const angle = -p.rotateSpeed * m; if (angle) { linear_algebra_1.Vec3.normalize(yawDir, camera.up); linear_algebra_1.Quat.setAxisAngle(yawQuat, yawDir, angle); linear_algebra_1.Vec3.transformQuat(_eye, _eye, yawQuat); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, yawQuat); _yawLastAngle = angle; } else if (!p.staticMoving && _yawLastAngle) { _yawLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor); linear_algebra_1.Vec3.normalize(yawDir, camera.up); linear_algebra_1.Quat.setAxisAngle(yawQuat, yawDir, _yawLastAngle); linear_algebra_1.Vec3.transformQuat(_eye, _eye, yawQuat); linear_algebra_1.Vec3.transformQuat(camera.up, camera.up, yawQuat); } } function zoomCamera() { const 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() { const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed; if (factor !== 0.0) { const radius = Math.max(1, camera.state.radius + camera.state.radius * factor); camera.setState({ radius }); } if (p.staticMoving) { linear_algebra_1.Vec2.copy(_focusStart, _focusEnd); } else { _focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor; } } const panMouseChange = (0, linear_algebra_1.Vec2)(); const panObjUp = (0, linear_algebra_1.Vec3)(); const 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)) { const 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); } } } const keyState = { moveUp: 0, moveDown: 0, moveLeft: 0, moveRight: 0, moveForward: 0, moveBack: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0, boostMove: 0, }; const moveDir = (0, linear_algebra_1.Vec3)(); const moveEye = (0, linear_algebra_1.Vec3)(); function moveCamera(deltaT) { linear_algebra_1.Vec3.sub(moveEye, camera.position, camera.target); const minDistance = Math.max(camera.state.minNear, p.minDistance); linear_algebra_1.Vec3.setMagnitude(moveEye, moveEye, minDistance); const moveSpeed = deltaT * (60 / 1000) * p.moveSpeed * (keyState.boostMove === 1 ? p.boostMoveFactor : 1); if (keyState.moveForward === 1) { linear_algebra_1.Vec3.normalize(moveDir, moveEye); linear_algebra_1.Vec3.scaleAndSub(camera.position, camera.position, moveDir, moveSpeed); if (p.flyMode || input.pointerLock) { linear_algebra_1.Vec3.sub(camera.target, camera.position, moveEye); } } if (keyState.moveBack === 1) { linear_algebra_1.Vec3.normalize(moveDir, moveEye); linear_algebra_1.Vec3.scaleAndAdd(camera.position, camera.position, moveDir, moveSpeed); if (p.flyMode || input.pointerLock) { linear_algebra_1.Vec3.sub(camera.target, camera.position, moveEye); } } if (keyState.moveLeft === 1) { linear_algebra_1.Vec3.cross(moveDir, moveEye, camera.up); linear_algebra_1.Vec3.normalize(moveDir, moveDir); if (p.flyMode || input.pointerLock) { linear_algebra_1.Vec3.scaleAndAdd(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, moveEye); } else { linear_algebra_1.Vec3.scaleAndSub(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, _eye); } } if (keyState.moveRight === 1) { linear_algebra_1.Vec3.cross(moveDir, moveEye, camera.up); linear_algebra_1.Vec3.normalize(moveDir, moveDir); if (p.flyMode || input.pointerLock) { linear_algebra_1.Vec3.scaleAndSub(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, moveEye); } else { linear_algebra_1.Vec3.scaleAndAdd(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, _eye); } } if (keyState.moveUp === 1) { linear_algebra_1.Vec3.normalize(moveDir, camera.up); if (p.flyMode || input.pointerLock) { linear_algebra_1.Vec3.scaleAndAdd(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, moveEye); } else { linear_algebra_1.Vec3.scaleAndSub(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, _eye); } } if (keyState.moveDown === 1) { linear_algebra_1.Vec3.normalize(moveDir, camera.up); if (p.flyMode || input.pointerLock) { linear_algebra_1.Vec3.scaleAndSub(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, moveEye); } else { linear_algebra_1.Vec3.scaleAndAdd(camera.position, camera.position, moveDir, moveSpeed); linear_algebra_1.Vec3.sub(camera.target, camera.position, _eye); } } if (p.flyMode || input.pointerLock) { const cameraDistance = linear_algebra_1.Vec3.distance(camera.position, scene.boundingSphereVisible.center); camera.setState({ minFar: cameraDistance + scene.boundingSphereVisible.radius }); } } /** * Ensure the distance between object and target is within the min/max distance * and not too large compared to `camera.state.radiusMax` */ function checkDistances() { const 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); } let lastUpdated = -1; /** Update the object's position, direction and up vectors */ function update(t) { if (lastUpdated === t) return; const deltaT = t - lastUpdated; if (lastUpdated > 0) { if (p.animate.name === 'spin') spin(deltaT); else if (p.animate.name === 'rock') rock(deltaT); } linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); rotateCamera(); rollCamera(); pitchCamera(); yawCamera(); zoomCamera(); focusCamera(); panCamera(); linear_algebra_1.Vec3.add(camera.position, camera.target, _eye); checkDistances(); if (lastUpdated > 0) { // clamp the maximum step size at 15 frames to avoid too big jumps // TODO: make this a parameter? moveCamera(Math.min(deltaT, 15 * 1000 / 60)); } linear_algebra_1.Vec3.sub(_eye, camera.position, camera.target); 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({ x, y, pageX, pageY, buttons, modifiers, isStart }) { const isOutside = outsideViewport(x, y); if (isStart && isOutside) return; if (!isStart && !_isInteracting) return; _isInteracting = true; resetRock(); // start rocking from the center after interactions const dragRotate = binding_1.Binding.match(b.dragRotate, buttons, modifiers); const dragRotateZ = binding_1.Binding.match(b.dragRotateZ, buttons, modifiers); const dragPan = binding_1.Binding.match(b.dragPan, buttons, modifiers); const dragZoom = binding_1.Binding.match(b.dragZoom, buttons, modifiers); const dragFocus = binding_1.Binding.match(b.dragFocus, buttons, modifiers); const dragFocusZoom = binding_1.Binding.match(b.dragFocusZoom, buttons, modifiers); getMouseOnCircle(pageX, pageY); getMouseOnScreen(pageX, pageY); const pr = input.pixelRatio; const vx = (x * pr - viewport.width / 2 - viewport.x) / viewport.width; const vy = -(input.height - y * pr - viewport.height / 2 - viewport.y) / viewport.height; if (isStart) { if (dragRotate) { linear_algebra_1.Vec2.copy(_rotCurr, mouseOnCircleVec2); linear_algebra_1.Vec2.copy(_rotPrev, _rotCurr); } if (dragRotateZ) { linear_algebra_1.Vec2.set(_rollCurr, vx, vy); linear_algebra_1.Vec2.copy(_rollPrev, _rollCurr); } 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.set(_rollCurr, vx, vy); if (dragZoom || dragFocusZoom) linear_algebra_1.Vec2.copy(_zoomEnd, mouseOnScreenVec2); if (dragFocus) linear_algebra_1.Vec2.copy(_focusEnd, mouseOnScreenVec2); if (dragFocusZoom) { const 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({ x, y, spinX, spinY, dz, buttons, modifiers }) { if (outsideViewport(x, y)) return; let 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(b.scrollZoom, buttons, modifiers)) { _zoomEnd[1] += delta; } if (binding_1.Binding.match(b.scrollFocus, buttons, modifiers)) { _focusEnd[1] += delta; } } function onPinch({ isStart, startX, startY, centerPageX, centerPageY, fractionDelta, buttons, modifiers }) { if (outsideViewport(startX, startY)) return; const pan = binding_1.Binding.match(b.dragPan, buttons, modifiers); const zoom = binding_1.Binding.match(b.scrollZoom, buttons, modifiers); _isInteracting = pan || zoom; if (pan) { getMouseOnScreen(centerPageX, centerPageY); if (isStart) { linear_algebra_1.Vec2.copy(_panStart, mouseOnScreenVec2); linear_algebra_1.Vec2.copy(_panEnd, _panStart); } else { linear_algebra_1.Vec2.copy(_panEnd, mouseOnScreenVec2); } } if (zoom) { _zoomEnd[1] += p.gestureScaleFactor * fractionDelta; } } function onMove({ movementX, movementY }) { if (!input.pointerLock || movementX === undefined || movementY === undefined) return; const cx = viewport.width * 0.5 - viewport.x; const cy = viewport.height * 0.5 - viewport.y; linear_algebra_1.Vec2.copy(_rotPrev, getMouseOnCircle(cx, cy)); linear_algebra_1.Vec2.copy(_rotCurr, getMouseOnCircle(movementX + cx, movementY + cy)); } function onKeyDown({ modifiers, code, key, x, y }) { if (outsideViewport(x, y)) return; if (binding_1.Binding.matchKey(b.keyMoveForward, code, modifiers, key)) { keyState.moveForward = 1; } else if (binding_1.Binding.matchKey(b.keyMoveBack, code, modifiers, key)) { keyState.moveBack = 1; } else if (binding_1.Binding.matchKey(b.keyMoveLeft, code, modifiers, key)) { keyState.moveLeft = 1; } else if (binding_1.Binding.matchKey(b.keyMoveRight, code, modifiers, key)) { keyState.moveRight = 1; } else if (binding_1.Binding.matchKey(b.keyMoveUp, code, modifiers, key)) { keyState.moveUp = 1; } else if (binding_1.Binding.matchKey(b.keyMoveDown, code, modifiers, key)) { keyState.moveDown = 1; } else if (binding_1.Binding.matchKey(b.keyRollLeft, code, modifiers, key)) { keyState.rollLeft = 1; } else if (binding_1.Binding.matchKey(b.keyRollRight, code, modifiers, key)) { keyState.rollRight = 1; } else if (binding_1.Binding.matchKey(b.keyPitchUp, code, modifiers, key)) { keyState.pitchUp = 1; } else if (binding_1.Binding.matchKey(b.keyPitchDown, code, modifiers, key)) { keyState.pitchDown = 1; } else if (binding_1.Binding.matchKey(b.keyYawLeft, code, modifiers, key)) { keyState.yawLeft = 1; } else if (binding_1.Binding.matchKey(b.keyYawRight, code, modifiers, key)) { keyState.yawRight = 1; } if (binding_1.Binding.matchKey(b.boostMove, code, modifiers, key)) { keyState.boostMove = 1; } if (binding_1.Binding.matchKey(b.enablePointerLock, code, modifiers, key)) { input.requestPointerLock(viewport); } } function onKeyUp({ modifiers, code, key, x, y }) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; if (outsideViewport(x, y)) return; let isModifierCode = false; if (code.startsWith('Alt')) { isModifierCode = true; modifiers.alt = true; } else if (code.startsWith('Shift')) { isModifierCode = true; modifiers.shift = true; } else if (code.startsWith('Control')) { isModifierCode = true; modifiers.control = true; } else if (code.startsWith('Meta')) { isModifierCode = true; modifiers.meta = true; } const codes = []; if (isModifierCode) { if (keyState.moveForward) codes.push(((_a = b.keyMoveForward.triggers[0]) === null || _a === void 0 ? void 0 : _a.code) || ''); if (keyState.moveBack) codes.push(((_b = b.keyMoveBack.triggers[0]) === null || _b === void 0 ? void 0 : _b.code) || ''); if (keyState.moveLeft) codes.push(((_c = b.keyMoveLeft.triggers[0]) === null || _c === void 0 ? void 0 : _c.code) || ''); if (keyState.moveRight) codes.push(((_d = b.keyMoveRight.triggers[0]) === null || _d === void 0 ? void 0 : _d.code) || ''); if (keyState.moveUp) codes.push(((_e = b.keyMoveUp.triggers[0]) === null || _e === void 0 ? void 0 : _e.code) || ''); if (keyState.moveDown) codes.push(((_f = b.keyMoveDown.triggers[0]) === null || _f === void 0 ? void 0 : _f.code) || ''); if (keyState.rollLeft) codes.push(((_g = b.keyRollLeft.triggers[0]) === null || _g === void 0 ? void 0 : _g.code) || ''); if (keyState.rollRight) codes.push(((_h = b.keyRollRight.triggers[0]) === null || _h === void 0 ? void 0 : _h.code) || ''); if (keyState.pitchUp) codes.push(((_j = b.keyPitchUp.triggers[0]) === null || _j === void 0 ? void 0 : _j.code) || ''); if (keyState.pitchDown) codes.push(((_k = b.keyPitchDown.triggers[0]) === null || _k === void 0 ? void 0 : _k.code) || ''); if (keyState.yawLeft) codes.push(((_l = b.keyYawLeft.triggers[0]) === null || _l === void 0 ? void 0 : _l.code) || ''); if (keyState.yawRight) codes.push(((_m = b.keyYawRight.triggers[0]) === null || _m === void 0 ? void 0 : _m.code) || ''); } else { codes.push(code); } for (const code of codes) { if (binding_1.Binding.matchKey(b.keyMoveForward, code, modifiers, key)) { keyState.moveForward = 0; } else if (binding_1.Binding.matchKey(b.keyMoveBack, code, modifiers, key)) { keyState.moveBack = 0; } else if (binding_1.Binding.matchKey(b.keyMoveLeft, code, modifiers, key)) { keyState.moveLeft = 0; } else if (binding_1.Binding.matchKey(b.keyMoveRight, code, modifiers, key)) { keyState.moveRight = 0; } else if (binding_1.Binding.matchKey(b.keyMoveUp, code, modifiers, key)) { keyState.moveUp = 0; } else if (binding_1.Binding.matchKey(b.keyMoveDown, code, modifiers, key)) { keyState.moveDown = 0; } else if (binding_1.Binding.matchKey(b.keyRollLeft, code, modifiers, key)) { keyState.rollLeft = 0; } else if (binding_1.Binding.matchKey(b.keyRollRight, code, modifiers, key)) { keyState.rollRight = 0; } else if (binding_1.Binding.matchKey(b.keyPitchUp, code, modifiers, key)) { keyState.pitchUp = 0; } else if (binding_1.Binding.matchKey(b.keyPitchDown, code, modifiers, key)) { keyState.pitchDown = 0; } else if (binding_1.Binding.matchKey(b.keyYawLeft, code, modifiers, key)) { keyState.yawLeft = 0; } else if (binding_1.Binding.matchKey(b.keyYawRight, code, modifiers, key)) { keyState.yawRight = 0; } } if (binding_1.Binding.matchKey(b.boostMove, code, modifiers, key)) { keyState.boostMove = 0; } } function initCameraMove() { linear_algebra_1.Vec3.sub(moveEye, camera.position, camera.target); const minDistance = Math.max(camera.state.minNear, p.minDistance); linear_algebra_1.Vec3.setMagnitude(moveEye, moveEye, minDistance); linear_algebra_1.Vec3.sub(camera.target, camera.position, moveEye); const cameraDistance = linear_algebra_1.Vec3.distance(camera.position, scene.boundingSphereVisible.center); camera.setState({ minFar: cameraDistance + scene.boundingSphereVisible.radius }); } function resetCameraMove() { const { center, radius } = scene.boundingSphereVisible; const cameraDistance = linear_algebra_1.Vec3.distance(camera.position, center); if (cameraDistance > radius) { const focus = camera.getFocus(center, radius); camera.setState({ ...focus, minFar: 0 }); } else { camera.setState({ minFar: 0, radius: scene.boundingSphereVisible.radius, }); } } function onLock(isLocked) { if (isLocked) { initCameraMove(); } else { resetCameraMove(); } } function unsetKeyState() { keyState.moveForward = 0; keyState.moveBack = 0; keyState.moveLeft = 0; keyState.moveRight = 0; keyState.moveUp = 0; keyState.moveDown = 0; keyState.rollLeft = 0; keyState.rollRight = 0; keyState.pitchUp = 0; keyState.pitchDown = 0; keyState.yawLeft = 0; keyState.yawRight = 0; keyState.boostMove = 0; } function onLeave() { unsetKeyState(); } function dispose() { if (disposed) return; disposed = true; dragSub.unsubscribe(); wheelSub.unsubscribe(); pinchSub.unsubscribe(); interactionEndSub.unsubscribe(); keyDownSub.unsubscribe(); keyUpSub.unsubscribe(); moveSub.unsubscribe(); lockSub.unsubscribe(); leaveSub.unsubscribe(); } const _spinSpeed = linear_algebra_1.Vec2.create(0.005, 0); function spin(deltaT) { if (p.animate.name !== 'spin' || p.animate.params.speed === 0 || _isInteracting) return; const radPerMs = p.animate.params.speed / 1000; _spinSpeed[0] = deltaT * radPerMs / getRotateFactor(); linear_algebra_1.Vec2.add(_rotCurr, _rotPrev, _spinSpeed); } let _rockPhase = 0; const _rockSpeed = linear_algebra_1.Vec2.create(0.005, 0); function rock(deltaT) { if (p.animate.name !== 'rock' || p.animate.params.speed === 0 || _isInteracting) return; const dt = deltaT / 1000 * p.animate.params.speed; const maxAngle = (0, misc_1.degToRad)(p.animate.params.angle) / getRotateFactor(); const angleA = Math.sin(_rockPhase * Math.PI * 2) * maxAngle; const 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, get isAnimating() { return p.animate.name !== 'off'; }, get isMoving() { return (keyState.moveForward === 1 || keyState.moveBack === 1 || keyState.moveLeft === 1 || keyState.moveRight === 1 || keyState.moveUp === 1 || keyState.moveDown === 1 || keyState.rollLeft === 1 || keyState.rollRight === 1 || keyState.pitchUp === 1 || keyState.pitchDown === 1 || keyState.yawLeft === 1 || keyState.yawRight === 1); }, get props() { return p; }, setProps: (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 } if (props.flyMode !== undefined && props.flyMode !== p.flyMode) { if (props.flyMode) { initCameraMove(); } else { resetCameraMove(); } } Object.assign(p, props); Object.assign(b, props.bindings); }, start, update, reset, dispose }; } TrackballControls.create = create; })(TrackballControls || (exports.TrackballControls = TrackballControls = {}));