diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index 155a94777881da609608b77cea9d6a0620d21446..31c4dfc1e41bffce4526dd59ee2302d9bde12fb2 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 1eaad6e4554730175c54c80fb651b27cb71ac2e3..a9442fc948c6ffe8e1de0f19236ec750760e8a68 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 ccb54f9332aca73d05b0e589cc31cbe7352a5028..4d21933e882b5a8274eabd8eee013a36c9d51f58 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 cdf89b914e547ee033b46552881bc8d4383440ca..71ac6eb26fa22999ce6aaa6b7dfde09dd76927fb 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 31c5b3fd542397ba368eb6e7ed53c751621b69b3..52d9608cf8e9269fd24926dabda99bea3eadb597 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 2983ce357fef282afe23182257b8e558fc8cb59f..0d90464d8fafe845e6db32af937870f0fa44d3cc 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 0000000000000000000000000000000000000000..c543ca7e09eaeaed62e1411dd44577e3302a68bc --- /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