trace-iterator.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Unit, StructureElement, ElementIndex, ResidueIndex } from 'mol-model/structure';
  7. import { Segmentation } from 'mol-data/int';
  8. import { MoleculeType, SecondaryStructureType, AtomRole } from 'mol-model/structure/model/types';
  9. import Iterator from 'mol-data/iterator';
  10. import { Vec3 } from 'mol-math/linear-algebra';
  11. import SortedRanges from 'mol-data/int/sorted-ranges';
  12. import { CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/structure/model/properties/coarse';
  13. import { getAtomicMoleculeType, getElementIndexForAtomRole } from 'mol-model/structure/util';
  14. import { getPolymerRanges } from '../polymer';
  15. import { AtomicConformation } from 'mol-model/structure/model/properties/atomic';
  16. /**
  17. * Iterates over individual residues/coarse elements in polymers of a unit while
  18. * providing information about the neighbourhood in the underlying model for drawing splines
  19. */
  20. export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> {
  21. switch (unit.kind) {
  22. case Unit.Kind.Atomic: return new AtomicPolymerTraceIterator(unit)
  23. case Unit.Kind.Spheres:
  24. case Unit.Kind.Gaussians:
  25. return new CoarsePolymerTraceIterator(unit)
  26. }
  27. }
  28. interface PolymerTraceElement {
  29. center: StructureElement
  30. first: boolean, last: boolean
  31. secStrucFirst: boolean, secStrucLast: boolean
  32. secStrucType: SecondaryStructureType
  33. moleculeType: MoleculeType
  34. p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3
  35. d12: Vec3, d23: Vec3
  36. }
  37. const SecStrucTypeNA = SecondaryStructureType.create(SecondaryStructureType.Flag.NA)
  38. function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
  39. return {
  40. center: StructureElement.create(unit),
  41. first: false, last: false,
  42. secStrucFirst: false, secStrucLast: false,
  43. secStrucType: SecStrucTypeNA,
  44. moleculeType: MoleculeType.unknown,
  45. p0: Vec3.zero(), p1: Vec3.zero(), p2: Vec3.zero(), p3: Vec3.zero(), p4: Vec3.zero(),
  46. d12: Vec3.create(1, 0, 0), d23: Vec3.create(1, 0, 0),
  47. }
  48. }
  49. const enum AtomicPolymerTraceIteratorState { nextPolymer, nextResidue }
  50. export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> {
  51. private value: PolymerTraceElement
  52. private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
  53. private residueIt: Segmentation.SegmentIterator<ResidueIndex>
  54. private polymerSegment: Segmentation.Segment<ResidueIndex>
  55. private secondaryStructureType: ArrayLike<SecondaryStructureType>
  56. private residueSegmentMin: ResidueIndex
  57. private residueSegmentMax: ResidueIndex
  58. private prevSecStrucType: SecondaryStructureType
  59. private currSecStrucType: SecondaryStructureType
  60. private nextSecStrucType: SecondaryStructureType
  61. private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer
  62. private residueAtomSegments: Segmentation<ElementIndex, ResidueIndex>
  63. private atomicConformation: AtomicConformation
  64. private p0 = Vec3.zero();
  65. private p1 = Vec3.zero();
  66. private p2 = Vec3.zero();
  67. private p3 = Vec3.zero();
  68. private p4 = Vec3.zero();
  69. private p5 = Vec3.zero();
  70. private p6 = Vec3.zero();
  71. // private v01 = Vec3.zero();
  72. private v12 = Vec3.zero();
  73. private v23 = Vec3.zero();
  74. // private v34 = Vec3.zero();
  75. hasNext: boolean = false;
  76. private pos(target: Vec3, index: number) {
  77. target[0] = this.atomicConformation.x[index]
  78. target[1] = this.atomicConformation.y[index]
  79. target[2] = this.atomicConformation.z[index]
  80. }
  81. private updateResidueSegmentRange(polymerSegment: Segmentation.Segment<ResidueIndex>) {
  82. const { index } = this.residueAtomSegments
  83. this.residueSegmentMin = index[this.unit.elements[polymerSegment.start]]
  84. this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]]
  85. }
  86. private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
  87. const { atomicHierarchy } = this.unit.model
  88. if (residueIndex < this.residueSegmentMin) {
  89. const cyclicIndex = atomicHierarchy.cyclicPolymerMap.get(this.residueSegmentMin)
  90. if (cyclicIndex !== undefined) {
  91. residueIndex = cyclicIndex - (this.residueSegmentMin - residueIndex - 1) as ResidueIndex
  92. } else {
  93. residueIndex = this.residueSegmentMin
  94. }
  95. } else if (residueIndex > this.residueSegmentMax) {
  96. const cyclicIndex = atomicHierarchy.cyclicPolymerMap.get(this.residueSegmentMax)
  97. if (cyclicIndex !== undefined) {
  98. residueIndex = cyclicIndex + (residueIndex - this.residueSegmentMax - 1) as ResidueIndex
  99. } else {
  100. residueIndex = this.residueSegmentMax
  101. }
  102. }
  103. const elementIndex = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole)
  104. return elementIndex === -1 ? atomicHierarchy.residueAtomSegments.offsets[residueIndex] : elementIndex
  105. }
  106. private setControlPoint(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: ResidueIndex) {
  107. if (SecondaryStructureType.is(this.currSecStrucType, SecondaryStructureType.Flag.Beta)) {
  108. Vec3.scale(out, Vec3.add(out, p1, Vec3.add(out, p3, Vec3.add(out, p2, p2))), 1/4)
  109. } else {
  110. Vec3.copy(out, p2)
  111. }
  112. }
  113. move() {
  114. const { residueIt, polymerIt, value } = this
  115. if (this.state === AtomicPolymerTraceIteratorState.nextPolymer) {
  116. while (polymerIt.hasNext) {
  117. this.polymerSegment = polymerIt.move();
  118. residueIt.setSegment(this.polymerSegment);
  119. this.updateResidueSegmentRange(this.polymerSegment)
  120. if (residueIt.hasNext) {
  121. this.state = AtomicPolymerTraceIteratorState.nextResidue
  122. this.currSecStrucType = SecStrucTypeNA
  123. this.nextSecStrucType = this.secondaryStructureType[this.residueSegmentMin]
  124. break
  125. }
  126. }
  127. }
  128. if (this.state === AtomicPolymerTraceIteratorState.nextResidue) {
  129. const { index: residueIndex } = residueIt.move();
  130. this.prevSecStrucType = this.currSecStrucType
  131. this.currSecStrucType = this.nextSecStrucType
  132. this.nextSecStrucType = residueIt.hasNext ? this.secondaryStructureType[residueIndex + 1] : SecStrucTypeNA
  133. value.secStrucType = this.currSecStrucType
  134. value.center.element = this.getElementIndex(residueIndex, 'trace')
  135. value.first = residueIndex === this.residueSegmentMin
  136. value.last = residueIndex === this.residueSegmentMax
  137. value.secStrucFirst = this.prevSecStrucType !== this.currSecStrucType
  138. value.secStrucLast = this.currSecStrucType !== this.nextSecStrucType
  139. value.moleculeType = getAtomicMoleculeType(this.unit.model, residueIndex)
  140. this.pos(this.p0, this.getElementIndex(residueIndex - 3 as ResidueIndex, 'trace'))
  141. this.pos(this.p1, this.getElementIndex(residueIndex - 2 as ResidueIndex, 'trace'))
  142. this.pos(this.p2, this.getElementIndex(residueIndex - 1 as ResidueIndex, 'trace'))
  143. this.pos(this.p3, this.getElementIndex(residueIndex, 'trace'))
  144. this.pos(this.p4, this.getElementIndex(residueIndex + 1 as ResidueIndex, 'trace'))
  145. this.pos(this.p5, this.getElementIndex(residueIndex + 2 as ResidueIndex, 'trace'))
  146. this.pos(this.p6, this.getElementIndex(residueIndex + 3 as ResidueIndex, 'trace'))
  147. // this.pos(this.v01, this.getAtomIndex(residueIndex - 2 as ResidueIndex, 'direction'))
  148. this.pos(this.v12, this.getElementIndex(residueIndex - 1 as ResidueIndex, 'direction'))
  149. this.pos(this.v23, this.getElementIndex(residueIndex, 'direction'))
  150. // this.pos(this.v34, this.getAtomIndex(residueIndex + 1 as ResidueIndex, 'direction'))
  151. this.setControlPoint(value.p0, this.p0, this.p1, this.p2, residueIndex - 2 as ResidueIndex)
  152. this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndex - 1 as ResidueIndex)
  153. this.setControlPoint(value.p2, this.p2, this.p3, this.p4, residueIndex)
  154. this.setControlPoint(value.p3, this.p3, this.p4, this.p5, residueIndex + 1 as ResidueIndex)
  155. this.setControlPoint(value.p4, this.p4, this.p5, this.p6, residueIndex + 2 as ResidueIndex)
  156. Vec3.copy(value.d12, this.v12)
  157. Vec3.copy(value.d23, this.v23)
  158. if (!residueIt.hasNext) {
  159. this.state = AtomicPolymerTraceIteratorState.nextPolymer
  160. }
  161. }
  162. this.hasNext = residueIt.hasNext || polymerIt.hasNext
  163. return this.value;
  164. }
  165. constructor(private unit: Unit.Atomic) {
  166. this.atomicConformation = unit.model.atomicConformation
  167. this.residueAtomSegments = unit.model.atomicHierarchy.residueAtomSegments
  168. this.secondaryStructureType = unit.model.properties.secondaryStructure.type
  169. this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
  170. this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements);
  171. this.value = createPolymerTraceElement(unit)
  172. this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
  173. }
  174. }
  175. const enum CoarsePolymerTraceIteratorState { nextPolymer, nextElement }
  176. export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement> {
  177. private value: PolymerTraceElement
  178. private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
  179. private polymerSegment: Segmentation.Segment<ResidueIndex>
  180. private state: CoarsePolymerTraceIteratorState = CoarsePolymerTraceIteratorState.nextPolymer
  181. private conformation: CoarseSphereConformation | CoarseGaussianConformation
  182. private elementIndex: number
  183. hasNext: boolean = false;
  184. private pos(target: Vec3, elementIndex: number) {
  185. elementIndex = Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1)
  186. const index = this.unit.elements[elementIndex]
  187. target[0] = this.conformation.x[index]
  188. target[1] = this.conformation.y[index]
  189. target[2] = this.conformation.z[index]
  190. }
  191. move() {
  192. if (this.state === CoarsePolymerTraceIteratorState.nextPolymer) {
  193. while (this.polymerIt.hasNext) {
  194. this.polymerSegment = this.polymerIt.move();
  195. this.elementIndex = this.polymerSegment.start
  196. if (this.elementIndex + 1 < this.polymerSegment.end) {
  197. this.state = CoarsePolymerTraceIteratorState.nextElement
  198. break
  199. }
  200. }
  201. }
  202. if (this.state === CoarsePolymerTraceIteratorState.nextElement) {
  203. this.elementIndex += 1
  204. this.value.center.element = this.value.center.unit.elements[this.elementIndex]
  205. this.pos(this.value.p0, this.elementIndex - 2)
  206. this.pos(this.value.p1, this.elementIndex - 1)
  207. this.pos(this.value.p2, this.elementIndex)
  208. this.pos(this.value.p3, this.elementIndex + 1)
  209. this.pos(this.value.p4, this.elementIndex + 2)
  210. this.value.first = this.elementIndex === this.polymerSegment.start
  211. this.value.last = this.elementIndex === this.polymerSegment.end - 1
  212. if (this.elementIndex + 1 >= this.polymerSegment.end) {
  213. this.state = CoarsePolymerTraceIteratorState.nextPolymer
  214. }
  215. }
  216. this.hasNext = this.elementIndex + 1 < this.polymerSegment.end || this.polymerIt.hasNext
  217. return this.value;
  218. }
  219. constructor(private unit: Unit.Spheres | Unit.Gaussians) {
  220. this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
  221. this.value = createPolymerTraceElement(unit)
  222. switch (unit.kind) {
  223. case Unit.Kind.Spheres: this.conformation = unit.model.coarseConformation.spheres; break
  224. case Unit.Kind.Gaussians: this.conformation = unit.model.coarseConformation.gaussians; break
  225. }
  226. this.hasNext = this.polymerIt.hasNext
  227. }
  228. }