structure.ts 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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 { PluginContext } from '../../mol-plugin/context';
  7. import { StructureHierarchy, buildStructureHierarchy, ModelRef, StructureComponentRef } from './structure/hierarchy';
  8. import { RxEventHelper } from '../../mol-util/rx-event-helper';
  9. export class StructureHierarchyManager {
  10. private ev = RxEventHelper.create();
  11. readonly behaviors = {
  12. hierarchy: this.ev.behavior(StructureHierarchy()),
  13. currentModels: this.ev.behavior([] as ReadonlyArray<ModelRef>)
  14. }
  15. private checkCurrent() {
  16. const hierarchy = this.behaviors.hierarchy.value;
  17. const current = this.behaviors.currentModels.value;
  18. if (current.length === 0) {
  19. const models = hierarchy.trajectories[0]?.models;
  20. if (models) {
  21. this.behaviors.currentModels.next(models);
  22. }
  23. return;
  24. }
  25. const newCurrent: ModelRef[] = [];
  26. for (const c of current) {
  27. const ref = hierarchy.refs.get(c.cell.transform.ref) as ModelRef;
  28. if (!ref) continue;
  29. newCurrent.push(ref);
  30. }
  31. this.behaviors.currentModels.next(newCurrent);
  32. }
  33. private sync() {
  34. const update = buildStructureHierarchy(this.plugin.state.dataState, this.behaviors.hierarchy.value);
  35. if (update.added.length === 0 && update.updated.length === 0 && update.removed.length === 0) {
  36. return;
  37. }
  38. this.behaviors.hierarchy.next(update.hierarchy)
  39. this.checkCurrent();
  40. }
  41. constructor(private plugin: PluginContext) {
  42. plugin.state.dataState.events.changed.subscribe(e => {
  43. if (e.inTransaction || plugin.behaviors.state.isAnimating.value) return;
  44. this.sync();
  45. });
  46. plugin.behaviors.state.isAnimating.subscribe(isAnimating => {
  47. if (!isAnimating && !plugin.behaviors.state.isUpdating.value) this.sync();
  48. })
  49. }
  50. }
  51. export namespace StructureHierarchyManager {
  52. function componentKey(c: StructureComponentRef) {
  53. if (!c.cell.transform.tags) return;
  54. return [...c.cell.transform.tags].sort().join();
  55. }
  56. export function getCommonComponentPivots(models: ReadonlyArray<ModelRef>) {
  57. if (!models[0]?.structures?.length) return [];
  58. if (models[0]?.structures?.length === 1) return models[0]?.structures[0]?.components || [];
  59. const pivots = new Map<string, StructureComponentRef>();
  60. for (const c of models[0]?.structures[0]?.components) {
  61. const key = componentKey(c);
  62. if (!key) continue;
  63. pivots.set(key, c);
  64. }
  65. for (const m of models) {
  66. for (const s of m.structures) {
  67. for (const c of s.components) {
  68. const key = componentKey(c);
  69. if (!key) continue;
  70. if (!pivots.has(key)) pivots.delete(key);
  71. }
  72. }
  73. }
  74. const ret: StructureComponentRef[] = [];
  75. pivots.forEach(function (this: StructureComponentRef[], p) { this.push(p) }, ret);
  76. return ret;
  77. }
  78. }