hetero.ts 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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 { Structure, StructureElement, ResidueIndex, Unit } from '../../../mol-model/structure';
  7. import { SequenceWrapper, StructureUnit } from './wrapper';
  8. import { OrderedSet, Segmentation, Interval, SortedArray } from '../../../mol-data/int';
  9. import { Loci } from '../../../mol-model/loci';
  10. import { ColorNames } from '../../../mol-util/color/names';
  11. export class HeteroSequenceWrapper extends SequenceWrapper<StructureUnit> {
  12. private readonly unitMap: Map<number, Unit>
  13. private readonly sequence: string[]
  14. private readonly sequenceIndices: Map<ResidueIndex, number>
  15. private readonly residueIndices: Map<number, ResidueIndex>
  16. private readonly seqToUnit: Map<number, Unit>
  17. residueLabel(seqIdx: number) {
  18. return this.sequence[seqIdx]
  19. }
  20. residueColor(seqIdx: number) {
  21. return ColorNames.black
  22. }
  23. eachResidue(loci: Loci, apply: (set: OrderedSet) => boolean) {
  24. let changed = false
  25. const { structure } = this.data
  26. if (StructureElement.Loci.is(loci)) {
  27. if (!Structure.areRootsEquivalent(loci.structure, structure)) return false
  28. loci = StructureElement.Loci.remap(loci, structure)
  29. for (const e of loci.elements) {
  30. const unit = this.unitMap.get(e.unit.id)
  31. if (unit) {
  32. const { index: residueIndex } = e.unit.model.atomicHierarchy.residueAtomSegments
  33. OrderedSet.forEach(e.indices, v => {
  34. const seqIdx = this.sequenceIndices.get(residueIndex[unit.elements[v]])
  35. if (seqIdx !== undefined && apply(Interval.ofSingleton(seqIdx))) changed = true
  36. })
  37. }
  38. }
  39. } else if (Structure.isLoci(loci)) {
  40. if (!Structure.areRootsEquivalent(loci.structure, structure)) return false
  41. if (apply(Interval.ofBounds(0, this.length))) changed = true
  42. }
  43. return changed
  44. }
  45. getLoci(seqIdx: number) {
  46. const elements: StructureElement.Loci['elements'][0][] = []
  47. const rI = this.residueIndices.get(seqIdx)
  48. if (rI !== undefined) {
  49. const unit = this.seqToUnit.get(seqIdx)!
  50. const { offsets } = unit.model.atomicHierarchy.residueAtomSegments
  51. const start = SortedArray.findPredecessorIndex(unit.elements, offsets[rI])
  52. const end = SortedArray.findPredecessorIndex(unit.elements, offsets[rI + 1])
  53. elements.push({ unit, indices: Interval.ofBounds(start, end) })
  54. }
  55. return StructureElement.Loci(this.data.structure, elements)
  56. }
  57. constructor(data: StructureUnit) {
  58. const sequence: string[] = []
  59. const sequenceIndices = new Map<ResidueIndex, number>()
  60. const residueIndices = new Map<number, ResidueIndex>()
  61. const seqToUnit = new Map<number, Unit>()
  62. for (let i = 0, il = data.units.length; i < il; ++i) {
  63. const unit = data.units[i]
  64. const { residueAtomSegments, residues } = unit.model.atomicHierarchy
  65. const residueIt = Segmentation.transientSegments(residueAtomSegments, unit.elements)
  66. while (residueIt.hasNext) {
  67. const { index } = residueIt.move()
  68. sequenceIndices.set(index, sequence.length)
  69. residueIndices.set(sequence.length, index)
  70. seqToUnit.set(sequence.length, unit)
  71. sequence.push(residues.label_comp_id.value(index))
  72. }
  73. }
  74. const length = sequence.length
  75. const markerArray = new Uint8Array(length)
  76. super(data, markerArray, length)
  77. this.unitMap = new Map()
  78. for (const unit of data.units) this.unitMap.set(unit.id, unit)
  79. this.sequence = sequence
  80. this.sequenceIndices = sequenceIndices
  81. this.residueIndices = residueIndices
  82. this.seqToUnit = seqToUnit
  83. }
  84. }