polymer.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  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, Element, StructureProperties, Model } from 'mol-model/structure';
  7. import { Segmentation, OrderedSet, Interval } 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 { SymmetryOperator } from 'mol-math/geometry';
  12. import SortedRanges from 'mol-data/int/sorted-ranges';
  13. export function getPolymerRanges(unit: Unit) {
  14. switch (unit.kind) {
  15. case Unit.Kind.Atomic: return unit.model.atomicHierarchy.polymerRanges
  16. case Unit.Kind.Spheres: return unit.model.coarseHierarchy.spheres.polymerRanges
  17. case Unit.Kind.Gaussians: return unit.model.coarseHierarchy.gaussians.polymerRanges
  18. }
  19. }
  20. export function getPolymerElementCount(unit: Unit) {
  21. let count = 0
  22. const { elements } = unit
  23. const polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), elements)
  24. switch (unit.kind) {
  25. case Unit.Kind.Atomic:
  26. const residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, elements)
  27. while (polymerIt.hasNext) {
  28. const polymerSegment = polymerIt.move()
  29. residueIt.setSegment(polymerSegment)
  30. while (residueIt.hasNext) {
  31. const residueSegment = residueIt.move()
  32. const { start, end } = residueSegment
  33. if (OrderedSet.areIntersecting(Interval.ofBounds(elements[start], elements[end - 1]), elements)) ++count
  34. }
  35. }
  36. break
  37. case Unit.Kind.Spheres:
  38. case Unit.Kind.Gaussians:
  39. while (polymerIt.hasNext) {
  40. const { start, end } = polymerIt.move()
  41. // TODO add OrderedSet.intersectionSize
  42. count += OrderedSet.size(OrderedSet.intersect(Interval.ofBounds(elements[start], elements[end - 1]), elements))
  43. }
  44. break
  45. }
  46. return count
  47. }
  48. function getTraceName(l: Element.Location) {
  49. const compId = StructureProperties.residue.label_comp_id(l)
  50. const chemCompMap = l.unit.model.properties.chemicalComponentMap
  51. const cc = chemCompMap.get(compId)
  52. const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
  53. let traceName = ''
  54. if (moleculeType === MoleculeType.protein) {
  55. traceName = 'CA'
  56. } else if (moleculeType === MoleculeType.DNA || moleculeType === MoleculeType.RNA) {
  57. traceName = 'P'
  58. }
  59. return traceName
  60. }
  61. function setTraceElement(l: Element.Location, residueSegment: Segmentation.Segment<Element>) {
  62. const elements = l.unit.elements
  63. l.element = elements[residueSegment.start]
  64. const traceName = getTraceName(l)
  65. for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
  66. l.element = elements[j];
  67. if (StructureProperties.atom.label_atom_id(l) === traceName) return j
  68. }
  69. return residueSegment.end - 1
  70. }
  71. function getTraceName2(model: Model, residueModelIndex: number) {
  72. const compId = model.atomicHierarchy.residues.label_comp_id.value(residueModelIndex)
  73. const chemCompMap = model.properties.chemicalComponentMap
  74. const cc = chemCompMap.get(compId)
  75. const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
  76. let traceName = ''
  77. if (moleculeType === MoleculeType.protein) {
  78. traceName = 'CA'
  79. } else if (moleculeType === MoleculeType.DNA) {
  80. // traceName = 'P'
  81. traceName = 'C3\''
  82. } else if (moleculeType === MoleculeType.RNA) {
  83. // traceName = 'P'
  84. traceName = 'C4\''
  85. }
  86. return traceName
  87. }
  88. function getTraceElement2(model: Model, residueModelSegment: Segmentation.Segment<Element>) {
  89. const traceName = getTraceName2(model, residueModelSegment.index)
  90. for (let j = residueModelSegment.start, _j = residueModelSegment.end; j < _j; j++) {
  91. if (model.atomicHierarchy.atoms.label_atom_id.value(j) === traceName) return j
  92. }
  93. console.log(`trace name element "${traceName}" not found`, { ...residueModelSegment })
  94. return residueModelSegment.start
  95. }
  96. function getDirectionName2(model: Model, residueModelIndex: number) {
  97. const compId = model.atomicHierarchy.residues.label_comp_id.value(residueModelIndex)
  98. const chemCompMap = model.properties.chemicalComponentMap
  99. const cc = chemCompMap.get(compId)
  100. const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
  101. let traceName = ''
  102. if (moleculeType === MoleculeType.protein) {
  103. traceName = 'O'
  104. } else if (moleculeType === MoleculeType.DNA) {
  105. traceName = 'O4\''
  106. } else if (moleculeType === MoleculeType.RNA) {
  107. traceName = 'C3\''
  108. } else {
  109. console.log('molecule type unknown', Number.isInteger(residueModelIndex) ? compId : residueModelIndex, chemCompMap)
  110. }
  111. return traceName
  112. }
  113. // function residueLabel(model: Model, rI: number) {
  114. // const { residues, chains, residueSegments, chainSegments } = model.atomicHierarchy
  115. // const { label_comp_id, label_seq_id } = residues
  116. // const { label_asym_id } = chains
  117. // const cI = chainSegments.segmentMap[residueSegments.segments[rI]]
  118. // return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)}`
  119. // }
  120. function getDirectionElement2(model: Model, residueModelSegment: Segmentation.Segment<Element>) {
  121. const traceName = getDirectionName2(model, residueModelSegment.index)
  122. for (let j = residueModelSegment.start, _j = residueModelSegment.end; j < _j; j++) {
  123. if (model.atomicHierarchy.atoms.label_atom_id.value(j) === traceName) return j
  124. }
  125. // console.log('direction name element not found', { ...residueModelSegment })
  126. return residueModelSegment.start
  127. }
  128. /** Iterates over consecutive pairs of residues/coarse elements in polymers */
  129. export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> {
  130. switch (unit.kind) {
  131. case Unit.Kind.Atomic: return new AtomicPolymerBackboneIterator(unit)
  132. case Unit.Kind.Spheres:
  133. case Unit.Kind.Gaussians:
  134. return new CoarsePolymerBackboneIterator(unit)
  135. }
  136. }
  137. interface PolymerBackbonePair {
  138. centerA: Element.Location
  139. centerB: Element.Location
  140. indexA: number
  141. indexB: number
  142. posA: Vec3
  143. posB: Vec3
  144. }
  145. function createPolymerBackbonePair (unit: Unit) {
  146. return {
  147. centerA: Element.Location(unit),
  148. centerB: Element.Location(unit),
  149. indexA: 0,
  150. indexB: 0,
  151. posA: Vec3.zero(),
  152. posB: Vec3.zero()
  153. }
  154. }
  155. const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextResidue }
  156. export class AtomicPolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> {
  157. private value: PolymerBackbonePair
  158. private polymerIt: SortedRanges.Iterator<Element>
  159. private residueIt: Segmentation.Iterator<Element>
  160. private polymerSegment: Segmentation.Segment<Element>
  161. private state: AtomicPolymerBackboneIteratorState = AtomicPolymerBackboneIteratorState.nextPolymer
  162. private pos: SymmetryOperator.CoordinateMapper
  163. hasNext: boolean = false;
  164. move() {
  165. const { residueIt, polymerIt, value, pos } = this
  166. if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) {
  167. while (polymerIt.hasNext) {
  168. this.polymerSegment = polymerIt.move();
  169. // console.log('polymerSegment', this.polymerSegment)
  170. residueIt.setSegment(this.polymerSegment);
  171. const residueSegment = residueIt.move();
  172. // console.log('first residueSegment', residueSegment, residueIt.hasNext)
  173. if (residueIt.hasNext) {
  174. value.indexB = setTraceElement(value.centerB, residueSegment)
  175. pos(value.centerB.element, value.posB)
  176. this.state = AtomicPolymerBackboneIteratorState.nextResidue
  177. break
  178. }
  179. }
  180. }
  181. if (this.state === AtomicPolymerBackboneIteratorState.nextResidue) {
  182. const residueSegment = residueIt.move();
  183. // console.log('next residueSegment', residueSegment)
  184. value.centerA.element = value.centerB.element
  185. value.indexA = value.indexB
  186. Vec3.copy(value.posA, value.posB)
  187. value.indexB = setTraceElement(value.centerB, residueSegment)
  188. pos(value.centerB.element, value.posB)
  189. if (!residueIt.hasNext) {
  190. // TODO need to advance to a polymer that has two or more residues (can't assume it has)
  191. this.state = AtomicPolymerBackboneIteratorState.nextPolymer
  192. }
  193. }
  194. this.hasNext = residueIt.hasNext || polymerIt.hasNext
  195. // console.log('hasNext', this.hasNext)
  196. // console.log('value', this.value)
  197. return this.value;
  198. }
  199. constructor(unit: Unit.Atomic) {
  200. const { residueSegments } = unit.model.atomicHierarchy
  201. // console.log('unit.elements', OrderedSet.toArray(unit.elements))
  202. this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
  203. this.residueIt = Segmentation.transientSegments(residueSegments, unit.elements)
  204. this.pos = unit.conformation.invariantPosition
  205. this.value = createPolymerBackbonePair(unit)
  206. this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
  207. }
  208. }
  209. const enum CoarsePolymerBackboneIteratorState { nextPolymer, nextElement }
  210. export class CoarsePolymerBackboneIterator<T extends number = number> implements Iterator<PolymerBackbonePair> {
  211. private value: PolymerBackbonePair
  212. private polymerIt: SortedRanges.Iterator<Element>
  213. private polymerSegment: Segmentation.Segment<Element>
  214. private state: CoarsePolymerBackboneIteratorState = CoarsePolymerBackboneIteratorState.nextPolymer
  215. private pos: SymmetryOperator.CoordinateMapper
  216. private elementIndex: number
  217. hasNext: boolean = false;
  218. move() {
  219. const { polymerIt, value, pos } = this
  220. if (this.state === CoarsePolymerBackboneIteratorState.nextPolymer) {
  221. if (polymerIt.hasNext) {
  222. this.polymerSegment = polymerIt.move();
  223. console.log('polymer', this.polymerSegment)
  224. this.elementIndex = this.polymerSegment.start
  225. this.elementIndex += 1
  226. if (this.elementIndex + 1 < this.polymerSegment.end) {
  227. value.centerB.element = value.centerB.unit.elements[this.elementIndex]
  228. value.indexB = this.elementIndex
  229. pos(value.centerB.element, value.posB)
  230. this.state = CoarsePolymerBackboneIteratorState.nextElement
  231. } else {
  232. this.state = CoarsePolymerBackboneIteratorState.nextPolymer
  233. }
  234. }
  235. }
  236. if (this.state === CoarsePolymerBackboneIteratorState.nextElement) {
  237. this.elementIndex += 1
  238. value.centerA.element = value.centerB.element
  239. value.indexA = value.indexB
  240. Vec3.copy(value.posA, value.posB)
  241. value.centerB.element = value.centerB.unit.elements[this.elementIndex]
  242. value.indexB = this.elementIndex
  243. pos(value.centerB.element, value.posB)
  244. if (this.elementIndex + 1 >= this.polymerSegment.end) {
  245. this.state = CoarsePolymerBackboneIteratorState.nextPolymer
  246. }
  247. }
  248. this.hasNext = this.elementIndex + 1 < this.polymerSegment.end || polymerIt.hasNext
  249. return this.value;
  250. }
  251. constructor(unit: Unit.Spheres | Unit.Gaussians) {
  252. this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
  253. this.pos = unit.conformation.invariantPosition
  254. this.value = createPolymerBackbonePair(unit)
  255. console.log('CoarsePolymerBackboneIterator', this.polymerIt.hasNext)
  256. this.hasNext = this.polymerIt.hasNext
  257. }
  258. }
  259. /**
  260. * Iterates over individual residues/coarse elements in polymers of a unit while
  261. * providing information about the neighbourhood in the underlying model for drawing splines
  262. */
  263. export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> {
  264. switch (unit.kind) {
  265. case Unit.Kind.Atomic: return new AtomicPolymerTraceIterator(unit)
  266. case Unit.Kind.Spheres:
  267. case Unit.Kind.Gaussians:
  268. return new CoarsePolymerTraceIterator(unit)
  269. }
  270. }
  271. interface PolymerTraceElement {
  272. center: Element.Location
  273. index: number
  274. first: boolean
  275. last: boolean
  276. secStrucType: SecondaryStructureType
  277. t0: Vec3
  278. t1: Vec3
  279. t2: Vec3
  280. t3: Vec3
  281. t4: Vec3
  282. d12: Vec3
  283. d23: Vec3
  284. }
  285. function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
  286. return {
  287. center: Element.Location(unit),
  288. index: 0,
  289. first: false,
  290. last: false,
  291. secStrucType: SecondaryStructureType.create(SecondaryStructureType.Flag.NA),
  292. t0: Vec3.zero(),
  293. t1: Vec3.zero(),
  294. t2: Vec3.zero(),
  295. t3: Vec3.zero(),
  296. t4: Vec3.zero(),
  297. d12: Vec3.zero(),
  298. d23: Vec3.zero(),
  299. }
  300. }
  301. const enum AtomicPolymerTraceIteratorState { nextPolymer, nextResidue }
  302. function setSegment (outSegment: Segmentation.Segment<Element>, index: number, segments: Segmentation<Element>, min: number, max: number): Segmentation.Segment<Element> {
  303. // index = Math.min(Math.max(0, index), segments.segments.length - 2)
  304. const _index = Math.min(Math.max(min, index), max)
  305. if (isNaN(_index)) console.log(_index, index, min, max)
  306. outSegment.index = _index
  307. outSegment.start = segments.segments[_index]
  308. outSegment.end = segments.segments[_index + 1]
  309. // console.log(index, {...outSegment}, {...boundingSegment}, segments.segments[boundingSegment.index])
  310. return outSegment
  311. }
  312. export class AtomicPolymerTraceIterator<T extends number = number> implements Iterator<PolymerTraceElement> {
  313. private value: PolymerTraceElement
  314. private polymerIt: SortedRanges.Iterator<Element>
  315. private residueIt: Segmentation.Iterator<Element>
  316. private residueSegmentMin: number
  317. private residueSegmentMax: number
  318. private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer
  319. private residueSegments: Segmentation<Element>
  320. private tmpSegment: Segmentation.Segment<Element>
  321. private unit: Unit.Atomic
  322. hasNext: boolean = false;
  323. private pos(target: Vec3, index: number) {
  324. target[0] = this.unit.model.atomicConformation.x[index]
  325. target[1] = this.unit.model.atomicConformation.y[index]
  326. target[2] = this.unit.model.atomicConformation.z[index]
  327. }
  328. updateResidueSegmentRange(polymerSegment: Segmentation.Segment<Element>) {
  329. const { polymerRanges, residueSegments } = this.unit.model.atomicHierarchy
  330. const sMin = polymerRanges[polymerSegment.index * 2]
  331. const sMax = polymerRanges[polymerSegment.index * 2 + 1]
  332. this.residueSegmentMin = residueSegments.segmentMap[sMin]
  333. this.residueSegmentMax = residueSegments.segmentMap[sMax]
  334. }
  335. move() {
  336. const { residueIt, polymerIt, value } = this
  337. if (this.state === AtomicPolymerTraceIteratorState.nextPolymer) {
  338. while (polymerIt.hasNext) {
  339. const polymerSegment = polymerIt.move();
  340. // console.log('polymerSegment', {...polymerSegment})
  341. residueIt.setSegment(polymerSegment);
  342. this.updateResidueSegmentRange(polymerSegment)
  343. if (residueIt.hasNext) {
  344. this.state = AtomicPolymerTraceIteratorState.nextResidue
  345. break
  346. }
  347. }
  348. }
  349. if (this.state === AtomicPolymerTraceIteratorState.nextResidue) {
  350. const { tmpSegment, residueSegments, residueSegmentMin, residueSegmentMax } = this
  351. const residueSegment = residueIt.move();
  352. const resSegIdx = residueSegment.index
  353. // console.log(residueLabel(this.unit.model, resSegIdx), resSegIdx, this.unit.model.properties.secondaryStructure.type[resSegIdx])
  354. value.index = setTraceElement(value.center, residueSegment)
  355. setSegment(tmpSegment, resSegIdx - 2, residueSegments, residueSegmentMin, residueSegmentMax)
  356. this.pos(value.t0, getTraceElement2(this.unit.model, tmpSegment))
  357. setSegment(tmpSegment, resSegIdx - 1, residueSegments, residueSegmentMin, residueSegmentMax)
  358. this.pos(value.t1, getTraceElement2(this.unit.model, tmpSegment))
  359. this.pos(value.d12, getDirectionElement2(this.unit.model, tmpSegment))
  360. setSegment(tmpSegment, resSegIdx, residueSegments, residueSegmentMin, residueSegmentMax)
  361. value.secStrucType = this.unit.model.properties.secondaryStructure.type[resSegIdx]
  362. this.pos(value.t2, getTraceElement2(this.unit.model, tmpSegment))
  363. this.pos(value.d23, getDirectionElement2(this.unit.model, tmpSegment))
  364. setSegment(tmpSegment, resSegIdx + 1, residueSegments, residueSegmentMin, residueSegmentMax)
  365. this.pos(value.t3, getTraceElement2(this.unit.model, tmpSegment))
  366. setSegment(tmpSegment, resSegIdx + 2, residueSegments, residueSegmentMin, residueSegmentMax)
  367. this.pos(value.t4, getTraceElement2(this.unit.model, tmpSegment))
  368. value.first = resSegIdx === residueSegmentMin
  369. value.last = resSegIdx === residueSegmentMax
  370. if (!residueIt.hasNext) {
  371. this.state = AtomicPolymerTraceIteratorState.nextPolymer
  372. }
  373. }
  374. this.hasNext = residueIt.hasNext || polymerIt.hasNext
  375. return this.value;
  376. }
  377. constructor(unit: Unit.Atomic) {
  378. const { residueSegments } = unit.model.atomicHierarchy
  379. this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
  380. this.residueIt = Segmentation.transientSegments(residueSegments, unit.elements);
  381. this.residueSegments = residueSegments
  382. this.value = createPolymerTraceElement(unit)
  383. this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
  384. this.tmpSegment = { index: 0, start: 0 as Element, end: 0 as Element }
  385. this.unit = unit
  386. }
  387. }
  388. export class CoarsePolymerTraceIterator<T extends number = number> implements Iterator<PolymerTraceElement> {
  389. private value: PolymerTraceElement
  390. hasNext: boolean = false;
  391. move() {
  392. return this.value;
  393. }
  394. constructor(unit: Unit.Spheres | Unit.Gaussians) {
  395. this.value = createPolymerTraceElement(unit)
  396. this.hasNext = false
  397. }
  398. }