structure-representation.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { arrayFind } from '../../mol-data/util';
  7. import { Structure } from '../../mol-model/structure';
  8. import { StateTransform, StateTree, StateSelection, StateObjectRef } from '../../mol-state';
  9. import { Task } from '../../mol-task';
  10. import { isProductionMode } from '../../mol-util/debug';
  11. import { objectForEach } from '../../mol-util/object';
  12. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  13. import { PluginContext } from '../../mol-plugin/context';
  14. import { PresetStructureReprentations } from './structure/preset';
  15. import { StructureRepresentationProvider, RepresentationProviderTags } from './structure/provider';
  16. import { UniqueArray } from '../../mol-data/generic';
  17. // TODO: support quality
  18. // TODO: support ignore hydrogens
  19. export type StructureRepresentationProviderRef = keyof PresetStructureReprentations | StructureRepresentationProvider | string
  20. export class StructureRepresentationBuilder {
  21. private providers: StructureRepresentationProvider[] = [];
  22. private providerMap: Map<string, StructureRepresentationProvider> = new Map();
  23. readonly defaultProvider = PresetStructureReprentations.auto;
  24. private resolveProvider(ref: StructureRepresentationProviderRef) {
  25. return typeof ref === 'string'
  26. ? PresetStructureReprentations[ref as keyof PresetStructureReprentations] ?? arrayFind(this.providers, p => p.id === ref)
  27. : ref;
  28. }
  29. hasProvider(s: Structure) {
  30. for (const p of this.providers) {
  31. if (!p.isApplicable || p.isApplicable(s, this.plugin)) return true;
  32. }
  33. return false;
  34. }
  35. getOptions(s: Structure) {
  36. const options: [string, string][] = [];
  37. const map: { [K in string]: PD.Any } = Object.create(null);
  38. for (const p of this.providers) {
  39. if (p.isApplicable && !p.isApplicable(s, this.plugin)) continue;
  40. options.push([p.id, p.display.name]);
  41. map[p.id] = p.params ? PD.Group(p.params(s, this.plugin)) : PD.EmptyGroup()
  42. }
  43. if (options.length === 0) return PD.MappedStatic('', { '': PD.EmptyGroup() });
  44. return PD.MappedStatic(options[0][0], map, { options });
  45. }
  46. hasManagedRepresentation(ref: StateObjectRef) {
  47. // TODO: make this state selection function?
  48. const tree = this.plugin.state.dataState.tree;
  49. const root = StateObjectRef.resolve(this.plugin.state.dataState, ref);
  50. if (!root) return false;
  51. return StateTree.doPreOrder(tree, root.transform, { found: false, map: this.providerMap }, (n, _, s) => {
  52. if (!n.tags) return;
  53. for (const t of n.tags) {
  54. if (s.map.has(t)) {
  55. s.found = true;
  56. return false;
  57. }
  58. }
  59. }).found;
  60. }
  61. getManagedRepresentations(ref: StateObjectRef) {
  62. const tree = this.plugin.state.dataState.tree;
  63. const root = StateObjectRef.resolve(this.plugin.state.dataState, ref);
  64. if (!root) return [];
  65. return StateTree.doPreOrder(tree, root.transform, { found: UniqueArray.create<string, StructureRepresentationProvider>(), map: this.providerMap }, (n, _, s) => {
  66. if (!n.tags) return;
  67. for (const t of n.tags) {
  68. if (s.map.has(t)) UniqueArray.add(s.found, t, s.map.get(t)!);
  69. }
  70. }).found.array;
  71. }
  72. register(provider: StructureRepresentationProvider) {
  73. if (this.providerMap.has(provider.id)) {
  74. throw new Error(`Repr. provider with id '${provider.id}' already registered.`);
  75. }
  76. // TODO: sort by group
  77. this.providers.push(provider);
  78. this.providerMap.set(provider.id, provider);
  79. }
  80. remove(providerRef: StructureRepresentationProviderRef, structureRoot?: StateObjectRef) {
  81. const id = this.resolveProvider(providerRef)?.id;
  82. if (!id) return;
  83. const state = this.plugin.state.dataState;
  84. const root = StateObjectRef.resolveRef(state, structureRoot) || StateTransform.RootRef;
  85. const reprs = StateSelection.findWithAllTags(state.tree, root, new Set([id, RepresentationProviderTags.Representation]));
  86. const builder = state.build();
  87. for (const r of reprs) {
  88. builder.delete(r.ref);
  89. }
  90. const tree = builder.currentTree;
  91. const selections = StateSelection.findWithAllTags(tree, root, new Set([RepresentationProviderTags.Selection]));
  92. for (const s of selections) {
  93. if (!tree.children.has(s.ref) || tree.children.get(s.ref).size === 0) builder.delete(s.ref);
  94. }
  95. if (builder.editInfo.count === 0) return;
  96. return this.plugin.runTask(state.updateTree(builder));
  97. }
  98. apply<K extends keyof PresetStructureReprentations>(parent: StateObjectRef, preset: K, params?: StructureRepresentationProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationProvider.State<PresetStructureReprentations[K]>> | undefined
  99. apply<P = any, S = {}>(parent: StateObjectRef, providers: StructureRepresentationProvider<P, S>, params?: P): Promise<S> | undefined
  100. apply(parent: StateObjectRef, providerId: string, params?: any): Promise<any> | undefined
  101. apply(parent: StateObjectRef, providerRef: string | StructureRepresentationProvider, params?: any): Promise<any> | undefined {
  102. const provider = this.resolveProvider(providerRef);
  103. if (!provider) return;
  104. const state = this.plugin.state.dataState;
  105. const cell = StateObjectRef.resolveAndCheck(state, parent);
  106. if (!cell) {
  107. if (!isProductionMode) console.warn(`Applying structure repr. provider to bad cell.`);
  108. return;
  109. }
  110. const prms = params || (provider.params
  111. ? PD.getDefaultValues(provider.params(cell.obj!.data, this.plugin))
  112. : {})
  113. const task = Task.create(`${provider.display.name}`, ctx => provider.apply(ctx, state, cell, prms, this.plugin) as Promise<any>);
  114. return this.plugin.runTask(task);
  115. }
  116. constructor(public plugin: PluginContext) {
  117. objectForEach(PresetStructureReprentations, r => this.register(r));
  118. }
  119. }