molstar
Version:
A comprehensive macromolecular library.
230 lines • 11.4 kB
JavaScript
import { __assign, __extends, __spreadArray } from "tslib";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { OrderedSet, SortedArray } from '../../mol-data/int';
import { StructureElement, StructureProperties, Unit } from '../../mol-model/structure';
import { FocusLoci } from '../../mol-plugin/behavior/dynamic/representation';
import { lociLabel } from '../../mol-theme/label';
import { Binding } from '../../mol-util/binding';
import { memoizeLatest } from '../../mol-util/memoize';
import { PluginUIComponent } from '../base';
import { ActionMenu } from '../controls/action-menu';
import { Button, IconButton, ToggleButton } from '../controls/common';
import { CancelOutlinedSvg, CenterFocusStrongSvg } from '../controls/icons';
function addSymmetryGroupEntries(entries, location, unitSymmetryGroup, granularity) {
var idx = SortedArray.indexOf(location.unit.elements, location.element);
var base = StructureElement.Loci(location.structure, [
{ unit: location.unit, indices: OrderedSet.ofSingleton(idx) }
]);
var extended = granularity === 'residue'
? StructureElement.Loci.extendToWholeResidues(base)
: StructureElement.Loci.extendToWholeChains(base);
var name = StructureProperties.entity.pdbx_description(location).join(', ');
for (var _i = 0, _a = unitSymmetryGroup.units; _i < _a.length; _i++) {
var u = _a[_i];
var loci = StructureElement.Loci(extended.structure, [
{ unit: u, indices: extended.elements[0].indices }
]);
var label = lociLabel(loci, { reverse: true, hidePrefix: true, htmlStyling: false, granularity: granularity });
if (!label)
label = lociLabel(loci, { hidePrefix: false, htmlStyling: false });
if (unitSymmetryGroup.units.length > 1) {
label += " | " + loci.elements[0].unit.conformation.operator.name;
}
var item = { label: label, category: name, loci: loci };
if (entries.has(name))
entries.get(name).push(item);
else
entries.set(name, [item]);
}
}
function getFocusEntries(structure) {
var entityEntries = new Map();
var l = StructureElement.Location.create(structure);
for (var _i = 0, _a = structure.unitSymmetryGroups; _i < _a.length; _i++) {
var ug = _a[_i];
if (!Unit.isAtomic(ug.units[0]))
continue;
l.unit = ug.units[0];
l.element = ug.elements[0];
var isMultiChain = Unit.Traits.is(l.unit.traits, 1 /* MultiChain */);
var entityType = StructureProperties.entity.type(l);
var isNonPolymer = entityType === 'non-polymer';
var isBranched = entityType === 'branched';
var isBirdMolecule = !!StructureProperties.entity.prd_id(l);
if (isBirdMolecule) {
addSymmetryGroupEntries(entityEntries, l, ug, 'chain');
}
else if (isNonPolymer && !isMultiChain) {
addSymmetryGroupEntries(entityEntries, l, ug, 'residue');
}
else if (isBranched || (isNonPolymer && isMultiChain)) {
var u = l.unit;
var residueIndex = u.model.atomicHierarchy.residueAtomSegments.index;
var prev = -1;
for (var i = 0, il = u.elements.length; i < il; ++i) {
var eI = u.elements[i];
var rI = residueIndex[eI];
if (rI !== prev) {
l.element = eI;
addSymmetryGroupEntries(entityEntries, l, ug, 'residue');
prev = rI;
}
}
}
}
var entries = [];
entityEntries.forEach(function (e, name) {
if (e.length === 1) {
entries.push({ label: name + ": " + e[0].label, loci: e[0].loci });
}
else if (e.length < 2000) {
entries.push.apply(entries, e);
}
});
return entries;
}
var StructureFocusControls = /** @class */ (function (_super) {
__extends(StructureFocusControls, _super);
function StructureFocusControls() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = { isBusy: false, showAction: false };
_this.getSelectionItems = memoizeLatest(function (structures) {
var _a;
var presetItems = [];
for (var _i = 0, structures_1 = structures; _i < structures_1.length; _i++) {
var s = structures_1[_i];
var d = (_a = s.cell.obj) === null || _a === void 0 ? void 0 : _a.data;
if (d) {
var entries = getFocusEntries(d);
if (entries.length > 0) {
presetItems.push(__spreadArray([
ActionMenu.Header(d.label, { description: d.label })
], ActionMenu.createItems(entries, {
label: function (f) { return f.label; },
category: function (f) { return f.category; },
description: function (f) { return f.label; }
}), true));
}
}
}
return presetItems;
});
_this.selectAction = function (item, e) {
if (!item || !_this.state.showAction) {
_this.setState({ showAction: false });
return;
}
var f = item.value;
if (e === null || e === void 0 ? void 0 : e.shiftKey) {
_this.plugin.managers.structure.focus.addFromLoci(f.loci);
}
else {
_this.plugin.managers.structure.focus.set(f);
}
_this.focusCamera();
};
_this.toggleAction = function () { return _this.setState({ showAction: !_this.state.showAction }); };
_this.focusCamera = function () {
var current = _this.plugin.managers.structure.focus.current;
if (current)
_this.plugin.managers.camera.focusLoci(current.loci);
};
_this.clear = function () {
_this.plugin.managers.structure.focus.clear();
_this.plugin.managers.camera.reset();
};
_this.highlightCurrent = function () {
var current = _this.plugin.managers.structure.focus.current;
if (current)
_this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: current.loci }, false);
};
_this.clearHighlights = function () {
_this.plugin.managers.interactivity.lociHighlights.clearHighlights();
};
return _this;
}
StructureFocusControls.prototype.componentDidMount = function () {
var _this = this;
this.subscribe(this.plugin.managers.structure.focus.behaviors.current, function (c) {
// clear the memo cache
_this.getSelectionItems([]);
_this.forceUpdate();
});
this.subscribe(this.plugin.managers.structure.focus.events.historyUpdated, function (c) {
_this.forceUpdate();
});
this.subscribe(this.plugin.behaviors.state.isBusy, function (v) {
_this.setState({ isBusy: v, showAction: false });
});
};
Object.defineProperty(StructureFocusControls.prototype, "isDisabled", {
get: function () {
return this.state.isBusy || this.actionItems.length === 0;
},
enumerable: false,
configurable: true
});
Object.defineProperty(StructureFocusControls.prototype, "actionItems", {
get: function () {
var historyItems = [];
var history = this.plugin.managers.structure.focus.history;
if (history.length > 0) {
historyItems.push(__spreadArray([
ActionMenu.Header('History', { description: 'Previously focused on items.' })
], ActionMenu.createItems(history, {
label: function (f) { return f.label; },
description: function (f) {
return f.category && f.label !== f.category
? f.category + " | " + f.label
: f.label;
}
}), true));
}
var presetItems = this.getSelectionItems(this.plugin.managers.structure.hierarchy.selection.structures);
if (presetItems.length === 1) {
var item = presetItems[0];
var header = item[0];
header.initiallyExpanded = true;
}
var items = [];
if (presetItems.length > 0)
items.push.apply(items, presetItems);
if (historyItems.length > 0)
items.push.apply(items, historyItems);
return items;
},
enumerable: false,
configurable: true
});
StructureFocusControls.prototype.getToggleBindingLabel = function () {
var _a;
var t = this.plugin.state.behaviors.transforms.get(FocusLoci.id);
if (!t)
return '';
var binding = (_a = t.params) === null || _a === void 0 ? void 0 : _a.bindings.clickFocus;
if (!binding || Binding.isEmpty(binding))
return '';
return Binding.formatTriggers(binding);
};
StructureFocusControls.prototype.render = function () {
var current = this.plugin.managers.structure.focus.current;
var label = (current === null || current === void 0 ? void 0 : current.label) || 'Nothing Focused';
var title = 'Click to Center Camera';
if (!current) {
title = 'Select focus using the menu';
var binding = this.getToggleBindingLabel();
if (binding) {
title += "\nor use '" + binding + "' on element";
}
}
return _jsxs(_Fragment, { children: [_jsxs("div", __assign({ className: 'msp-flex-row' }, { children: [_jsx(Button, __assign({ noOverflow: true, onClick: this.focusCamera, title: title, onMouseEnter: this.highlightCurrent, onMouseLeave: this.clearHighlights, disabled: this.isDisabled || !current, style: { textAlignLast: current ? 'left' : void 0 } }, { children: label }), void 0), current && _jsx(IconButton, { svg: CancelOutlinedSvg, onClick: this.clear, title: 'Clear', className: 'msp-form-control', flex: true, disabled: this.isDisabled }, void 0), _jsx(ToggleButton, { icon: CenterFocusStrongSvg, title: 'Select a focus target to center on an show its surroundings. Hold shift to focus on multiple targets.', toggle: this.toggleAction, isSelected: this.state.showAction, disabled: this.isDisabled, style: { flex: '0 0 40px', padding: 0 } }, void 0)] }), void 0), this.state.showAction && _jsx(ActionMenu, { items: this.actionItems, onSelect: this.selectAction }, void 0)] }, void 0);
};
return StructureFocusControls;
}(PluginUIComponent));
export { StructureFocusControls };
//# sourceMappingURL=focus.js.map