polymer.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { StructureSelection, StructureQuery, Structure, Queries, StructureProperties as SP, StructureElement, Unit, ElementIndex } from '../../../mol-model/structure';
  7. import { SequenceWrapper, StructureUnit } from './wrapper';
  8. import { OrderedSet, Interval, SortedArray } from '../../../mol-data/int';
  9. import { Loci } from '../../../mol-model/loci';
  10. import { Sequence } from '../../../mol-model/sequence';
  11. import { MissingResidues } from '../../../mol-model/structure/model/properties/common';
  12. import { ColorNames } from '../../../mol-util/color/names';
  13. export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
  14. private readonly sequence: Sequence
  15. private readonly missing: MissingResidues
  16. private readonly observed: OrderedSet // sequences indices
  17. private readonly modelNum: number
  18. private readonly asymId: string
  19. seqId(seqIdx: number) {
  20. return this.sequence.offset + seqIdx + 1
  21. }
  22. residueLabel(seqIdx: number) {
  23. return this.sequence.sequence[seqIdx]
  24. }
  25. residueColor(seqIdx: number) {
  26. return this.missing.has(this.modelNum, this.asymId, this.seqId(seqIdx))
  27. ? ColorNames.grey
  28. : ColorNames.black
  29. }
  30. eachResidue(loci: Loci, apply: (set: OrderedSet) => boolean) {
  31. let changed = false
  32. const { structure, unit } = this.data
  33. if (StructureElement.isLoci(loci)) {
  34. if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
  35. loci = StructureElement.Loci.remap(loci, structure)
  36. const { offset } = this.sequence
  37. for (const e of loci.elements) {
  38. if (e.unit.id === unit.id) {
  39. OrderedSet.forEach(e.indices, v => {
  40. if (apply(getSeqIndices(e.unit, e.unit.elements[v], offset))) changed = true
  41. })
  42. }
  43. }
  44. } else if (Structure.isLoci(loci)) {
  45. if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
  46. if (apply(this.observed)) changed = true
  47. }
  48. return changed
  49. }
  50. getLoci(seqIdx: number) {
  51. const query = createResidueQuery(this.data.unit.id, this.seqId(seqIdx));
  52. return StructureSelection.toLoci2(StructureQuery.run(query, this.data.structure));
  53. }
  54. constructor(data: StructureUnit) {
  55. const l = StructureElement.create(data.unit, data.unit.elements[0])
  56. const sequence = data.unit.model.sequence.byEntityKey[SP.entity.key(l)].sequence
  57. const length = sequence.sequence.length
  58. const markerArray = new Uint8Array(length)
  59. super(data, markerArray, length)
  60. this.sequence = sequence
  61. this.missing = data.unit.model.properties.missingResidues
  62. this.modelNum = data.unit.model.modelNum
  63. this.asymId = Unit.isAtomic(data.unit) ? SP.chain.label_asym_id(l) : SP.coarse.asym_id(l)
  64. const missing: number[] = []
  65. for (let i = 0; i < length; ++i) {
  66. if (this.missing.has(this.modelNum, this.asymId, this.seqId(i))) missing.push(i)
  67. }
  68. this.observed = OrderedSet.subtract(Interval.ofBounds(0, length), SortedArray.ofSortedArray(missing))
  69. }
  70. }
  71. function createResidueQuery(unitId: number, label_seq_id: number) {
  72. return Queries.generators.atoms({
  73. unitTest: ctx => {
  74. return SP.unit.id(ctx.element) === unitId
  75. },
  76. residueTest: ctx => {
  77. if (ctx.element.unit.kind === Unit.Kind.Atomic) {
  78. return SP.residue.label_seq_id(ctx.element) === label_seq_id
  79. } else {
  80. return (
  81. SP.coarse.seq_id_begin(ctx.element) <= label_seq_id &&
  82. SP.coarse.seq_id_end(ctx.element) >= label_seq_id
  83. )
  84. }
  85. }
  86. });
  87. }
  88. function getSeqIndices(unit: Unit, element: ElementIndex, offset: number): Interval {
  89. const { model } = unit
  90. switch (unit.kind) {
  91. case Unit.Kind.Atomic:
  92. const residueIndex = model.atomicHierarchy.residueAtomSegments.index[element]
  93. const seqId = model.atomicHierarchy.residues.label_seq_id.value(residueIndex)
  94. return Interval.ofSingleton(seqId - 1 - offset)
  95. case Unit.Kind.Spheres:
  96. return Interval.ofRange(
  97. model.coarseHierarchy.spheres.seq_id_begin.value(element) - 1 - offset,
  98. model.coarseHierarchy.spheres.seq_id_end.value(element) - 1 - offset
  99. )
  100. case Unit.Kind.Gaussians:
  101. return Interval.ofRange(
  102. model.coarseHierarchy.gaussians.seq_id_begin.value(element) - 1 - offset,
  103. model.coarseHierarchy.gaussians.seq_id_end.value(element) - 1 - offset
  104. )
  105. }
  106. }