entity-source.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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 { StructureProperties, StructureElement, Link, Model } from '../../mol-model/structure';
  7. import { Color } from '../../mol-util/color';
  8. import { Location } from '../../mol-model/location';
  9. import { ColorTheme, LocationColor } from '../color';
  10. import { ParamDefinition as PD } from '../../mol-util/param-definition'
  11. import { ThemeDataContext } from '../../mol-theme/theme';
  12. import { ScaleLegend } from '../../mol-util/color/scale';
  13. import { Table, Column } from '../../mol-data/db';
  14. import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
  15. import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
  16. import { TableLegend } from '../../mol-util/color/lists';
  17. const DefaultColor = Color(0xFAFAFA)
  18. const Description = 'Gives ranges of a polymer chain a color based on the entity source it originates from. Genes get the same color per entity.'
  19. export const EntitySourceColorThemeParams = {
  20. ...getPaletteParams({ type: 'set', setList: 'set-3' }),
  21. }
  22. export type EntitySourceColorThemeParams = typeof EntitySourceColorThemeParams
  23. export function getEntitySourceColorThemeParams(ctx: ThemeDataContext) {
  24. const params = PD.clone(EntitySourceColorThemeParams)
  25. if (ctx.structure) {
  26. if (getMaps(ctx.structure.root.models).srcKeySerialMap.size > 12) {
  27. params.palette.defaultValue.name = 'scale'
  28. params.palette.defaultValue.params = { list: 'red-yellow-blue' }
  29. }
  30. }
  31. return params
  32. }
  33. function modelEntityKey(modelIndex: number, entityId: string) {
  34. return `${modelIndex}|${entityId}`
  35. }
  36. type EntitySrc = Table<{
  37. entity_id: mmCIF_Schema['entity_src_gen']['entity_id'],
  38. pdbx_src_id: mmCIF_Schema['entity_src_gen']['pdbx_src_id'],
  39. pdbx_beg_seq_num: mmCIF_Schema['entity_src_gen']['pdbx_beg_seq_num'],
  40. pdbx_end_seq_num: mmCIF_Schema['entity_src_gen']['pdbx_end_seq_num'],
  41. }>
  42. type GeneSrcGene = Column<mmCIF_Schema['entity_src_gen']['pdbx_gene_src_gene']['T']>
  43. function srcKey(modelIndex: number, entityId: string, srcId: number, gene: string) {
  44. return `${modelIndex}|${entityId}|${gene ? gene : srcId}`
  45. }
  46. function addSrc(seqToSrcByModelEntity: Map<string, Int16Array>, srcKeySerialMap: Map<string, number>, modelIndex: number, model: Model, entity_src: EntitySrc, gene_src_gene?: GeneSrcGene) {
  47. const { entity_id, pdbx_src_id, pdbx_beg_seq_num, pdbx_end_seq_num } = entity_src
  48. for (let j = 0, jl = entity_src._rowCount; j < jl; ++j) {
  49. const entityId = entity_id.value(j)
  50. const mK = modelEntityKey(modelIndex, entityId)
  51. let seqToSrc: Int16Array
  52. if (!seqToSrcByModelEntity.has(mK)) {
  53. const entityIndex = model.entities.getEntityIndex(entityId)
  54. const seq = model.sequence.sequences[entityIndex].sequence
  55. seqToSrc = new Int16Array(seq.sequence.length)
  56. seqToSrcByModelEntity.set(mK, seqToSrc)
  57. } else {
  58. seqToSrc = seqToSrcByModelEntity.get(mK)!
  59. }
  60. const sK = srcKey(modelIndex, entityId, pdbx_src_id.value(j), gene_src_gene ? gene_src_gene.value(j).join(',') : '')
  61. // may not be given (= 0) indicating src is for the whole seq
  62. const beg = pdbx_beg_seq_num.valueKind(j) === Column.ValueKind.Present ? pdbx_beg_seq_num.value(j) : 1
  63. const end = pdbx_end_seq_num.valueKind(j) === Column.ValueKind.Present ? pdbx_end_seq_num.value(j) : seqToSrc.length
  64. let srcIndex: number // serial no starting from 1
  65. if (srcKeySerialMap.has(sK)) {
  66. srcIndex = srcKeySerialMap.get(sK)!
  67. } else {
  68. srcIndex = srcKeySerialMap.size + 1
  69. srcKeySerialMap.set(sK, srcIndex)
  70. }
  71. // set src index
  72. for (let i = beg, il = end; i <= il; ++i) {
  73. seqToSrc[i - 1] = srcIndex
  74. }
  75. }
  76. }
  77. function getMaps(models: ReadonlyArray<Model>) {
  78. const seqToSrcByModelEntity = new Map<string, Int16Array>()
  79. const srcKeySerialMap = new Map<string, number>() // serial no starting from 1
  80. for (let i = 0, il = models.length; i <il; ++i) {
  81. const m = models[i]
  82. if (m.sourceData.kind !== 'mmCIF') continue
  83. const { entity_src_gen, entity_src_nat, pdbx_entity_src_syn } = m.sourceData.data
  84. addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, entity_src_gen, entity_src_gen.pdbx_gene_src_gene)
  85. addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, entity_src_nat)
  86. addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, pdbx_entity_src_syn)
  87. }
  88. return { seqToSrcByModelEntity, srcKeySerialMap }
  89. }
  90. export function EntitySourceColorTheme(ctx: ThemeDataContext, props: PD.Values<EntitySourceColorThemeParams>): ColorTheme<EntitySourceColorThemeParams> {
  91. let color: LocationColor
  92. let legend: ScaleLegend | TableLegend | undefined
  93. if (ctx.structure) {
  94. const l = StructureElement.Location.create()
  95. const { models } = ctx.structure.root
  96. const { seqToSrcByModelEntity, srcKeySerialMap } = getMaps(models)
  97. const palette = getPalette(srcKeySerialMap.size + 1, props)
  98. legend = palette.legend
  99. const getSrcColor = (location: StructureElement.Location) => {
  100. const modelIndex = models.indexOf(location.unit.model)
  101. const entityId = StructureProperties.entity.id(location)
  102. const mK = modelEntityKey(modelIndex, entityId)
  103. const seqToSrc = seqToSrcByModelEntity.get(mK)
  104. if (seqToSrc) {
  105. // minus 1 to convert seqId to array index
  106. return palette.color(seqToSrc[StructureProperties.residue.label_seq_id(location) - 1])
  107. } else {
  108. return DefaultColor
  109. }
  110. }
  111. color = (location: Location): Color => {
  112. if (StructureElement.Location.is(location)) {
  113. return getSrcColor(location)
  114. } else if (Link.isLocation(location)) {
  115. l.unit = location.aUnit
  116. l.element = location.aUnit.elements[location.aIndex]
  117. return getSrcColor(l)
  118. }
  119. return DefaultColor
  120. }
  121. } else {
  122. color = () => DefaultColor
  123. }
  124. return {
  125. factory: EntitySourceColorTheme,
  126. granularity: 'group',
  127. color,
  128. props,
  129. description: Description,
  130. legend
  131. }
  132. }
  133. export const EntitySourceColorThemeProvider: ColorTheme.Provider<EntitySourceColorThemeParams> = {
  134. label: 'Entity Source',
  135. factory: EntitySourceColorTheme,
  136. getParams: getEntitySourceColorThemeParams,
  137. defaultValues: PD.getDefaultValues(EntitySourceColorThemeParams),
  138. isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
  139. }