selection.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. export function toLoci(sel: StructureSelection): StructureElement.Loci {
  31. const elements: { unit: Unit, indices: OrderedSet<StructureElement.UnitIndex> }[] = [];
  32. const { unitMap } = sel.source;
  33. for (const unit of unionStructure(sel).units) {
  34. if (unit === unitMap.get(unit.id)) {
  35. elements[elements.length] = {
  36. unit,
  37. indices: OrderedSet.ofBounds(0 as StructureElement.UnitIndex, unit.elements.length as StructureElement.UnitIndex)
  38. };
  39. } else {
  40. elements[elements.length] = {
  41. unit,
  42. indices: OrderedSet.ofSortedArray(SortedArray.indicesOf(unitMap.get(unit.id).elements, unit.elements))
  43. };
  44. }
  45. }
  46. return StructureElement.Loci(sel.source, elements);
  47. }
  48. /** use source unit in loci.elements */
  49. export function toLoci2(sel: StructureSelection): StructureElement.Loci {
  50. const elements: { unit: Unit, indices: OrderedSet<StructureElement.UnitIndex> }[] = [];
  51. const { unitMap } = sel.source;
  52. for (const _unit of unionStructure(sel).units) {
  53. const unit = unitMap.get(_unit.id)
  54. if (unit === _unit) {
  55. elements[elements.length] = {
  56. unit,
  57. indices: OrderedSet.ofBounds(0 as StructureElement.UnitIndex, unit.elements.length as StructureElement.UnitIndex)
  58. };
  59. } else {
  60. elements[elements.length] = {
  61. unit,
  62. indices: OrderedSet.ofSortedArray(SortedArray.indicesOf(unit.elements, _unit.elements))
  63. };
  64. }
  65. }
  66. return StructureElement.Loci(sel.source, elements);
  67. }
  68. export interface Builder {
  69. add(structure: Structure): void,
  70. getSelection(): StructureSelection
  71. }
  72. function getSelection(source: Structure, structures: Structure[], allSingletons: boolean) {
  73. const len = structures.length;
  74. if (len === 0) return Empty(source);
  75. if (allSingletons) return Singletons(source, structureUnion(source, structures));
  76. return Sequence(source, structures);
  77. }
  78. class LinearBuilderImpl implements Builder {
  79. private structures: Structure[] = [];
  80. private allSingletons = true;
  81. add(structure: Structure) {
  82. const elementCount = structure.elementCount;
  83. if (elementCount === 0) return;
  84. this.structures[this.structures.length] = structure;
  85. if (elementCount !== 1) this.allSingletons = false;
  86. }
  87. getSelection() { return getSelection(this.source, this.structures, this.allSingletons); }
  88. constructor(private source: Structure) { }
  89. }
  90. class HashBuilderImpl implements Builder {
  91. private structures: Structure[] = [];
  92. private allSingletons = true;
  93. private uniqueSets = HashSet(Structure.hashCode, Structure.areUnitAndIndicesEqual);
  94. add(structure: Structure) {
  95. const atomCount = structure.elementCount;
  96. if (atomCount === 0 || !this.uniqueSets.add(structure)) return;
  97. this.structures[this.structures.length] = structure;
  98. if (atomCount !== 1) this.allSingletons = false;
  99. }
  100. getSelection() { return getSelection(this.structure, this.structures, this.allSingletons); }
  101. constructor(private structure: Structure) { }
  102. }
  103. export function LinearBuilder(structure: Structure): Builder { return new LinearBuilderImpl(structure); }
  104. export function UniqueBuilder(structure: Structure): Builder { return new HashBuilderImpl(structure); }
  105. // TODO: build timeout checking into this?
  106. export function forEach(sel: StructureSelection, fn: (s: Structure, i: number) => void) {
  107. let idx = 0;
  108. if (StructureSelection.isSingleton(sel)) {
  109. for (const unit of sel.structure.units) {
  110. const { elements } = unit;
  111. for (let i = 0, _i = elements.length; i < _i; i++) {
  112. // TODO: optimize this somehow???
  113. const s = Structure.create([unit.getChild(SortedArray.ofSingleton(elements[i]))], { parent: sel.source });
  114. fn(s, idx++);
  115. }
  116. }
  117. } else {
  118. for (const s of sel.structures) {
  119. fn(s, idx++);
  120. }
  121. }
  122. }
  123. export function withInputStructure(selection: StructureSelection, structure: Structure) {
  124. if (isSingleton(selection)) return Singletons(structure, selection.structure);
  125. return Sequence(structure, selection.structures);
  126. }
  127. // TODO: spatial lookup?
  128. }
  129. export { StructureSelection }