molstar
Version:
A comprehensive macromolecular library.
220 lines (219 loc) • 11.6 kB
JavaScript
"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' }
});