label.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import { Unit, StructureElement, StructureProperties as Props, Link } from '../mol-model/structure';
  8. import { Loci } from '../mol-model/loci';
  9. import { OrderedSet } from '../mol-data/int';
  10. // for `labelFirst`, don't create right away to avoid problems with circular dependencies/imports
  11. let elementLocA: StructureElement.Location
  12. let elementLocB: StructureElement.Location
  13. function setElementLocation(loc: StructureElement.Location, unit: Unit, index: StructureElement.UnitIndex) {
  14. loc.unit = unit
  15. loc.element = unit.elements[index]
  16. }
  17. export function lociLabel(loci: Loci): string {
  18. switch (loci.kind) {
  19. case 'structure-loci':
  20. return loci.structure.models.map(m => m.entry).join(', ')
  21. case 'element-loci':
  22. return structureElementStatsLabel(StructureElement.Stats.ofLoci(loci))
  23. case 'link-loci':
  24. const link = loci.links[0]
  25. return link ? linkLabel(link) : 'Unknown'
  26. case 'shape-loci':
  27. return loci.shape.name
  28. case 'group-loci':
  29. const g = loci.groups[0]
  30. return g ? loci.shape.getLabel(OrderedSet.start(g.ids), loci.instance) : 'Unknown'
  31. case 'every-loci':
  32. return 'Everything'
  33. case 'empty-loci':
  34. return 'Nothing'
  35. case 'data-loci':
  36. return ''
  37. }
  38. }
  39. function countLabel(count: number, label: string) {
  40. return count === 1 ? `1 ${label}` : `${count} ${label}s`
  41. }
  42. /** Gets residue count of the model chain segments the unit is a subset of */
  43. function getResidueCount(unit: Unit.Atomic) {
  44. const { elements, model } = unit
  45. const { chainAtomSegments, residueAtomSegments } = model.atomicHierarchy
  46. const elementStart = chainAtomSegments.offsets[chainAtomSegments.index[elements[0]]]
  47. const elementEnd = chainAtomSegments.offsets[chainAtomSegments.index[elements[elements.length - 1]] + 1]
  48. return residueAtomSegments.index[elementEnd] - residueAtomSegments.index[elementStart]
  49. }
  50. export function structureElementStatsLabel(stats: StructureElement.Stats, countsOnly = false) {
  51. const { unitCount, residueCount, elementCount } = stats
  52. if (!countsOnly && elementCount === 1 && residueCount === 0 && unitCount === 0) {
  53. return elementLabel(stats.firstElementLoc, 'element')
  54. } else if (!countsOnly && elementCount === 0 && residueCount === 1 && unitCount === 0) {
  55. return elementLabel(stats.firstResidueLoc, 'residue')
  56. } else if (!countsOnly && elementCount === 0 && residueCount === 0 && unitCount === 1) {
  57. const { unit } = stats.firstUnitLoc
  58. const granularity = (Unit.isAtomic(unit) && getResidueCount(unit) === 1) ? 'residue' : 'chain'
  59. return elementLabel(stats.firstUnitLoc, granularity)
  60. } else {
  61. const label: string[] = []
  62. if (unitCount > 0) label.push(countLabel(unitCount, 'Chain'))
  63. if (residueCount > 0) label.push(countLabel(residueCount, 'Residue'))
  64. if (elementCount > 0) label.push(countLabel(elementCount, 'Element'))
  65. return label.join(', ')
  66. }
  67. }
  68. export function linkLabel(link: Link.Location) {
  69. if (!elementLocA) elementLocA = StructureElement.Location.create()
  70. if (!elementLocB) elementLocB = StructureElement.Location.create()
  71. setElementLocation(elementLocA, link.aUnit, link.aIndex)
  72. setElementLocation(elementLocB, link.bUnit, link.bIndex)
  73. return `${elementLabel(elementLocA)} - ${elementLabel(elementLocB)}`
  74. }
  75. export type LabelGranularity = 'element' | 'residue' | 'chain' | 'structure'
  76. export function elementLabel(location: StructureElement.Location, granularity: LabelGranularity = 'element') {
  77. const entry = location.unit.model.entry
  78. const model = `Model ${location.unit.model.modelNum}`
  79. const instance = location.unit.conformation.operator.name
  80. const label = [entry, model, instance]
  81. if (Unit.isAtomic(location.unit)) {
  82. label.push(atomicElementLabel(location as StructureElement.Location<Unit.Atomic>, granularity))
  83. } else if (Unit.isCoarse(location.unit)) {
  84. label.push(coarseElementLabel(location as StructureElement.Location<Unit.Spheres | Unit.Gaussians>, granularity))
  85. } else {
  86. label.push('Unknown')
  87. }
  88. return label.join(' | ')
  89. }
  90. export function atomicElementLabel(location: StructureElement.Location<Unit.Atomic>, granularity: LabelGranularity) {
  91. const label_asym_id = Props.chain.label_asym_id(location)
  92. const auth_asym_id = Props.chain.auth_asym_id(location)
  93. const seq_id = location.unit.model.atomicHierarchy.residues.auth_seq_id.isDefined ? Props.residue.auth_seq_id(location) : Props.residue.label_seq_id(location)
  94. const comp_id = Props.residue.label_comp_id(location)
  95. const atom_id = Props.atom.label_atom_id(location)
  96. const alt_id = Props.atom.label_alt_id(location)
  97. const microHetCompIds = Props.residue.microheterogeneityCompIds(location)
  98. const compId = granularity === 'residue' && microHetCompIds.length > 1 ?
  99. `(${microHetCompIds.join('|')})` : comp_id
  100. const label: string[] = []
  101. switch (granularity) {
  102. case 'element':
  103. label.push(`${atom_id}${alt_id ? `%${alt_id}` : ''}`)
  104. case 'residue':
  105. label.push(`${compId} ${seq_id}`)
  106. case 'chain':
  107. label.push(`Chain ${label_asym_id}:${auth_asym_id}`)
  108. }
  109. return label.reverse().join(' | ')
  110. }
  111. export function coarseElementLabel(location: StructureElement.Location<Unit.Spheres | Unit.Gaussians>, granularity: LabelGranularity) {
  112. // TODO handle granularity
  113. const asym_id = Props.coarse.asym_id(location)
  114. const seq_id_begin = Props.coarse.seq_id_begin(location)
  115. const seq_id_end = Props.coarse.seq_id_end(location)
  116. if (seq_id_begin === seq_id_end) {
  117. const entityIndex = Props.coarse.entityKey(location)
  118. const seq = location.unit.model.sequence.byEntityKey[entityIndex]
  119. const comp_id = seq.compId.value(seq_id_begin - 1) // 1-indexed
  120. return `${comp_id} ${seq_id_begin}:${asym_id}`
  121. } else {
  122. return `${seq_id_begin}-${seq_id_end}:${asym_id}`
  123. }
  124. }