UNPKG

molstar

Version:

A comprehensive macromolecular library.

220 lines (219 loc) 12.5 kB
/** * Copyright (c) 2019-2022 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> */ import { InteractionsRepresentationProvider } from '../../../../mol-model-props/computed/representations/interactions'; import { InteractionTypeColorThemeProvider } from '../../../../mol-model-props/computed/themes/interaction-type'; import { StructureElement } from '../../../../mol-model/structure'; import { createStructureRepresentationParams } from '../../../../mol-plugin-state/helpers/structure-representation-params'; import { StateTransforms } from '../../../../mol-plugin-state/transforms'; import { PluginBehavior } from '../../../behavior'; import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder'; import { StateSelection, StateTransform } from '../../../../mol-state'; import { SizeTheme } from '../../../../mol-theme/size'; import { ParamDefinition as PD } from '../../../../mol-util/param-definition'; import { PluginCommands } from '../../../commands'; import { Material } from '../../../../mol-util/material'; import { Clip } from '../../../../mol-util/clip'; const StructureFocusRepresentationParams = (plugin) => { const reprParams = StateTransforms.Representation.StructureRepresentation3D.definition.params(void 0, plugin); return { expandRadius: PD.Numeric(5, { min: 1, max: 10, step: 1 }), targetParams: PD.Group(reprParams, { label: 'Target', customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', size: 'physical', typeParams: { sizeFactor: 0.22, sizeAspectRatio: 0.73, adjustCylinderLength: true, xrayShaded: true, aromaticBonds: false, multipleBonds: 'off', excludeTypes: ['hydrogen-bond', 'metal-coordination'] }, }) }), surroundingsParams: PD.Group(reprParams, { label: 'Surroundings', customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', size: 'physical', typeParams: { sizeFactor: 0.16, excludeTypes: ['hydrogen-bond', 'metal-coordination'] } }) }), nciParams: PD.Group(reprParams, { label: 'Non-covalent Int.', customDefault: createStructureRepresentationParams(plugin, void 0, { type: InteractionsRepresentationProvider, color: InteractionTypeColorThemeProvider, size: SizeTheme.BuiltIn.uniform }) }), components: PD.MultiSelect(FocusComponents, PD.arrayToOptions(FocusComponents)), excludeTargetFromSurroundings: PD.Boolean(false, { label: 'Exclude Target', description: 'Exclude the focus "target" from the surroudings component.' }), ignoreHydrogens: PD.Boolean(false), ignoreHydrogensVariant: PD.Select('all', PD.arrayToOptions(['all', 'non-polar'])), ignoreLight: PD.Boolean(false), material: Material.getParam(), clip: PD.Group(Clip.Params), }; }; const FocusComponents = ['target', 'surroundings', 'interactions']; export var StructureFocusRepresentationTags; (function (StructureFocusRepresentationTags) { StructureFocusRepresentationTags["TargetSel"] = "structure-focus-target-sel"; StructureFocusRepresentationTags["TargetRepr"] = "structure-focus-target-repr"; StructureFocusRepresentationTags["SurrSel"] = "structure-focus-surr-sel"; StructureFocusRepresentationTags["SurrRepr"] = "structure-focus-surr-repr"; StructureFocusRepresentationTags["SurrNciRepr"] = "structure-focus-surr-nci-repr"; })(StructureFocusRepresentationTags || (StructureFocusRepresentationTags = {})); const TagSet = new Set([StructureFocusRepresentationTags.TargetSel, StructureFocusRepresentationTags.TargetRepr, StructureFocusRepresentationTags.SurrSel, StructureFocusRepresentationTags.SurrRepr, StructureFocusRepresentationTags.SurrNciRepr]); class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscribers { constructor() { super(...arguments); this.currentSource = void 0; } get surrLabel() { return `[Focus] Surroundings (${this.params.expandRadius} Å)`; } getReprParams(reprParams) { return { ...reprParams, type: { name: reprParams.type.name, params: { ...reprParams.type.params, ignoreHydrogens: this.params.ignoreHydrogens, ignoreHydrogensVariant: this.params.ignoreHydrogensVariant, ignoreLight: this.params.ignoreLight, material: this.params.material, clip: this.params.clip } } }; } ensureShape(cell) { var _a; const state = this.plugin.state.data, tree = state.tree; const builder = state.build(); const refs = StateSelection.findUniqueTagsInSubtree(tree, cell.transform.ref, TagSet); // Selections if (!refs[StructureFocusRepresentationTags.TargetSel]) { refs[StructureFocusRepresentationTags.TargetSel] = builder .to(cell) .apply(StateTransforms.Model.StructureSelectionFromBundle, { bundle: StructureElement.Bundle.Empty, label: '[Focus] Target' }, { tags: StructureFocusRepresentationTags.TargetSel }).ref; } if (!refs[StructureFocusRepresentationTags.SurrSel]) { refs[StructureFocusRepresentationTags.SurrSel] = builder .to(cell) .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.generator.empty(), label: this.surrLabel }, { tags: StructureFocusRepresentationTags.SurrSel }).ref; } const components = this.params.components; // Representations if (components.indexOf('target') >= 0 && !refs[StructureFocusRepresentationTags.TargetRepr]) { refs[StructureFocusRepresentationTags.TargetRepr] = builder .to(refs[StructureFocusRepresentationTags.TargetSel]) .apply(StateTransforms.Representation.StructureRepresentation3D, this.getReprParams(this.params.targetParams), { tags: StructureFocusRepresentationTags.TargetRepr }).ref; } if (components.indexOf('surroundings') >= 0 && !refs[StructureFocusRepresentationTags.SurrRepr]) { refs[StructureFocusRepresentationTags.SurrRepr] = builder .to(refs[StructureFocusRepresentationTags.SurrSel]) .apply(StateTransforms.Representation.StructureRepresentation3D, this.getReprParams(this.params.surroundingsParams), { tags: StructureFocusRepresentationTags.SurrRepr }).ref; } if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr] && cell.obj && InteractionsRepresentationProvider.isApplicable((_a = cell.obj) === null || _a === void 0 ? void 0 : _a.data)) { refs[StructureFocusRepresentationTags.SurrNciRepr] = builder .to(refs[StructureFocusRepresentationTags.SurrSel]) .apply(StateTransforms.Representation.StructureRepresentation3D, this.getReprParams(this.params.nciParams), { tags: StructureFocusRepresentationTags.SurrNciRepr }).ref; } return { state, builder, refs }; } clear(root) { const state = this.plugin.state.data; this.currentSource = void 0; const foci = state.select(StateSelection.Generators.byRef(root).subtree().withTag(StructureFocusRepresentationTags.TargetSel)); const surrs = state.select(StateSelection.Generators.byRef(root).subtree().withTag(StructureFocusRepresentationTags.SurrSel)); if (foci.length === 0 && surrs.length === 0) return; const update = state.build(); const bundle = StructureElement.Bundle.Empty; for (const f of foci) { update.to(f).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle })); } const expression = MS.struct.generator.empty(); for (const s of surrs) { update.to(s).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression })); } return PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true, doNotUpdateCurrent: true } }); } async focus(sourceLoci) { const parent = this.plugin.helpers.substructureParent.get(sourceLoci.structure); if (!parent || !parent.obj) return; this.currentSource = sourceLoci; const loci = StructureElement.Loci.remap(sourceLoci, parent.obj.data); const residueLoci = StructureElement.Loci.extendToWholeResidues(loci); const residueBundle = StructureElement.Bundle.fromLoci(residueLoci); const target = StructureElement.Bundle.toExpression(residueBundle); let surroundings = MS.struct.modifier.includeSurroundings({ 0: target, radius: this.params.expandRadius, 'as-whole-residues': true }); if (this.params.excludeTargetFromSurroundings) { surroundings = MS.struct.modifier.exceptBy({ 0: surroundings, by: target }); } const { state, builder, refs } = this.ensureShape(parent); builder.to(refs[StructureFocusRepresentationTags.TargetSel]).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle: residueBundle })); builder.to(refs[StructureFocusRepresentationTags.SurrSel]).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression: surroundings, label: this.surrLabel })); await PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } }); } register(ref) { this.subscribeObservable(this.plugin.managers.structure.focus.behaviors.current, (entry) => { if (entry) this.focus(entry.loci); else this.clear(StateTransform.RootRef); }); } async update(params) { const old = this.params; this.params = params; if (old.excludeTargetFromSurroundings !== params.excludeTargetFromSurroundings) { if (this.currentSource) { this.focus(this.currentSource); } return true; } const state = this.plugin.state.data; const builder = state.build(); const all = StateSelection.Generators.root.subtree(); const components = this.params.components; // TODO: create component if previously didnt exist let hasComponent = components.indexOf('target') >= 0; for (const repr of state.select(all.withTag(StructureFocusRepresentationTags.TargetRepr))) { if (!hasComponent) builder.delete(repr.transform.ref); else builder.to(repr).update(this.getReprParams(this.params.targetParams)); } hasComponent = components.indexOf('surroundings') >= 0; for (const repr of state.select(all.withTag(StructureFocusRepresentationTags.SurrRepr))) { if (!hasComponent) builder.delete(repr.transform.ref); else builder.to(repr).update(this.getReprParams(this.params.surroundingsParams)); } hasComponent = components.indexOf('interactions') >= 0; for (const repr of state.select(all.withTag(StructureFocusRepresentationTags.SurrNciRepr))) { if (!hasComponent) builder.delete(repr.transform.ref); else builder.to(repr).update(this.getReprParams(this.params.nciParams)); } await PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } }); if (params.expandRadius !== old.expandRadius) { if (this.currentSource) { this.focus(this.currentSource); } return true; } return true; } } export const StructureFocusRepresentation = PluginBehavior.create({ name: 'create-structure-focus-representation', display: { name: 'Structure Focus Representation' }, category: 'interaction', ctor: StructureFocusRepresentationBehavior, params: (_, plugin) => StructureFocusRepresentationParams(plugin) });