123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- /**
- * Copyright (c) 2019-2020 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 { EmptyLoci, isEmptyLoci, Loci } from '../../../../mol-model/loci';
- import { Bond, Structure, StructureElement } from '../../../../mol-model/structure';
- import { createStructureRepresentationParams } from '../../../../mol-plugin-state/helpers/structure-representation-params';
- import { PluginStateObject } from '../../../../mol-plugin-state/objects';
- import { StateTransforms } from '../../../../mol-plugin-state/transforms';
- import { PluginBehavior } from '../../../../mol-plugin/behavior';
- import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
- import { StateObjectCell, StateSelection, StateTransform } from '../../../../mol-state';
- import { BuiltInSizeThemes } from '../../../../mol-theme/size';
- import { Binding } from '../../../../mol-util/binding';
- import { ButtonsType, ModifiersKeys } from '../../../../mol-util/input/input-observer';
- import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
- import { PluginCommands } from '../../../commands';
- const B = ButtonsType
- const M = ModifiersKeys
- const Trigger = Binding.Trigger
- const DefaultStructureRepresentationInteractionBindings = {
- clickInteractionAroundOnly: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Show the structure interaction around only the clicked element using ${triggers}.'),
- }
- const StructureRepresentationInteractionParams = {
- bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }),
- }
- type StructureRepresentationInteractionProps = PD.Values<typeof StructureRepresentationInteractionParams>
- export enum StructureRepresentationInteractionTags {
- Group = 'structure-interaction-group',
- ResidueSel = 'structure-interaction-residue-sel',
- ResidueRepr = 'structure-interaction-residue-repr',
- SurrSel = 'structure-interaction-surr-sel',
- SurrRepr = 'structure-interaction-surr-repr',
- SurrNciRepr = 'structure-interaction-surr-nci-repr'
- }
- const TagSet: Set<StructureRepresentationInteractionTags> = new Set([StructureRepresentationInteractionTags.Group, StructureRepresentationInteractionTags.ResidueSel, StructureRepresentationInteractionTags.ResidueRepr, StructureRepresentationInteractionTags.SurrSel, StructureRepresentationInteractionTags.SurrRepr, StructureRepresentationInteractionTags.SurrNciRepr])
- export class StructureRepresentationInteractionBehavior extends PluginBehavior.WithSubscribers<StructureRepresentationInteractionProps> {
- private createResVisualParams(s: Structure) {
- return createStructureRepresentationParams(this.plugin, s, {
- type: 'ball-and-stick',
- size: 'uniform'
- });
- }
- private createSurVisualParams(s: Structure) {
- return createStructureRepresentationParams(this.plugin, s, {
- type: 'ball-and-stick',
- color: 'element-symbol',
- size: 'uniform'
- });
- }
- private createSurNciVisualParams(s: Structure) {
- return createStructureRepresentationParams(this.plugin, s, {
- type: InteractionsRepresentationProvider,
- color: InteractionTypeColorThemeProvider,
- size: BuiltInSizeThemes.uniform
- });
- }
- private ensureShape(cell: StateObjectCell<PluginStateObject.Molecule.Structure>) {
- const state = this.plugin.state.dataState, tree = state.tree;
- const builder = state.build();
- const refs = StateSelection.findUniqueTagsInSubtree(tree, cell.transform.ref, TagSet);
- if (!refs['structure-interaction-group']) {
- refs['structure-interaction-group'] = builder.to(cell).group(StateTransforms.Misc.CreateGroup,
- { label: 'Current Focus' }, { tags: StructureRepresentationInteractionTags.Group }).ref;
- }
- // Selections
- if (!refs[StructureRepresentationInteractionTags.ResidueSel]) {
- refs[StructureRepresentationInteractionTags.ResidueSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromBundle,
- { bundle: { } as any, label: 'Residue' }, { tags: StructureRepresentationInteractionTags.ResidueSel }).ref;
- }
- if (!refs[StructureRepresentationInteractionTags.SurrSel]) {
- refs[StructureRepresentationInteractionTags.SurrSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromExpression,
- { expression: { } as any, label: 'Surroundings' }, { tags: StructureRepresentationInteractionTags.SurrSel }).ref;
- }
- // Representations
- // TODO: ability to customize how it looks in the behavior params
- if (!refs[StructureRepresentationInteractionTags.ResidueRepr]) {
- refs[StructureRepresentationInteractionTags.ResidueRepr] = builder.to(refs['structure-interaction-residue-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
- this.createResVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.ResidueRepr }).ref;
- }
- if (!refs[StructureRepresentationInteractionTags.SurrRepr]) {
- refs[StructureRepresentationInteractionTags.SurrRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
- this.createSurVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.SurrRepr }).ref;
- }
- if (!refs[StructureRepresentationInteractionTags.SurrNciRepr]) {
- refs[StructureRepresentationInteractionTags.SurrNciRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
- this.createSurNciVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.SurrNciRepr }).ref;
- }
- return { state, builder, refs };
- }
- private clear(root: StateTransform.Ref) {
- const state = this.plugin.state.dataState;
- const groups = state.select(StateSelection.Generators.byRef(root).subtree().withTag(StructureRepresentationInteractionTags.Group));
- if (groups.length === 0) return;
- const update = state.build();
- const bundle = StructureElement.Bundle.Empty;
- const expression = MS.struct.generator.empty();
- for (const g of groups) {
- // TODO: update props of the group node to ghost
- const res = StateSelection.findTagInSubtree(state.tree, g.transform.ref, StructureRepresentationInteractionTags.ResidueSel);
- const surr = StateSelection.findTagInSubtree(state.tree, g.transform.ref, StructureRepresentationInteractionTags.SurrSel);
- if (res) update.to(res).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle }));
- if (surr) update.to(surr).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression }));
- }
- PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
- }
- register(ref: string): void {
- let lastLoci: Loci = EmptyLoci;
- this.subscribeObservable(this.plugin.events.state.object.removed, o => {
- if (!PluginStateObject.Molecule.Structure.is(o.obj) || !StructureElement.Loci.is(lastLoci)) return;
- if (lastLoci.structure === o.obj.data) {
- lastLoci = EmptyLoci;
- }
- });
- this.subscribeObservable(this.plugin.events.state.object.updated, o => {
- if (!PluginStateObject.Molecule.Structure.is(o.oldObj) || !StructureElement.Loci.is(lastLoci)) return;
- if (lastLoci.structure === o.oldObj.data) {
- lastLoci = EmptyLoci;
- }
- });
- this.subscribeObservable(this.plugin.behaviors.interaction.click, ({ current, button, modifiers }) => {
- const { clickInteractionAroundOnly } = this.params.bindings
- if (Binding.match(clickInteractionAroundOnly, button, modifiers)) {
- if (isEmptyLoci(current.loci)) {
- this.clear(StateTransform.RootRef);
- lastLoci = current.loci;
- return;
- }
- let loci: StructureElement.Loci;
- if (StructureElement.Loci.is(current.loci)) {
- loci = current.loci;
- } else if (Bond.isLoci(current.loci)) {
- loci = Bond.toStructureElementLoci(current.loci);
- } else if (Structure.isLoci(current.loci)) {
- loci = Structure.toStructureElementLoci(current.loci.structure);
- } else {
- return;
- }
- if (StructureElement.Loci.isEmpty(loci)) return;
- const parent = this.plugin.helpers.substructureParent.get(loci.structure);
- if (!parent || !parent.obj) return;
- if (Loci.areEqual(lastLoci, loci)) {
- lastLoci = EmptyLoci;
- this.clear(parent.transform.ref);
- return;
- }
- lastLoci = loci;
- const residueLoci = StructureElement.Loci.extendToWholeResidues(StructureElement.Loci.remap(loci, parent.obj!.data))
- const residueBundle = StructureElement.Bundle.fromLoci(residueLoci)
- const surroundings = MS.struct.modifier.includeSurroundings({
- 0: StructureElement.Bundle.toExpression(residueBundle),
- radius: 5,
- 'as-whole-residues': true
- });
- const { state, builder, refs } = this.ensureShape(parent);
- builder.to(refs[StructureRepresentationInteractionTags.ResidueSel]!).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle: residueBundle }));
- builder.to(refs[StructureRepresentationInteractionTags.SurrSel]!).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression: surroundings }));
- PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
- }
- });
- }
- async update(params: StructureRepresentationInteractionProps) {
- return false;
- }
- }
- export const StructureRepresentationInteraction = PluginBehavior.create({
- name: 'create-structure-representation-interaction',
- display: { name: 'Structure Representation Interaction' },
- category: 'interaction',
- ctor: StructureRepresentationInteractionBehavior,
- params: () => StructureRepresentationInteractionParams
- });
|