trace-iterator.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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 } 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 { getPolymerRanges } from '../polymer';
  14. import { AtomicConformation } from 'mol-model/structure/model/properties/atomic';
  15. /**
  16. * Iterates over individual residues/coarse elements in polymers of a unit while
  17. * providing information about the neighbourhood in the underlying model for drawing splines
  18. */
  19. export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> {
  20. switch (unit.kind) {
  21. case Unit.Kind.Atomic: return new AtomicPolymerTraceIterator(unit)
  22. case Unit.Kind.Spheres:
  23. case Unit.Kind.Gaussians:
  24. return new CoarsePolymerTraceIterator(unit)
  25. }
  26. }
  27. interface PolymerTraceElement {
  28. center: StructureElement
  29. centerPrev: StructureElement
  30. centerNext: StructureElement
  31. first: boolean, last: boolean
  32. secStrucFirst: boolean, secStrucLast: boolean
  33. secStrucType: SecondaryStructureType
  34. moleculeType: MoleculeType
  35. isCoarseBackbone: boolean
  36. p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3
  37. d12: Vec3, d23: Vec3
  38. }
  39. const SecStrucTypeNA = SecondaryStructureType.create(SecondaryStructureType.Flag.NA)
  40. function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
  41. return {
  42. center: StructureElement.create(unit),
  43. centerPrev: StructureElement.create(unit),
  44. centerNext: StructureElement.create(unit),
  45. first: false, last: false,
  46. secStrucFirst: false, secStrucLast: false,
  47. secStrucType: SecStrucTypeNA,
  48. moleculeType: MoleculeType.unknown,
  49. isCoarseBackbone: false,
  50. p0: Vec3.zero(), p1: Vec3.zero(), p2: Vec3.zero(), p3: Vec3.zero(), p4: Vec3.zero(),
  51. d12: Vec3.create(1, 0, 0), d23: Vec3.create(1, 0, 0),
  52. }
  53. }
  54. const enum AtomicPolymerTraceIteratorState { nextPolymer, nextResidue }
  55. export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> {
  56. private value: PolymerTraceElement
  57. private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
  58. private residueIt: Segmentation.SegmentIterator<ResidueIndex>
  59. private polymerSegment: Segmentation.Segment<ResidueIndex>
  60. private cyclicPolymerMap: Map<ResidueIndex, ResidueIndex>
  61. private secondaryStructureType: ArrayLike<SecondaryStructureType>
  62. private residueSegmentMin: ResidueIndex
  63. private residueSegmentMax: ResidueIndex
  64. private prevSecStrucType: SecondaryStructureType
  65. private currSecStrucType: SecondaryStructureType
  66. private nextSecStrucType: SecondaryStructureType
  67. private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer
  68. private residueAtomSegments: Segmentation<ElementIndex, ResidueIndex>
  69. private traceElementIndex: ArrayLike<ElementIndex>
  70. private directionElementIndex: ArrayLike<ElementIndex | -1>
  71. private moleculeType: ArrayLike<MoleculeType>
  72. private atomicConformation: AtomicConformation
  73. private p0 = Vec3.zero();
  74. private p1 = Vec3.zero();
  75. private p2 = Vec3.zero();
  76. private p3 = Vec3.zero();
  77. private p4 = Vec3.zero();
  78. private p5 = Vec3.zero();
  79. private p6 = Vec3.zero();
  80. // private v01 = Vec3.zero();
  81. private v12 = Vec3.create(1, 0, 0);
  82. private v23 = Vec3.create(1, 0, 0);
  83. // private v34 = Vec3.zero();
  84. hasNext: boolean = false;
  85. private pos(target: Vec3, index: number) {
  86. if (index !== -1) {
  87. target[0] = this.atomicConformation.x[index]
  88. target[1] = this.atomicConformation.y[index]
  89. target[2] = this.atomicConformation.z[index]
  90. }
  91. }
  92. private updateResidueSegmentRange(polymerSegment: Segmentation.Segment<ResidueIndex>) {
  93. const { index } = this.residueAtomSegments
  94. this.residueSegmentMin = index[this.unit.elements[polymerSegment.start]]
  95. this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]]
  96. }
  97. private getResidueIndex(residueIndex: number) {
  98. if (residueIndex < this.residueSegmentMin) {
  99. const cyclicIndex = this.cyclicPolymerMap.get(this.residueSegmentMin)
  100. if (cyclicIndex !== undefined) {
  101. residueIndex = cyclicIndex - (this.residueSegmentMin - residueIndex - 1)
  102. } else {
  103. residueIndex = this.residueSegmentMin
  104. }
  105. } else if (residueIndex > this.residueSegmentMax) {
  106. const cyclicIndex = this.cyclicPolymerMap.get(this.residueSegmentMax)
  107. if (cyclicIndex !== undefined) {
  108. residueIndex = cyclicIndex + (residueIndex - this.residueSegmentMax - 1)
  109. } else {
  110. residueIndex = this.residueSegmentMax
  111. }
  112. }
  113. return residueIndex as ResidueIndex
  114. }
  115. private setControlPoint(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: ResidueIndex) {
  116. const ss = this.secondaryStructureType[residueIndex]
  117. if (SecondaryStructureType.is(ss, SecondaryStructureType.Flag.Beta)) {
  118. Vec3.scale(out, Vec3.add(out, p1, Vec3.add(out, p3, Vec3.add(out, p2, p2))), 1/4)
  119. } else {
  120. Vec3.copy(out, p2)
  121. }
  122. }
  123. move() {
  124. const { residueIt, polymerIt, value } = this
  125. if (this.state === AtomicPolymerTraceIteratorState.nextPolymer) {
  126. while (polymerIt.hasNext) {
  127. this.polymerSegment = polymerIt.move();
  128. residueIt.setSegment(this.polymerSegment);
  129. this.updateResidueSegmentRange(this.polymerSegment)
  130. if (residueIt.hasNext) {
  131. this.state = AtomicPolymerTraceIteratorState.nextResidue
  132. this.currSecStrucType = SecStrucTypeNA
  133. this.nextSecStrucType = this.secondaryStructureType[this.residueSegmentMin]
  134. break
  135. }
  136. }
  137. }
  138. if (this.state === AtomicPolymerTraceIteratorState.nextResidue) {
  139. const { index: residueIndex } = residueIt.move();
  140. this.prevSecStrucType = this.currSecStrucType
  141. this.currSecStrucType = this.nextSecStrucType
  142. this.nextSecStrucType = residueIt.hasNext ? this.secondaryStructureType[residueIndex + 1] : SecStrucTypeNA
  143. value.secStrucType = this.currSecStrucType
  144. value.first = residueIndex === this.residueSegmentMin
  145. value.last = residueIndex === this.residueSegmentMax
  146. value.secStrucFirst = this.prevSecStrucType !== this.currSecStrucType
  147. value.secStrucLast = this.currSecStrucType !== this.nextSecStrucType
  148. value.moleculeType = this.moleculeType[residueIndex]
  149. const residueIndexPrev3 = this.getResidueIndex(residueIndex - 3)
  150. const residueIndexPrev2 = this.getResidueIndex(residueIndex - 2)
  151. const residueIndexPrev1 = this.getResidueIndex(residueIndex - 1)
  152. const residueIndexNext1 = this.getResidueIndex(residueIndex + 1)
  153. const residueIndexNext2 = this.getResidueIndex(residueIndex + 2)
  154. const residueIndexNext3 = this.getResidueIndex(residueIndex + 3)
  155. if (value.first) {
  156. value.centerPrev.element = this.traceElementIndex[residueIndexPrev1]
  157. value.center.element = this.traceElementIndex[residueIndex]
  158. this.pos(this.p0, this.traceElementIndex[residueIndexPrev3])
  159. this.pos(this.p1, this.traceElementIndex[residueIndexPrev2])
  160. this.pos(this.p2, this.traceElementIndex[residueIndexPrev1])
  161. this.pos(this.p3, this.traceElementIndex[residueIndex])
  162. this.pos(this.p4, this.traceElementIndex[residueIndexNext1])
  163. this.pos(this.p5, this.traceElementIndex[residueIndexNext2])
  164. this.pos(this.v12, this.directionElementIndex[residueIndexPrev1])
  165. } else {
  166. value.centerPrev.element = value.center.element
  167. value.center.element = value.centerNext.element
  168. Vec3.copy(this.p0, this.p1)
  169. Vec3.copy(this.p1, this.p2)
  170. Vec3.copy(this.p2, this.p3)
  171. Vec3.copy(this.p3, this.p4)
  172. Vec3.copy(this.p4, this.p5)
  173. Vec3.copy(this.p5, this.p6)
  174. Vec3.copy(this.v12, this.v23)
  175. }
  176. value.centerNext.element = this.traceElementIndex[residueIndexNext1]
  177. this.pos(this.p6, this.traceElementIndex[residueIndexNext3])
  178. this.pos(this.v23, this.directionElementIndex[residueIndex])
  179. value.isCoarseBackbone = this.directionElementIndex[residueIndex] === -1
  180. this.setControlPoint(value.p0, this.p0, this.p1, this.p2, residueIndexPrev2)
  181. this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndexPrev1)
  182. this.setControlPoint(value.p2, this.p2, this.p3, this.p4, residueIndex)
  183. this.setControlPoint(value.p3, this.p3, this.p4, this.p5, residueIndexNext1)
  184. this.setControlPoint(value.p4, this.p4, this.p5, this.p6, residueIndexNext2)
  185. Vec3.copy(value.d12, this.v12)
  186. Vec3.copy(value.d23, this.v23)
  187. if (!residueIt.hasNext) {
  188. this.state = AtomicPolymerTraceIteratorState.nextPolymer
  189. }
  190. }
  191. this.hasNext = residueIt.hasNext || polymerIt.hasNext
  192. return this.value;
  193. }
  194. constructor(private unit: Unit.Atomic) {
  195. this.atomicConformation = unit.model.atomicConformation
  196. this.residueAtomSegments = unit.model.atomicHierarchy.residueAtomSegments
  197. this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex as ArrayLike<ElementIndex> // can assume it won't be -1 for polymer residues
  198. this.directionElementIndex = unit.model.atomicHierarchy.derived.residue.directionElementIndex
  199. this.moleculeType = unit.model.atomicHierarchy.derived.residue.moleculeType
  200. this.cyclicPolymerMap = unit.model.atomicHierarchy.cyclicPolymerMap
  201. this.secondaryStructureType = unit.model.properties.secondaryStructure.type
  202. this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
  203. this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements);
  204. this.value = createPolymerTraceElement(unit)
  205. this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
  206. }
  207. }
  208. const enum CoarsePolymerTraceIteratorState { nextPolymer, nextElement }
  209. export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement> {
  210. private value: PolymerTraceElement
  211. private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
  212. private polymerSegment: Segmentation.Segment<ResidueIndex>
  213. private state: CoarsePolymerTraceIteratorState = CoarsePolymerTraceIteratorState.nextPolymer
  214. private conformation: CoarseSphereConformation | CoarseGaussianConformation
  215. private elementIndex: number
  216. hasNext: boolean = false;
  217. private getElementIndex(elementIndex: number) {
  218. return Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1) as ElementIndex
  219. }
  220. private pos(target: Vec3, elementIndex: number) {
  221. const index = this.unit.elements[elementIndex]
  222. target[0] = this.conformation.x[index]
  223. target[1] = this.conformation.y[index]
  224. target[2] = this.conformation.z[index]
  225. }
  226. move() {
  227. if (this.state === CoarsePolymerTraceIteratorState.nextPolymer) {
  228. while (this.polymerIt.hasNext) {
  229. this.polymerSegment = this.polymerIt.move();
  230. this.elementIndex = this.polymerSegment.start
  231. if (this.elementIndex + 1 < this.polymerSegment.end) {
  232. this.state = CoarsePolymerTraceIteratorState.nextElement
  233. break
  234. }
  235. }
  236. }
  237. if (this.state === CoarsePolymerTraceIteratorState.nextElement) {
  238. this.elementIndex += 1
  239. const elementIndexPrev2 = this.getElementIndex(this.elementIndex - 2)
  240. const elementIndexPrev1 = this.getElementIndex(this.elementIndex - 1)
  241. const elementIndexNext1 = this.getElementIndex(this.elementIndex + 1)
  242. const elementIndexNext2 = this.getElementIndex(this.elementIndex + 2)
  243. this.value.centerPrev.element = this.value.center.unit.elements[elementIndexPrev1]
  244. this.value.center.element = this.value.center.unit.elements[this.elementIndex]
  245. this.value.centerNext.element = this.value.center.unit.elements[elementIndexNext1]
  246. this.pos(this.value.p0, elementIndexPrev2)
  247. this.pos(this.value.p1, elementIndexPrev1)
  248. this.pos(this.value.p2, this.elementIndex)
  249. this.pos(this.value.p3, elementIndexNext1)
  250. this.pos(this.value.p4, elementIndexNext2)
  251. this.value.first = this.elementIndex === this.polymerSegment.start
  252. this.value.last = this.elementIndex === this.polymerSegment.end - 1
  253. if (this.elementIndex + 1 >= this.polymerSegment.end) {
  254. this.state = CoarsePolymerTraceIteratorState.nextPolymer
  255. }
  256. }
  257. this.hasNext = this.elementIndex + 1 < this.polymerSegment.end || this.polymerIt.hasNext
  258. return this.value;
  259. }
  260. constructor(private unit: Unit.Spheres | Unit.Gaussians) {
  261. this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
  262. this.value = createPolymerTraceElement(unit)
  263. switch (unit.kind) {
  264. case Unit.Kind.Spheres: this.conformation = unit.model.coarseConformation.spheres; break
  265. case Unit.Kind.Gaussians: this.conformation = unit.model.coarseConformation.gaussians; break
  266. }
  267. this.hasNext = this.polymerIt.hasNext
  268. }
  269. }