From b31e58d821de573a68e8b7981272b3d7591581ff Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Tue, 20 Nov 2018 15:24:33 -0800 Subject: [PATCH] added polymer-id color scheme, removed model.asymIdSerialMap --- .../structure/model/formats/mmcif.ts | 20 ---- src/mol-model/structure/model/model.ts | 2 - .../structure/representation/cartoon.ts | 2 +- .../representation/molecular-surface.ts | 2 +- src/mol-theme/color.ts | 2 + src/mol-theme/color/chain-id.ts | 25 +++-- src/mol-theme/color/polymer-id.ts | 104 ++++++++++++++++++ 7 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 src/mol-theme/color/polymer-id.ts diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index 155a94777..31c4dfc1e 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -88,24 +88,6 @@ function getModifiedResidueNameMap(format: mmCIF_Format): Model['properties']['m return { parentId, details }; } -function getAsymIdSerialMap(format: mmCIF_Format): ReadonlyMap<string, number> { - const data = format.data.struct_asym; - const map = new Map<string, number>(); - let serial = 0 - - const id = data.id - const count = data._rowCount - for (let i = 0; i < count; ++i) { - const _id = id.value(i) - if (!map.has(_id)) { - map.set(_id, serial) - serial += 1 - } - } - - return map; -} - function getChemicalComponentMap(format: mmCIF_Format): ChemicalComponentMap { const map = new Map<string, ChemicalComponent>(); const { id, type, name, pdbx_synonyms, formula, formula_weight } = format.data.chem_comp @@ -150,7 +132,6 @@ function getSaccharideComponentMap(format: mmCIF_Format): SaccharideComponentMap export interface FormatData { modifiedResidues: Model['properties']['modifiedResidues'] - asymIdSerialMap: Model['properties']['asymIdSerialMap'] chemicalComponentMap: Model['properties']['chemicalComponentMap'] saccharideComponentMap: Model['properties']['saccharideComponentMap'] } @@ -158,7 +139,6 @@ export interface FormatData { function getFormatData(format: mmCIF_Format): FormatData { return { modifiedResidues: getModifiedResidueNameMap(format), - asymIdSerialMap: getAsymIdSerialMap(format), chemicalComponentMap: getChemicalComponentMap(format), saccharideComponentMap: getSaccharideComponentMap(format) } diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 1eaad6e45..a9442fc94 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -47,8 +47,6 @@ export interface Model extends Readonly<{ parentId: ReadonlyMap<string, string>, details: ReadonlyMap<string, string> }>, - /** maps asym id to unique serial number */ - readonly asymIdSerialMap: ReadonlyMap<string, number> /** maps residue name to `ChemicalComponent` data */ readonly chemicalComponentMap: ChemicalComponentMap /** maps residue name to `SaccharideComponent` data */ diff --git a/src/mol-repr/structure/representation/cartoon.ts b/src/mol-repr/structure/representation/cartoon.ts index ccb54f933..4d21933e8 100644 --- a/src/mol-repr/structure/representation/cartoon.ts +++ b/src/mol-repr/structure/representation/cartoon.ts @@ -31,7 +31,7 @@ export const CartoonParams = { ...NucleotideBlockParams, ...PolymerDirectionParams, sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }), - colorTheme: PD.Mapped('polymer-index', BuiltInColorThemeOptions, getBuiltInColorThemeParams), + colorTheme: PD.Mapped('polymer-id', BuiltInColorThemeOptions, getBuiltInColorThemeParams), visuals: PD.MultiSelect<CartoonVisualName>(['polymer-trace', 'polymer-gap', 'nucleotide-block'], CartoonVisualOptions), } PD.getDefaultValues(CartoonParams).colorTheme.name diff --git a/src/mol-repr/structure/representation/molecular-surface.ts b/src/mol-repr/structure/representation/molecular-surface.ts index cdf89b914..71ac6eb26 100644 --- a/src/mol-repr/structure/representation/molecular-surface.ts +++ b/src/mol-repr/structure/representation/molecular-surface.ts @@ -27,7 +27,7 @@ export const MolecularSurfaceParams = { ...GaussianSurfaceParams, ...GaussianWireframeParams, ...GaussianDensityVolumeParams, - colorTheme: PD.Mapped('polymer-index', BuiltInColorThemeOptions, getBuiltInColorThemeParams), + colorTheme: PD.Mapped('polymer-id', BuiltInColorThemeOptions, getBuiltInColorThemeParams), visuals: PD.MultiSelect<MolecularSurfaceVisualName>(['gaussian-surface'], MolecularSurfaceVisualOptions), } PD.getDefaultValues(MolecularSurfaceParams).colorTheme.name diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts index 31c5b3fd5..52d9608cf 100644 --- a/src/mol-theme/color.ts +++ b/src/mol-theme/color.ts @@ -17,6 +17,7 @@ import { CrossLinkColorThemeProvider } from './color/cross-link'; import { ElementIndexColorThemeProvider } from './color/element-index'; import { ElementSymbolColorThemeProvider } from './color/element-symbol'; import { MoleculeTypeColorThemeProvider } from './color/molecule-type'; +import { PolymerIdColorThemeProvider } from './color/polymer-id'; import { PolymerIndexColorThemeProvider } from './color/polymer-index'; import { ResidueNameColorThemeProvider } from './color/residue-name'; import { SecondaryStructureColorThemeProvider } from './color/secondary-structure'; @@ -96,6 +97,7 @@ export const BuiltInColorThemes = { 'element-index': ElementIndexColorThemeProvider, 'element-symbol': ElementSymbolColorThemeProvider, 'molecule-type': MoleculeTypeColorThemeProvider, + 'polymer-id': PolymerIdColorThemeProvider, 'polymer-index': PolymerIndexColorThemeProvider, 'residue-name': ResidueNameColorThemeProvider, 'secondary-structure': SecondaryStructureColorThemeProvider, diff --git a/src/mol-theme/color/chain-id.ts b/src/mol-theme/color/chain-id.ts index 2983ce357..0d90464d8 100644 --- a/src/mol-theme/color/chain-id.ts +++ b/src/mol-theme/color/chain-id.ts @@ -12,6 +12,7 @@ import { ColorTheme, LocationColor } from '../color'; import { ParamDefinition as PD } from 'mol-util/param-definition' import { ThemeDataContext } from 'mol-theme/theme'; import { ColorListOptions, ColorListName } from 'mol-util/color/scale'; +import { Column } from 'mol-data/db'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives every chain a color based on its `asym_id` value.' @@ -34,6 +35,17 @@ function getAsymId(unit: Unit): StructureElement.Property<string> { } } +function addAsymIds(map: Map<string, number>, data: Column<string>) { + let j = map.size + for (let o = 0, ol = data.rowCount; o < ol; ++o) { + const k = data.value(o) + if (!map.has(k)) { + map.set(k, j) + j += 1 + } + } +} + export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThemeProps): ColorTheme<ChainIdColorThemeProps> { let color: LocationColor const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' }) @@ -42,14 +54,13 @@ export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThem const l = StructureElement.create() const { models } = ctx.structure const asymIdSerialMap = new Map<string, number>() - let j = 0 for (let i = 0, il = models.length; i <il; ++i) { - models[i].properties.asymIdSerialMap.forEach((v, k) => { - if (!asymIdSerialMap.has(k)) { - asymIdSerialMap.set(k, j) - j += 1 - } - }) + const m = models[i] + addAsymIds(asymIdSerialMap, m.atomicHierarchy.chains.label_asym_id) + if (m.coarseHierarchy.isDefined) { + addAsymIds(asymIdSerialMap, m.coarseHierarchy.spheres.asym_id) + addAsymIds(asymIdSerialMap, m.coarseHierarchy.gaussians.asym_id) + } } scale.setDomain(0, asymIdSerialMap.size - 1) const scaleColor = scale.color diff --git a/src/mol-theme/color/polymer-id.ts b/src/mol-theme/color/polymer-id.ts new file mode 100644 index 000000000..c543ca7e0 --- /dev/null +++ b/src/mol-theme/color/polymer-id.ts @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Unit, StructureProperties, StructureElement, Link } from 'mol-model/structure'; + +import { ColorScale, Color } from 'mol-util/color'; +import { Location } from 'mol-model/location'; +import { ColorTheme, LocationColor } from '../color'; +import { ParamDefinition as PD } from 'mol-util/param-definition' +import { ThemeDataContext } from 'mol-theme/theme'; +import { ColorListOptions, ColorListName } from 'mol-util/color/scale'; +import { Column } from 'mol-data/db'; +import { Entities } from 'mol-model/structure/model/properties/common'; + +const DefaultColor = Color(0xCCCCCC) +const Description = 'Gives every polymer chain a color based on its `asym_id` value.' + +export const PolymerIdColorThemeParams = { + list: PD.Select<ColorListName>('RdYlBu', ColorListOptions), +} +export function getPolymerIdColorThemeParams(ctx: ThemeDataContext) { + return PolymerIdColorThemeParams // TODO return copy +} +export type PolymerIdColorThemeProps = PD.Values<typeof PolymerIdColorThemeParams> + +function getAsymId(unit: Unit): StructureElement.Property<string> { + switch (unit.kind) { + case Unit.Kind.Atomic: + return StructureProperties.chain.label_asym_id + case Unit.Kind.Spheres: + case Unit.Kind.Gaussians: + return StructureProperties.coarse.asym_id + } +} + +function addPolymerAsymIds(map: Map<string, number>, asymId: Column<string>, entityId: Column<string>, entities: Entities) { + let j = map.size + for (let o = 0, ol = asymId.rowCount; o < ol; ++o) { + const e = entityId.value(o) + const eI = entities.getEntityIndex(e) + if (entities.data.type.value(eI) === 'polymer') { + const k = asymId.value(o) + if (!map.has(k)) { + map.set(k, j) + j += 1 + } + } + } +} + +export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PolymerIdColorThemeProps): ColorTheme<PolymerIdColorThemeProps> { + let color: LocationColor + const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' }) + + if (ctx.structure) { + const l = StructureElement.create() + const { models } = ctx.structure + const polymerAsymIdSerialMap = new Map<string, number>() + for (let i = 0, il = models.length; i <il; ++i) { + for (let i = 0, il = models.length; i <il; ++i) { + const m = models[i] + addPolymerAsymIds(polymerAsymIdSerialMap, m.atomicHierarchy.chains.label_asym_id, m.atomicHierarchy.chains.label_entity_id, m.entities) + if (m.coarseHierarchy.isDefined) { + addPolymerAsymIds(polymerAsymIdSerialMap, m.coarseHierarchy.spheres.asym_id, m.coarseHierarchy.spheres.entity_id, m.entities) + addPolymerAsymIds(polymerAsymIdSerialMap, m.coarseHierarchy.gaussians.asym_id, m.coarseHierarchy.spheres.entity_id, m.entities) + } + } + } + scale.setDomain(0, polymerAsymIdSerialMap.size - 1) + const scaleColor = scale.color + + color = (location: Location): Color => { + if (StructureElement.isLocation(location)) { + const asym_id = getAsymId(location.unit) + return scaleColor(polymerAsymIdSerialMap.get(asym_id(location)) || 0) + } else if (Link.isLocation(location)) { + const asym_id = getAsymId(location.aUnit) + l.unit = location.aUnit + l.element = location.aUnit.elements[location.aIndex] + return scaleColor(polymerAsymIdSerialMap.get(asym_id(l)) || 0) + } + return DefaultColor + } + } else { + color = () => DefaultColor + } + + return { + granularity: 'group', + color, + props, + description: Description, + legend: scale ? scale.legend : undefined + } +} + +export const PolymerIdColorThemeProvider: ColorTheme.Provider<typeof PolymerIdColorThemeParams> = { + label: 'Polymer Id', + factory: PolymerIdColorTheme, + getParams: getPolymerIdColorThemeParams +} \ No newline at end of file -- GitLab