structure-focus-representation.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /**
  2. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { InteractionsRepresentationProvider } from '../../../../mol-model-props/computed/representations/interactions';
  8. import { InteractionTypeColorThemeProvider } from '../../../../mol-model-props/computed/themes/interaction-type';
  9. import { StructureElement } from '../../../../mol-model/structure';
  10. import { createStructureRepresentationParams } from '../../../../mol-plugin-state/helpers/structure-representation-params';
  11. import { PluginStateObject } from '../../../../mol-plugin-state/objects';
  12. import { StateTransforms } from '../../../../mol-plugin-state/transforms';
  13. import { PluginBehavior } from '../../../behavior';
  14. import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
  15. import { StateObjectCell, StateSelection, StateTransform } from '../../../../mol-state';
  16. import { SizeTheme } from '../../../../mol-theme/size';
  17. import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
  18. import { PluginCommands } from '../../../commands';
  19. import { PluginContext } from '../../../context';
  20. const StructureFocusRepresentationParams = (plugin: PluginContext) => {
  21. const reprParams = StateTransforms.Representation.StructureRepresentation3D.definition.params!(void 0, plugin) as PD.Params;
  22. return {
  23. // TODO: min = 0 to turn them off?
  24. expandRadius: PD.Numeric(5, { min: 1, max: 10, step: 1 }),
  25. targetParams: PD.Group(reprParams, {
  26. label: 'Target',
  27. customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', size: 'uniform' })
  28. }),
  29. surroundingsParams: PD.Group(reprParams, {
  30. label: 'Surroundings',
  31. customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', color: 'element-symbol', size: 'uniform' })
  32. }),
  33. nciParams: PD.Group(reprParams, {
  34. label: 'Non-covalent Int.',
  35. customDefault: createStructureRepresentationParams(plugin, void 0, {
  36. type: InteractionsRepresentationProvider,
  37. color: InteractionTypeColorThemeProvider,
  38. size: SizeTheme.BuiltIn.uniform
  39. })
  40. })
  41. };
  42. }
  43. type StructureFocusRepresentationProps = PD.ValuesFor<ReturnType<typeof StructureFocusRepresentationParams>>
  44. export enum StructureFocusRepresentationTags {
  45. TargetSel = 'structure-focus-target-sel',
  46. TargetRepr = 'structure-focus-target-repr',
  47. SurrSel = 'structure-focus-surr-sel',
  48. SurrRepr = 'structure-focus-surr-repr',
  49. SurrNciRepr = 'structure-focus-surr-nci-repr'
  50. }
  51. const TagSet: Set<StructureFocusRepresentationTags> = new Set([StructureFocusRepresentationTags.TargetSel, StructureFocusRepresentationTags.TargetRepr, StructureFocusRepresentationTags.SurrSel, StructureFocusRepresentationTags.SurrRepr, StructureFocusRepresentationTags.SurrNciRepr])
  52. export class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscribers<StructureFocusRepresentationProps> {
  53. private get surrLabel() { return `[Focus] Surroundings (${this.params.expandRadius} Å)`; }
  54. private ensureShape(cell: StateObjectCell<PluginStateObject.Molecule.Structure>) {
  55. const state = this.plugin.state.data, tree = state.tree;
  56. const builder = state.build();
  57. const refs = StateSelection.findUniqueTagsInSubtree(tree, cell.transform.ref, TagSet);
  58. // Selections
  59. if (!refs[StructureFocusRepresentationTags.TargetSel]) {
  60. refs[StructureFocusRepresentationTags.TargetSel] = builder
  61. .to(cell)
  62. .apply(StateTransforms.Model.StructureSelectionFromBundle,
  63. { bundle: StructureElement.Bundle.Empty, label: '[Focus] Target' }, { tags: StructureFocusRepresentationTags.TargetSel }).ref;
  64. }
  65. if (!refs[StructureFocusRepresentationTags.SurrSel]) {
  66. refs[StructureFocusRepresentationTags.SurrSel] = builder
  67. .to(cell)
  68. .apply(StateTransforms.Model.StructureSelectionFromExpression,
  69. { expression: MS.struct.generator.empty(), label: this.surrLabel }, { tags: StructureFocusRepresentationTags.SurrSel }).ref;
  70. }
  71. // Representations
  72. if (!refs[StructureFocusRepresentationTags.TargetRepr]) {
  73. refs[StructureFocusRepresentationTags.TargetRepr] = builder
  74. .to(refs[StructureFocusRepresentationTags.TargetSel]!)
  75. .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.targetParams, { tags: StructureFocusRepresentationTags.TargetRepr }).ref;
  76. }
  77. if (!refs[StructureFocusRepresentationTags.SurrRepr]) {
  78. refs[StructureFocusRepresentationTags.SurrRepr] = builder
  79. .to(refs[StructureFocusRepresentationTags.SurrSel]!)
  80. .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.surroundingsParams, { tags: StructureFocusRepresentationTags.SurrRepr }).ref;
  81. }
  82. if (!refs[StructureFocusRepresentationTags.SurrNciRepr]) {
  83. refs[StructureFocusRepresentationTags.SurrNciRepr] = builder
  84. .to(refs[StructureFocusRepresentationTags.SurrSel]!)
  85. .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.nciParams, { tags: StructureFocusRepresentationTags.SurrNciRepr }).ref;
  86. }
  87. return { state, builder, refs };
  88. }
  89. private clear(root: StateTransform.Ref) {
  90. const state = this.plugin.state.data;
  91. const foci = state.select(StateSelection.Generators.byRef(root).subtree().withTag(StructureFocusRepresentationTags.TargetSel));
  92. const surrs = state.select(StateSelection.Generators.byRef(root).subtree().withTag(StructureFocusRepresentationTags.SurrSel));
  93. if (foci.length === 0 && surrs.length === 0) return;
  94. const update = state.build();
  95. const bundle = StructureElement.Bundle.Empty;
  96. for (const f of foci) {
  97. update.to(f).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle }));
  98. }
  99. const expression = MS.struct.generator.empty();
  100. for (const s of surrs) {
  101. update.to(s).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression }));
  102. }
  103. return PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
  104. }
  105. private async focus(loci: StructureElement.Loci) {
  106. const parent = this.plugin.helpers.substructureParent.get(loci.structure);
  107. if (!parent || !parent.obj) return;
  108. const residueLoci = StructureElement.Loci.extendToWholeResidues(StructureElement.Loci.remap(loci, parent.obj!.data))
  109. const residueBundle = StructureElement.Bundle.fromLoci(residueLoci)
  110. const surroundings = MS.struct.modifier.includeSurroundings({
  111. 0: StructureElement.Bundle.toExpression(residueBundle),
  112. radius: this.params.expandRadius,
  113. 'as-whole-residues': true
  114. });
  115. const { state, builder, refs } = this.ensureShape(parent);
  116. builder.to(refs[StructureFocusRepresentationTags.TargetSel]!).update(StateTransforms.Model.StructureSelectionFromBundle, old => ({ ...old, bundle: residueBundle }));
  117. builder.to(refs[StructureFocusRepresentationTags.SurrSel]!).update(StateTransforms.Model.StructureSelectionFromExpression, old => ({ ...old, expression: surroundings, label: this.surrLabel }));
  118. await PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
  119. // TODO make work with durationMs > 0
  120. this.plugin.managers.camera.focusLoci(loci)
  121. }
  122. register(ref: string): void {
  123. this.subscribeObservable(this.plugin.managers.structure.focus.behaviors.current, (entry) => {
  124. if (entry) this.focus(entry.loci)
  125. else this.clear(StateTransform.RootRef)
  126. });
  127. }
  128. async update(params: StructureFocusRepresentationProps) {
  129. let oldRadius = this.params.expandRadius;
  130. this.params = params;
  131. const state = this.plugin.state.data;
  132. const builder = state.build();
  133. const all = StateSelection.Generators.root.subtree();
  134. for (const repr of state.select(all.withTag(StructureFocusRepresentationTags.TargetRepr))) {
  135. builder.to(repr).update(this.params.targetParams);
  136. }
  137. for (const repr of state.select(all.withTag(StructureFocusRepresentationTags.SurrRepr))) {
  138. builder.to(repr).update(this.params.surroundingsParams);
  139. }
  140. for (const repr of state.select(all.withTag(StructureFocusRepresentationTags.SurrNciRepr))) {
  141. builder.to(repr).update(this.params.nciParams);
  142. }
  143. await PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
  144. // TODO: update properly
  145. if (params.expandRadius !== oldRadius) await this.clear(StateTransform.RootRef);
  146. return true;
  147. }
  148. }
  149. export const StructureFocusRepresentation = PluginBehavior.create({
  150. name: 'create-structure-focus-representation',
  151. display: { name: 'Structure Focus Representation' },
  152. category: 'interaction',
  153. ctor: StructureFocusRepresentationBehavior,
  154. params: (_, plugin) => StructureFocusRepresentationParams(plugin)
  155. });