UNPKG

molstar

Version:

A comprehensive macromolecular library.

306 lines 17.7 kB
"use strict"; /** * Copyright (c) 2018-2021 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> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FocusLoci = exports.DefaultLociLabelProvider = exports.SelectLoci = exports.HighlightLoci = void 0; var tslib_1 = require("tslib"); var marker_action_1 = require("../../../mol-util/marker-action"); var objects_1 = require("../../../mol-plugin-state/objects"); var label_1 = require("../../../mol-theme/label"); var behavior_1 = require("../behavior"); var spine_1 = require("../../../mol-state/tree/spine"); var mol_state_1 = require("../../../mol-state"); var input_observer_1 = require("../../../mol-util/input/input-observer"); var binding_1 = require("../../../mol-util/binding"); var param_definition_1 = require("../../../mol-util/param-definition"); var loci_1 = require("../../../mol-model/loci"); var structure_1 = require("../../../mol-model/structure"); var array_1 = require("../../../mol-util/array"); var B = input_observer_1.ButtonsType; var M = input_observer_1.ModifiersKeys; var Trigger = binding_1.Binding.Trigger; // var DefaultHighlightLociBindings = { hoverHighlightOnly: (0, binding_1.Binding)([Trigger(0 /* None */)], 'Highlight', 'Hover element using ${triggers}'), hoverHighlightOnlyExtend: (0, binding_1.Binding)([Trigger(0 /* None */, M.create({ shift: true }))], 'Extend highlight', 'From selected to hovered element along polymer using ${triggers}'), }; var HighlightLociParams = { bindings: param_definition_1.ParamDefinition.Value(DefaultHighlightLociBindings, { isHidden: true }), ignore: param_definition_1.ParamDefinition.Value([], { isHidden: true }), preferAtoms: param_definition_1.ParamDefinition.Boolean(false, { description: 'Always prefer atoms over bonds' }), mark: param_definition_1.ParamDefinition.Boolean(true) }; exports.HighlightLoci = behavior_1.PluginBehavior.create({ name: 'representation-highlight-loci', category: 'interaction', ctor: /** @class */ (function (_super) { (0, tslib_1.__extends)(class_1, _super); function class_1() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.lociMarkProvider = function (interactionLoci, action, noRender) { if (!_this.ctx.canvas3d || !_this.params.mark) return; _this.ctx.canvas3d.mark(interactionLoci, action, noRender); }; return _this; } class_1.prototype.getLoci = function (loci) { return this.params.preferAtoms && structure_1.Bond.isLoci(loci) && loci.bonds.length === 2 ? structure_1.Bond.toFirstStructureElementLoci(loci) : loci; }; class_1.prototype.register = function () { var _this = this; this.subscribeObservable(this.ctx.behaviors.interaction.hover, function (_a) { var _b; var current = _a.current, buttons = _a.buttons, modifiers = _a.modifiers; if (!_this.ctx.canvas3d || _this.ctx.isBusy) return; var loci = _this.getLoci(current.loci); if (((_b = _this.params.ignore) === null || _b === void 0 ? void 0 : _b.indexOf(loci.kind)) >= 0) { _this.ctx.managers.interactivity.lociHighlights.highlightOnly({ repr: current.repr, loci: loci_1.EmptyLoci }); return; } var matched = false; if (binding_1.Binding.match(_this.params.bindings.hoverHighlightOnly, buttons, modifiers)) { // remove repr to highlight loci everywhere on hover _this.ctx.managers.interactivity.lociHighlights.highlightOnly({ loci: loci }); matched = true; } if (binding_1.Binding.match(_this.params.bindings.hoverHighlightOnlyExtend, buttons, modifiers)) { // remove repr to highlight loci everywhere on hover _this.ctx.managers.interactivity.lociHighlights.highlightOnlyExtend({ loci: loci }); matched = true; } if (!matched) { _this.ctx.managers.interactivity.lociHighlights.highlightOnly({ repr: current.repr, loci: loci_1.EmptyLoci }); } }); this.ctx.managers.interactivity.lociHighlights.addProvider(this.lociMarkProvider); }; class_1.prototype.unregister = function () { this.ctx.managers.interactivity.lociHighlights.removeProvider(this.lociMarkProvider); }; return class_1; }(behavior_1.PluginBehavior.Handler)), params: function () { return HighlightLociParams; }, display: { name: 'Highlight Loci on Canvas' } }); // var DefaultSelectLociBindings = { clickSelect: binding_1.Binding.Empty, clickToggleExtend: (0, binding_1.Binding)([Trigger(1 /* Primary */, M.create({ shift: true }))], 'Toggle extended selection', '${triggers} to extend selection along polymer'), clickSelectOnly: binding_1.Binding.Empty, clickToggle: (0, binding_1.Binding)([Trigger(1 /* Primary */, M.create())], 'Toggle selection', '${triggers} on element'), clickDeselect: binding_1.Binding.Empty, clickDeselectAllOnEmpty: (0, binding_1.Binding)([Trigger(1 /* Primary */, M.create())], 'Deselect all', 'Click on nothing using ${triggers}'), }; var SelectLociParams = { bindings: param_definition_1.ParamDefinition.Value(DefaultSelectLociBindings, { isHidden: true }), ignore: param_definition_1.ParamDefinition.Value([], { isHidden: true }), preferAtoms: param_definition_1.ParamDefinition.Boolean(false, { description: 'Always prefer atoms over bonds' }), mark: param_definition_1.ParamDefinition.Boolean(true) }; exports.SelectLoci = behavior_1.PluginBehavior.create({ name: 'representation-select-loci', category: 'interaction', ctor: /** @class */ (function (_super) { (0, tslib_1.__extends)(class_2, _super); function class_2(ctx, params) { var _this = _super.call(this, ctx, params) || this; _this.lociMarkProvider = function (reprLoci, action, noRender) { if (!_this.ctx.canvas3d || !_this.params.mark) return; _this.ctx.canvas3d.mark({ loci: reprLoci.loci }, action, noRender); }; _this.spine = new spine_1.StateTreeSpine.Impl(ctx.state.data.cells); return _this; } class_2.prototype.getLoci = function (loci) { return this.params.preferAtoms && structure_1.Bond.isLoci(loci) && loci.bonds.length === 2 ? structure_1.Bond.toFirstStructureElementLoci(loci) : loci; }; class_2.prototype.applySelectMark = function (ref, clear) { var cell = this.ctx.state.data.cells.get(ref); if (cell && objects_1.PluginStateObject.isRepresentation3D(cell.obj)) { this.spine.current = cell; var so = this.spine.getRootOfType(objects_1.PluginStateObject.Molecule.Structure); if (so) { if (clear) { this.lociMarkProvider({ loci: structure_1.Structure.Loci(so.data) }, marker_action_1.MarkerAction.Deselect); } var loci = this.ctx.managers.structure.selection.getLoci(so.data); this.lociMarkProvider({ loci: loci }, marker_action_1.MarkerAction.Select); } } }; class_2.prototype.register = function () { var _this = this; var lociIsEmpty = function (loci) { return loci_1.Loci.isEmpty(loci); }; var lociIsNotEmpty = function (loci) { return !loci_1.Loci.isEmpty(loci); }; var actions = [ ['clickSelect', function (current) { return _this.ctx.managers.interactivity.lociSelects.select(current); }, lociIsNotEmpty], ['clickToggle', function (current) { return _this.ctx.managers.interactivity.lociSelects.toggle(current); }, lociIsNotEmpty], ['clickToggleExtend', function (current) { return _this.ctx.managers.interactivity.lociSelects.toggleExtend(current); }, lociIsNotEmpty], ['clickSelectOnly', function (current) { return _this.ctx.managers.interactivity.lociSelects.selectOnly(current); }, lociIsNotEmpty], ['clickDeselect', function (current) { return _this.ctx.managers.interactivity.lociSelects.deselect(current); }, lociIsNotEmpty], ['clickDeselectAllOnEmpty', function () { return _this.ctx.managers.interactivity.lociSelects.deselectAll(); }, lociIsEmpty], ]; // sort the action so that the ones with more modifiers trigger sooner. actions.sort(function (a, b) { var x = _this.params.bindings[a[0]], y = _this.params.bindings[b[0]]; var k = x.triggers.length === 0 ? 0 : (0, array_1.arrayMax)(x.triggers.map(function (t) { return M.size(t.modifiers); })); var l = y.triggers.length === 0 ? 0 : (0, array_1.arrayMax)(y.triggers.map(function (t) { return M.size(t.modifiers); })); return l - k; }); this.subscribeObservable(this.ctx.behaviors.interaction.click, function (_a) { var _b; var current = _a.current, button = _a.button, modifiers = _a.modifiers; if (!_this.ctx.canvas3d || _this.ctx.isBusy || !_this.ctx.selectionMode) return; var loci = _this.getLoci(current.loci); if (((_b = _this.params.ignore) === null || _b === void 0 ? void 0 : _b.indexOf(loci.kind)) >= 0) return; // only trigger the 1st action that matches for (var _i = 0, actions_1 = actions; _i < actions_1.length; _i++) { var _c = actions_1[_i], binding = _c[0], action = _c[1], condition = _c[2]; if (binding_1.Binding.match(_this.params.bindings[binding], button, modifiers) && (!condition || condition(loci))) { action({ repr: current.repr, loci: loci }); break; } } }); this.ctx.managers.interactivity.lociSelects.addProvider(this.lociMarkProvider); this.subscribeObservable(this.ctx.state.events.object.created, function (_a) { var ref = _a.ref; return _this.applySelectMark(ref); }); // re-apply select-mark to all representation of an updated structure this.subscribeObservable(this.ctx.state.events.object.updated, function (_a) { var ref = _a.ref, obj = _a.obj, oldObj = _a.oldObj, oldData = _a.oldData, action = _a.action; var cell = _this.ctx.state.data.cells.get(ref); if (cell && objects_1.PluginStateObject.Molecule.Structure.is(cell.obj)) { var structure = obj.data; var oldStructure = action === 'recreate' ? oldObj === null || oldObj === void 0 ? void 0 : oldObj.data : action === 'in-place' ? oldData : undefined; if (oldStructure && structure_1.Structure.areEquivalent(structure, oldStructure) && structure_1.Structure.areHierarchiesEqual(structure, oldStructure)) return; var reprs = _this.ctx.state.data.select(mol_state_1.StateSelection.Generators.ofType(objects_1.PluginStateObject.Molecule.Structure.Representation3D, ref)); for (var _i = 0, reprs_1 = reprs; _i < reprs_1.length; _i++) { var repr = reprs_1[_i]; _this.applySelectMark(repr.transform.ref, true); } } }); }; class_2.prototype.unregister = function () { this.ctx.managers.interactivity.lociSelects.removeProvider(this.lociMarkProvider); }; return class_2; }(behavior_1.PluginBehavior.Handler)), params: function () { return SelectLociParams; }, display: { name: 'Select Loci on Canvas' } }); // exports.DefaultLociLabelProvider = behavior_1.PluginBehavior.create({ name: 'default-loci-label-provider', category: 'interaction', ctor: /** @class */ (function () { function class_3(ctx) { this.ctx = ctx; this.f = { label: function (loci) { var label = []; if (structure_1.StructureElement.Loci.is(loci) && loci.elements.length === 1) { var u = loci.elements[0].unit; var l = structure_1.StructureElement.Location.create(loci.structure, u, u.elements[0]); var name_1 = structure_1.StructureProperties.entity.pdbx_description(l).join(', '); label.push(name_1); } label.push((0, label_1.lociLabel)(loci)); return label.filter(function (l) { return !!l; }).join('</br>'); }, group: function (label) { return label.toString().replace(/Model [0-9]+/g, 'Models'); }, priority: 100 }; } class_3.prototype.register = function () { this.ctx.managers.lociLabels.addProvider(this.f); }; class_3.prototype.unregister = function () { this.ctx.managers.lociLabels.removeProvider(this.f); }; return class_3; }()), display: { name: 'Provide Default Loci Label' } }); // var DefaultFocusLociBindings = { clickFocus: (0, binding_1.Binding)([ Trigger(1 /* Primary */, M.create()), ], 'Representation Focus', 'Click element using ${triggers}'), clickFocusAdd: (0, binding_1.Binding)([ Trigger(1 /* Primary */, M.create({ shift: true })), ], 'Representation Focus Add', 'Click element using ${triggers}'), clickFocusSelectMode: (0, binding_1.Binding)([ // default is empty ], 'Representation Focus', 'Click element using ${triggers}'), clickFocusAddSelectMode: (0, binding_1.Binding)([ // default is empty ], 'Representation Focus Add', 'Click element using ${triggers}'), }; var FocusLociParams = { bindings: param_definition_1.ParamDefinition.Value(DefaultFocusLociBindings, { isHidden: true }), }; exports.FocusLoci = behavior_1.PluginBehavior.create({ name: 'representation-focus-loci', category: 'interaction', ctor: /** @class */ (function (_super) { (0, tslib_1.__extends)(class_4, _super); function class_4() { return _super !== null && _super.apply(this, arguments) || this; } class_4.prototype.register = function () { var _this = this; this.subscribeObservable(this.ctx.behaviors.interaction.click, function (_a) { var _b; var current = _a.current, button = _a.button, modifiers = _a.modifiers; var _c = _this.params.bindings, clickFocus = _c.clickFocus, clickFocusAdd = _c.clickFocusAdd, clickFocusSelectMode = _c.clickFocusSelectMode, clickFocusAddSelectMode = _c.clickFocusAddSelectMode; // only apply structure focus for appropriate granularity var granularity = _this.ctx.managers.interactivity.props.granularity; if (granularity !== 'residue' && granularity !== 'element') return; var binding = _this.ctx.selectionMode ? clickFocusSelectMode : clickFocus; var matched = binding_1.Binding.match(binding, button, modifiers); var bindingAdd = _this.ctx.selectionMode ? clickFocusAddSelectMode : clickFocusAdd; var matchedAdd = binding_1.Binding.match(bindingAdd, button, modifiers); if (!matched && !matchedAdd) return; var loci = loci_1.Loci.normalize(current.loci, 'residue'); var entry = _this.ctx.managers.structure.focus.current; if (entry && loci_1.Loci.areEqual(entry.loci, loci)) { _this.ctx.managers.structure.focus.clear(); } else { if (matched) { _this.ctx.managers.structure.focus.setFromLoci(loci); } else { _this.ctx.managers.structure.focus.addFromLoci(loci); // focus-add is not handled in camera behavior, doing it here var current_1 = (_b = _this.ctx.managers.structure.focus.current) === null || _b === void 0 ? void 0 : _b.loci; if (current_1) _this.ctx.managers.camera.focusLoci(current_1); } } }); }; return class_4; }(behavior_1.PluginBehavior.Handler)), params: function () { return FocusLociParams; }, display: { name: 'Representation Focus Loci on Canvas' } }); //# sourceMappingURL=representation.js.map