indexed.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { ResidueIndex, ChainIndex, ElementIndex, EntityIndex } from '../../indexing';
  7. import { Unit, Structure, StructureElement } from '../../../structure';
  8. import { Segmentation } from 'mol-data/int';
  9. import { UUID } from 'mol-util';
  10. import { CifWriter } from 'mol-io/writer/cif';
  11. import { Model } from '../../model';
  12. export interface IndexedCustomProperty<Idx extends IndexedCustomProperty.Index, T = any> {
  13. readonly id: UUID,
  14. readonly kind: Unit.Kind,
  15. readonly level: IndexedCustomProperty.Level,
  16. has(idx: Idx): boolean,
  17. get(idx: Idx): T | undefined,
  18. getElements(structure: Structure): IndexedCustomProperty.Elements<T>
  19. }
  20. export namespace IndexedCustomProperty {
  21. export type Index = ElementIndex | ResidueIndex | ChainIndex | EntityIndex
  22. export type Level = 'atom' | 'residue' | 'chain' | 'entity'
  23. export interface Elements<T> {
  24. elements: StructureElement[],
  25. property(index: number): T
  26. }
  27. export function getCifDataSource<Idx extends Index, T>(structure: Structure, prop: IndexedCustomProperty<Idx, T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
  28. if (!prop) return { rowCount: 0 };
  29. if (cache && cache[prop.id]) return cache[prop.id];
  30. const data = prop.getElements(structure);
  31. const ret = { data, rowCount: data.elements.length };
  32. if (cache) cache[prop.id] = ret;
  33. return ret;
  34. }
  35. export type Atom<T> = IndexedCustomProperty<ElementIndex, T>
  36. export function fromAtomMap<T>(map: Map<ElementIndex, T>): Atom<T> {
  37. return new ElementMappedCustomProperty(map);
  38. }
  39. export function fromAtomArray<T>(array: ArrayLike<T>): Atom<T> {
  40. // TODO: create "array based custom property" as optimization
  41. return new ElementMappedCustomProperty(arrayToMap(array));
  42. }
  43. export type Residue<T> = IndexedCustomProperty<ResidueIndex, T>
  44. const getResidueSegments = (model: Model) => model.atomicHierarchy.residueAtomSegments;
  45. export function fromResidueMap<T>(map: Map<ResidueIndex, T>): Residue<T> {
  46. return new SegmentedMappedIndexedCustomProperty('residue', map, getResidueSegments, Unit.Kind.Atomic);
  47. }
  48. export function fromResidueArray<T>(array: ArrayLike<T>): Residue<T> {
  49. // TODO: create "array based custom property" as optimization
  50. return new SegmentedMappedIndexedCustomProperty('residue', arrayToMap(array), getResidueSegments, Unit.Kind.Atomic);
  51. }
  52. export type Chain<T> = IndexedCustomProperty<ChainIndex, T>
  53. const getChainSegments = (model: Model) => model.atomicHierarchy.chainAtomSegments;
  54. export function fromChainMap<T>(map: Map<ChainIndex, T>): Chain<T> {
  55. return new SegmentedMappedIndexedCustomProperty('chain', map, getChainSegments, Unit.Kind.Atomic);
  56. }
  57. export function fromChainArray<T>(array: ArrayLike<T>): Chain<T> {
  58. // TODO: create "array based custom property" as optimization
  59. return new SegmentedMappedIndexedCustomProperty('chain', arrayToMap(array), getChainSegments, Unit.Kind.Atomic);
  60. }
  61. export type Entity<T> = IndexedCustomProperty<EntityIndex, T>
  62. export function fromEntityMap<T>(map: Map<EntityIndex, T>): Entity<T> {
  63. return new EntityMappedCustomProperty(map);
  64. }
  65. }
  66. function arrayToMap<Idx extends IndexedCustomProperty.Index, T>(array: ArrayLike<T>): Map<Idx, T> {
  67. const ret = new Map<Idx, T>();
  68. for (let i = 0 as Idx, _i = array.length; i < _i; i++) ret.set(i, array[i as number]);
  69. return ret;
  70. }
  71. class SegmentedMappedIndexedCustomProperty<Idx extends IndexedCustomProperty.Index, T = any> implements IndexedCustomProperty<Idx, T> {
  72. readonly id: UUID = UUID.create();
  73. readonly kind: Unit.Kind;
  74. has(idx: Idx): boolean { return this.map.has(idx); }
  75. get(idx: Idx) { return this.map.get(idx); }
  76. private getStructureElements(structure: Structure) {
  77. const models = structure.models;
  78. if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
  79. const seenIndices = new Set<Idx>();
  80. const unitGroups = structure.unitSymmetryGroups;
  81. const loci: StructureElement[] = [];
  82. const segments = this.segmentGetter(models[0]);
  83. for (const unitGroup of unitGroups) {
  84. const unit = unitGroup.units[0];
  85. if (unit.kind !== this.kind) {
  86. continue;
  87. }
  88. const chains = Segmentation.transientSegments(segments, unit.elements);
  89. while (chains.hasNext) {
  90. const seg = chains.move();
  91. if (!this.has(seg.index) || seenIndices.has(seg.index)) continue;
  92. seenIndices.add(seg.index);
  93. loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
  94. }
  95. }
  96. loci.sort((x, y) => x.element - y.element);
  97. return loci;
  98. }
  99. getElements(structure: Structure): IndexedCustomProperty.Elements<T> {
  100. const index = this.segmentGetter(structure.model).index;
  101. const elements = this.getStructureElements(structure);
  102. return { elements, property: i => this.get(index[elements[i].element])! };
  103. }
  104. constructor(public level: 'residue' | 'chain', private map: Map<Idx, T>, private segmentGetter: (model: Model) => Segmentation<ElementIndex, Idx>, kind: Unit.Kind) {
  105. this.kind = kind;
  106. }
  107. }
  108. class ElementMappedCustomProperty<T = any> implements IndexedCustomProperty<ElementIndex, T> {
  109. readonly id: UUID = UUID.create();
  110. readonly kind: Unit.Kind;
  111. readonly level = 'atom';
  112. has(idx: ElementIndex): boolean { return this.map.has(idx); }
  113. get(idx: ElementIndex) { return this.map.get(idx); }
  114. private getStructureElements(structure: Structure) {
  115. const models = structure.models;
  116. if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
  117. const seenIndices = new Set<ElementIndex>();
  118. const unitGroups = structure.unitSymmetryGroups;
  119. const loci: StructureElement[] = [];
  120. for (const unitGroup of unitGroups) {
  121. const unit = unitGroup.units[0];
  122. if (unit.kind !== this.kind) {
  123. continue;
  124. }
  125. const elements = unit.elements;
  126. for (let i = 0, _i = elements.length; i < _i; i++) {
  127. const e = elements[i];
  128. if (!this.has(e) || seenIndices.has(e)) continue;
  129. seenIndices.add(elements[i]);
  130. loci[loci.length] = StructureElement.create(unit, e);
  131. }
  132. }
  133. loci.sort((x, y) => x.element - y.element);
  134. return loci;
  135. }
  136. getElements(structure: Structure): IndexedCustomProperty.Elements<T> {
  137. const elements = this.getStructureElements(structure);
  138. return { elements, property: i => this.get(elements[i].element)! };
  139. }
  140. constructor(private map: Map<ElementIndex, T>) {
  141. this.kind = Unit.Kind.Atomic;
  142. }
  143. }
  144. class EntityMappedCustomProperty<T = any> implements IndexedCustomProperty<EntityIndex, T> {
  145. readonly id: UUID = UUID.create();
  146. readonly kind: Unit.Kind;
  147. readonly level = 'entity';
  148. has(idx: EntityIndex): boolean { return this.map.has(idx); }
  149. get(idx: EntityIndex) { return this.map.get(idx); }
  150. private getStructureElements(structure: Structure) {
  151. const models = structure.models;
  152. if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
  153. const index = models[0].atomicHierarchy.index;
  154. const seenIndices = new Set<EntityIndex>();
  155. const unitGroups = structure.unitSymmetryGroups;
  156. const loci: StructureElement[] = [];
  157. const segments = models[0].atomicHierarchy.chainAtomSegments;
  158. for (const unitGroup of unitGroups) {
  159. const unit = unitGroup.units[0];
  160. if (unit.kind !== this.kind) {
  161. continue;
  162. }
  163. const chains = Segmentation.transientSegments(segments, unit.elements);
  164. while (chains.hasNext) {
  165. const seg = chains.move();
  166. const eI = index.getEntityFromChain(seg.index);
  167. if (!this.has(eI) || seenIndices.has(eI)) continue;
  168. seenIndices.add(eI);
  169. loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
  170. }
  171. }
  172. loci.sort((x, y) => x.element - y.element);
  173. return loci;
  174. }
  175. getElements(structure: Structure): IndexedCustomProperty.Elements<T> {
  176. const elements = this.getStructureElements(structure);
  177. const chainIndex = structure.model.atomicHierarchy.chainAtomSegments.index;
  178. const index = structure.model.atomicHierarchy.index;
  179. return { elements, property: i => this.get(index.getEntityFromChain(chainIndex[elements[i].element]))! };
  180. }
  181. constructor(private map: Map<EntityIndex, T>) {
  182. this.kind = Unit.Kind.Atomic;
  183. }
  184. }