UNPKG

molstar

Version:

A comprehensive macromolecular library.

220 lines (219 loc) 11.6 kB
"use strict"; /** * Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Jason Pattle <jpattle.exscientia.co.uk> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CameraControls = exports.CameraAxisHelper = exports.FocusLoci = exports.DefaultFocusLociBindings = exports.DefaultClickResetCameraOnEmptySelectMode = exports.DefaultClickResetCameraOnEmpty = void 0; const loci_1 = require("../../../mol-model/loci"); const param_definition_1 = require("../../../mol-util/param-definition"); const behavior_1 = require("../behavior"); const input_observer_1 = require("../../../mol-util/input/input-observer"); const binding_1 = require("../../../mol-util/binding"); const commands_1 = require("../../commands"); const camera_helper_1 = require("../../../mol-canvas3d/helper/camera-helper"); const linear_algebra_1 = require("../../../mol-math/linear-algebra"); 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.DefaultClickResetCameraOnEmpty = (0, binding_1.Binding)([ Trigger(B.Flag.Primary, M.create()), Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true })) ], 'Reset camera focus', 'Click on nothing using ${triggers}'); exports.DefaultClickResetCameraOnEmptySelectMode = (0, binding_1.Binding)([ Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true })) ], 'Reset camera focus (Selection Mode)', 'Click on nothing using ${triggers}'); exports.DefaultFocusLociBindings = { clickCenterFocus: (0, binding_1.Binding)([ Trigger(B.Flag.Primary, M.create()), Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true })) ], 'Camera center and focus', 'Click element using ${triggers}'), clickCenterFocusSelectMode: (0, binding_1.Binding)([ Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true })) ], 'Camera center and focus (Selection Mode)', 'Click element using ${triggers}'), clickResetCameraOnEmpty: exports.DefaultClickResetCameraOnEmpty, clickResetCameraOnEmptySelectMode: exports.DefaultClickResetCameraOnEmptySelectMode, }; const FocusLociParams = { minRadius: param_definition_1.ParamDefinition.Numeric(8, { min: 1, max: 50, step: 1 }), extraRadius: param_definition_1.ParamDefinition.Numeric(4, { min: 1, max: 50, step: 1 }, { description: 'Value added to the bounding-sphere radius of the Loci' }), durationMs: param_definition_1.ParamDefinition.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'Camera transition duration' }), bindings: param_definition_1.ParamDefinition.Value(exports.DefaultFocusLociBindings, { isHidden: true }), }; exports.FocusLoci = behavior_1.PluginBehavior.create({ name: 'camera-focus-loci', category: 'interaction', ctor: class extends behavior_1.PluginBehavior.Handler { register() { this.subscribeObservable(this.ctx.behaviors.interaction.click, ({ current, button, modifiers }) => { var _a, _b; if (!this.ctx.canvas3d) return; const binding = this.ctx.selectionMode ? this.params.bindings.clickCenterFocusSelectMode : this.params.bindings.clickCenterFocus; const resetBinding = this.ctx.selectionMode ? ((_a = this.params.bindings.clickResetCameraOnEmptySelectMode) !== null && _a !== void 0 ? _a : exports.DefaultClickResetCameraOnEmptySelectMode) : ((_b = this.params.bindings.clickResetCameraOnEmpty) !== null && _b !== void 0 ? _b : exports.DefaultClickResetCameraOnEmpty); if (loci_1.Loci.isEmpty(current.loci) && binding_1.Binding.match(resetBinding, button, modifiers)) { commands_1.PluginCommands.Camera.Reset(this.ctx, {}); return; } if (binding_1.Binding.match(binding, button, modifiers)) { const loci = loci_1.Loci.normalize(current.loci, this.ctx.managers.interactivity.props.granularity); this.ctx.managers.camera.focusLoci(loci, this.params); } }); } }, params: () => FocusLociParams, display: { name: 'Camera Focus Loci on Canvas' } }); exports.CameraAxisHelper = behavior_1.PluginBehavior.create({ name: 'camera-axis-helper', category: 'interaction', ctor: class extends behavior_1.PluginBehavior.Handler { register() { let lastPlane = camera_helper_1.CameraHelperAxis.None; let state = 0; this.subscribeObservable(this.ctx.behaviors.interaction.click, ({ current }) => { if (!this.ctx.canvas3d || !(0, camera_helper_1.isCameraAxesLoci)(current.loci)) return; const axis = current.loci.elements[0].groupId; if (axis === camera_helper_1.CameraHelperAxis.None) { lastPlane = camera_helper_1.CameraHelperAxis.None; state = 0; return; } const { camera } = this.ctx.canvas3d; let dir, up; if (axis >= camera_helper_1.CameraHelperAxis.X && axis <= camera_helper_1.CameraHelperAxis.Z) { lastPlane = camera_helper_1.CameraHelperAxis.None; state = 0; const d = linear_algebra_1.Vec3.sub((0, linear_algebra_1.Vec3)(), camera.target, camera.position); const c = linear_algebra_1.Vec3.cross((0, linear_algebra_1.Vec3)(), d, camera.up); up = (0, linear_algebra_1.Vec3)(); up[axis - 1] = 1; dir = linear_algebra_1.Vec3.cross((0, linear_algebra_1.Vec3)(), up, c); if (linear_algebra_1.Vec3.magnitude(dir) === 0) dir = d; } else { if (lastPlane === axis) { state = (state + 1) % 2; } else { lastPlane = axis; state = 0; } if (axis === camera_helper_1.CameraHelperAxis.XY) { up = state ? linear_algebra_1.Vec3.unitX : linear_algebra_1.Vec3.unitY; dir = linear_algebra_1.Vec3.negUnitZ; } else if (axis === camera_helper_1.CameraHelperAxis.XZ) { up = state ? linear_algebra_1.Vec3.unitX : linear_algebra_1.Vec3.unitZ; dir = linear_algebra_1.Vec3.negUnitY; } else { up = state ? linear_algebra_1.Vec3.unitY : linear_algebra_1.Vec3.unitZ; dir = linear_algebra_1.Vec3.negUnitX; } } this.ctx.canvas3d.requestCameraReset({ snapshot: (scene, camera) => camera.getInvariantFocus(scene.boundingSphereVisible.center, scene.boundingSphereVisible.radius, up, dir) }); }); } }, params: () => ({}), display: { name: 'Camera Axis Helper' } }); const DefaultCameraControlsBindings = { keySpinAnimation: (0, binding_1.Binding)([Key('I')], 'Spin Animation', 'Press ${triggers}'), keyRockAnimation: (0, binding_1.Binding)([Key('O')], 'Rock Animation', 'Press ${triggers}'), keyToggleFlyMode: (0, binding_1.Binding)([Key('Space', M.create({ shift: true }))], 'Toggle Fly Mode', 'Press ${triggers}'), keyResetView: (0, binding_1.Binding)([Key('T')], 'Reset View', 'Press ${triggers}'), keyGlobalIllumination: (0, binding_1.Binding)([Key('G')], 'Global Illumination', 'Press ${triggers}'), }; const CameraControlsParams = { bindings: param_definition_1.ParamDefinition.Value(DefaultCameraControlsBindings, { isHidden: true }), }; exports.CameraControls = behavior_1.PluginBehavior.create({ name: 'camera-controls', category: 'interaction', ctor: class extends behavior_1.PluginBehavior.Handler { register() { this.subscribeObservable(this.ctx.behaviors.interaction.key, ({ code, key, modifiers }) => { var _a; if (!this.ctx.canvas3d) return; // include defaults for backwards state compatibility const b = { ...DefaultCameraControlsBindings, ...this.params.bindings }; const tp = this.ctx.canvas3d.props.trackball; const ip = this.ctx.canvas3d.props.illumination; if (binding_1.Binding.matchKey(b.keySpinAnimation, code, modifiers, key)) { const name = tp.animate.name !== 'spin' ? 'spin' : 'off'; if (name === 'off') { this.ctx.canvas3d.setProps({ trackball: { animate: { name, params: {} } } }); } else { this.ctx.canvas3d.setProps({ trackball: { animate: { name, params: { speed: 1 } } } }); } } if (binding_1.Binding.matchKey(b.keyRockAnimation, code, modifiers, key)) { const name = tp.animate.name !== 'rock' ? 'rock' : 'off'; if (name === 'off') { this.ctx.canvas3d.setProps({ trackball: { animate: { name, params: {} } } }); } else { this.ctx.canvas3d.setProps({ trackball: { animate: { name, params: { speed: 0.3, angle: 10 } } } }); } } if (binding_1.Binding.matchKey(b.keyToggleFlyMode, code, modifiers, key)) { const flyMode = !tp.flyMode; this.ctx.canvas3d.setProps({ trackball: { flyMode } }); if ((_a = this.ctx.canvas3dContext) === null || _a === void 0 ? void 0 : _a.canvas) { this.ctx.canvas3dContext.canvas.style.cursor = flyMode ? 'crosshair' : 'unset'; } } if (binding_1.Binding.matchKey(b.keyResetView, code, modifiers, key)) { commands_1.PluginCommands.Camera.Reset(this.ctx, {}); } if (binding_1.Binding.matchKey(b.keyGlobalIllumination, code, modifiers, key)) { this.ctx.canvas3d.setProps({ illumination: { ...ip, enabled: !ip.enabled, } }); } }); } }, params: () => CameraControlsParams, display: { name: 'Camera Controls on Canvas' } });