selection.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /**
  2. * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { HashSet } from '../../../mol-data/generic';
  7. import { Structure, StructureElement, Unit } from '../structure';
  8. import { structureUnion } from './utils/structure-set';
  9. import { OrderedSet, SortedArray } from '../../../mol-data/int';
  10. // A selection is a pair of a Structure and a sequence of unique AtomSets
  11. type StructureSelection = StructureSelection.Singletons | StructureSelection.Sequence
  12. namespace StructureSelection {
  13. // If each element of the selection is a singleton, we can use a more efficient representation.
  14. export interface Singletons { readonly kind: 'singletons', readonly source: Structure, readonly structure: Structure }
  15. export interface Sequence { readonly kind: 'sequence', readonly source: Structure, readonly structures: Structure[] }
  16. export function Singletons(source: Structure, structure: Structure): Singletons { return { kind: 'singletons', source, structure }; }
  17. export function Sequence(source: Structure, structures: Structure[]): Sequence { return { kind: 'sequence', source, structures }; }
  18. export function Empty(source: Structure): StructureSelection { return Singletons(source, Structure.Empty); };
  19. export function isSingleton(s: StructureSelection): s is Singletons { return s.kind === 'singletons'; }
  20. export function isEmpty(s: StructureSelection) { return isSingleton(s) ? s.structure.units.length === 0 : s.structures.length === 0; }
  21. export function structureCount(sel: StructureSelection) {
  22. if (isSingleton(sel)) return sel.structure.elementCount;
  23. return sel.structures.length;
  24. }
  25. export function unionStructure(sel: StructureSelection): Structure {
  26. if (isEmpty(sel)) return Structure.Empty;
  27. if (isSingleton(sel)) return sel.structure;
  28. return structureUnion(sel.source, sel.structures);
  29. }
  30. /** Convert selection to loci and use "current structure units" in Loci elements */
  31. export function toLociWithCurrentUnits(sel: StructureSelection): StructureElement.Loci {
  32. const elements: { unit: Unit, indices: OrderedSet<StructureElement.UnitIndex> }[] = [];
  33. const { unitMap } = sel.source;
  34. for (const unit of unionStructure(sel).units) {
  35. if (unit === unitMap.get(unit.id)) {
  36. elements[elements.length] = {
  37. unit,
  38. indices: OrderedSet.ofBounds(0 as StructureElement.UnitIndex, unit.elements.length as StructureElement.UnitIndex)
  39. };
  40. } else {
  41. elements[elements.length] = {
  42. unit,
  43. indices: OrderedSet.ofSortedArray(SortedArray.indicesOf(unitMap.get(unit.id).elements, unit.elements))
  44. };
  45. }
  46. }
  47. return StructureElement.Loci(sel.source, elements);
  48. }
  49. /** use source unit in loci.elements */
  50. export function toLociWithSourceUnits(sel: StructureSelection): StructureElement.Loci {
  51. const elements: { unit: Unit, indices: OrderedSet<StructureElement.UnitIndex> }[] = [];
  52. const { unitMap } = sel.source;
  53. for (const _unit of unionStructure(sel).units) {
  54. const unit = unitMap.get(_unit.id);
  55. if (unit === _unit) {
  56. elements[elements.length] = {
  57. unit,
  58. indices: OrderedSet.ofBounds(0 as StructureElement.UnitIndex, unit.elements.length as StructureElement.UnitIndex)
  59. };
  60. } else {
  61. elements[elements.length] = {
  62. unit,
  63. indices: OrderedSet.ofSortedArray(SortedArray.indicesOf(unit.elements, _unit.elements))
  64. };
  65. }
  66. }
  67. return StructureElement.Loci(sel.source, elements);
  68. }
  69. export interface Builder {
  70. add(structure: Structure): void,
  71. getSelection(): StructureSelection
  72. }
  73. function getSelection(source: Structure, structures: Structure[], allSingletons: boolean) {
  74. const len = structures.length;
  75. if (len === 0) return Empty(source);
  76. if (allSingletons) return Singletons(source, structureUnion(source, structures));
  77. return Sequence(source, structures);
  78. }
  79. class LinearBuilderImpl implements Builder {
  80. private structures: Structure[] = [];
  81. private allSingletons = true;
  82. add(structure: Structure) {
  83. const elementCount = structure.elementCount;
  84. if (elementCount === 0) return;
  85. this.structures[this.structures.length] = structure;
  86. if (elementCount !== 1) this.allSingletons = false;
  87. }
  88. getSelection() { return getSelection(this.source, this.structures, this.allSingletons); }
  89. constructor(private source: Structure) { }
  90. }
  91. class HashBuilderImpl implements Builder {
  92. private structures: Structure[] = [];
  93. private allSingletons = true;
  94. private uniqueSets = HashSet(Structure.hashCode, Structure.areUnitIdsAndIndicesEqual);
  95. add(structure: Structure) {
  96. const atomCount = structure.elementCount;
  97. if (atomCount === 0 || !this.uniqueSets.add(structure)) return;
  98. this.structures[this.structures.length] = structure;
  99. if (atomCount !== 1) this.allSingletons = false;
  100. }
  101. getSelection() { return getSelection(this.structure, this.structures, this.allSingletons); }
  102. constructor(private structure: Structure) { }
  103. }
  104. export function LinearBuilder(structure: Structure): Builder { return new LinearBuilderImpl(structure); }
  105. export function UniqueBuilder(structure: Structure): Builder { return new HashBuilderImpl(structure); }
  106. // TODO: build timeout checking into this?
  107. export function forEach(sel: StructureSelection, fn: (s: Structure, i: number) => void) {
  108. let idx = 0;
  109. if (StructureSelection.isSingleton(sel)) {
  110. for (const unit of sel.structure.units) {
  111. const { elements } = unit;
  112. for (let i = 0, _i = elements.length; i < _i; i++) {
  113. // TODO: optimize this somehow???
  114. const s = Structure.create([unit.getChild(SortedArray.ofSingleton(elements[i]))], { parent: sel.source });
  115. fn(s, idx++);
  116. }
  117. }
  118. } else {
  119. for (const s of sel.structures) {
  120. fn(s, idx++);
  121. }
  122. }
  123. }
  124. export function withInputStructure(selection: StructureSelection, structure: Structure) {
  125. if (isSingleton(selection)) return Singletons(structure, selection.structure);
  126. return Sequence(structure, selection.structures);
  127. }
  128. // TODO: spatial lookup?
  129. }
  130. export { StructureSelection };