selection.ts 5.6 KB

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