123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /**
- * Copyright (C) 2022, Protein Bioinformatics Research Group, RCNS
- *
- * Licensed under CC BY-NC 4.0, see LICENSE file for more info.
- *
- * @author Gabor Tusnady <tusnady.gabor@ttk.hu>
- * @author Csongor Gerdan <gerdan.csongor@ttk.hu>
- */
- import { StructureElement, Unit } from 'molstar/lib/mol-model/structure';
- import { ColorTheme } from 'molstar/lib/mol-theme/color';
- import { ThemeDataContext } from 'molstar/lib/mol-theme/theme';
- import { Color } from 'molstar/lib/mol-util/color';
- import { ColorNames } from 'molstar/lib/mol-util/color/names';
- import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition';
- import { Location } from 'molstar/lib/mol-model/location';
- import { ChainList, getResidue, PDBTMChain } from './types';
- import { TmDetChainListCache, TmDetDescriptorCache } from './prop';
- export type TmDetColorThemeParams = {
- pdbtmDescriptor: any
- };
- export function TmDetColorTheme(
- ctx: ThemeDataContext,
- props: PD.Values<TmDetColorThemeParams>
- ): ColorTheme<TmDetColorThemeParams> {
- let descriptorChains: PDBTMChain[] = [];
- let pdbtmDescriptor = props.pdbtmDescriptor;
- const pdbId = ctx.structure?.model.entryId;
- // If it is not given as parameter,
- // but there is one in the cache for this structure.
- if (!pdbtmDescriptor) {
- if (pdbId) {
- pdbtmDescriptor = TmDetDescriptorCache.get(pdbId);
- }
- }
- if (pdbtmDescriptor) {
- descriptorChains = pdbtmDescriptor.chains;
- }
- const tmType = pdbtmDescriptor.additional_entry_annotations.tm_type;
- const chainList = createResidueListsPerChain(descriptorChains, pdbtmDescriptor.side1, tmType);
- if (pdbId) {
- TmDetChainListCache.set(pdbId, chainList);
- }
- return {
- factory: TmDetColorTheme,
- granularity: 'group', //'residue' as any,
- color: (location: Location) => getColor(location, chainList),
- props: props,
- description: 'TMDet...',
- };
- }
- const DefaultResidueColor = ColorNames.lightgrey;
- enum SiteIndexes {
- Side1 = 0,
- Side2 = 1,
- TmAlpha = 2,
- TmBeta = 3,
- TmReentrantLoop = 4,
- InterfacialHelix = 5,
- UnknownLocalization = 6,
- MembraneInside = 7,
- Periplasm = 8
- };
- // Old default values - it is overwritten by ult_* CSS classes
- // See below updateSiteColors().
- const siteColors = [
- Color.fromArray([255,100,100], 0), // Side1
- Color.fromArray([100,100,255], 0), // Side2
- Color.fromArray([255,255, 0], 0), // TM alpha
- Color.fromArray([255,255, 0], 0), // TM beta
- Color.fromArray([255,127, 0], 0), // TM re-entrant loop
- Color.fromArray([0,255, 0], 0), // Interfacial Helix
- Color.fromArray([196,196,196], 0), // Unknow localization
- Color.fromArray([0,255, 0], 0), // Membrane Inside
- Color.fromArray([255, 0, 255], 0) // Periplasm
- ];
- const siteCssNames = [
- "ult_side1",
- "ult_side2",
- "ult_alpha",
- "ult_beta",
- "ult_reentrant",
- "ult_ifh",
- "ult_unknown",
- "ult_membins",
- "ult_periplasm"
- ];
- const regionColorMapFromCss = new Map();
- // set default values
- siteCssNames.forEach((className, index) => {
- regionColorMapFromCss.set(className, siteColors[index]);
- });
- function getColor(location: Location, chains: ChainList): Color {
- let color = DefaultResidueColor;
- // TODO: How to solve cases when chain operation occurs?
- if (StructureElement.Location.is(location) && Unit.isAtomic(location.unit)) {
- const { chainId, residueId } = getChainAndResidueIds(location);
- color = residueColor(chains, chainId!, residueId!);
- }
- return color;
- }
- export function getChainAndResidueIds(location: Location) {
- let chainId = undefined;
- let residueId = undefined;
- if (StructureElement.Location.is(location) && Unit.isAtomic(location.unit)) {
- const atomicHierarchy = location.unit.model.atomicHierarchy;
- const residueIdx = StructureElement.residueIndex(location);
- const chainIdx = StructureElement.Location.chainIndex(location);
- residueId = atomicHierarchy.residues.label_seq_id.value(residueIdx).toString();
- chainId = atomicHierarchy.chains.label_asym_id.value(chainIdx).toString();
- }
- return {
- chainId: chainId,
- residueId: residueId
- };
- }
- export function createResidueListsPerChain(chains: PDBTMChain[], side1: string|null, tmType: string) {
- const chainList: ChainList = [];
- const hasBeta = chains.some(chain => chain.additional_chain_annotations.type == "beta");
- chains.forEach((chain: PDBTMChain) => {
- const chainType = chain.additional_chain_annotations.type;
- chainList.push({ chainId: chain.chain_label, type: chainType, residues: [] });
- let annotationErrorLogged = false;
- chain.residues.forEach((residue) => {
- let siteId = residue.site_data![0].site_id_ref - 1;
- let siteColorId = siteId;
- if (tmType == "Tm_Alpha" && hasBeta && side1 == "Periplasm") {
- if (siteId == SiteIndexes.Side1) {
- siteColorId = SiteIndexes.Periplasm;
- } else if (siteId == SiteIndexes.Side2) {
- if (chainType == "non_tm" || chainType == "alpha") {
- siteColorId = SiteIndexes.Side1;
- } else if (chainType == "beta") {
- siteColorId = SiteIndexes.Side2;
- }
- }
- } else if (chainType == "beta") {
- if (side1 == "Periplasm" && siteColorId == SiteIndexes.Side1
- || side1 == "Outside" && siteColorId == SiteIndexes.Side2) {
- siteColorId = SiteIndexes.Periplasm;
- } else if (!annotationErrorLogged && side1 == "Inside" && siteColorId == SiteIndexes.Side1) {
- console.error(`Annotation error: beta chain has inside region ${chain.chain_label}:${residue.pdb_res_label}`);
- annotationErrorLogged = true;
- }
- }
- chainList[chainList.length - 1].residues.push({
- authId: residue.pdb_res_label,
- siteId: siteId,
- siteColorId: siteColorId
- });
- });
- });
- return chainList;
- }
- function residueColor(chains: ChainList, chainId: string, residueId: string): Color {
- let color = DefaultResidueColor;
- const residue = getResidue(chains, chainId, residueId);
- if (residue) {
- color = siteColors[residue.siteColorId];
- }
- return color;
- }
- // Provider
- export const TmDetColorThemeProvider: ColorTheme.Provider<TmDetColorThemeParams, 'tmdet-custom-color-theme'> = {
- name: 'tmdet-custom-color-theme',
- label: 'TMDet Topology Theme',
- category: 'TMDet',
- factory: TmDetColorTheme,
- getParams: () => ({ pdbtmDescriptor: { isOptional: true } }),
- defaultValues: { pdbtmDescriptor: undefined },
- isApplicable: () => true,
- };
- // Colors from CSS rules
- function loadRegionColorsFromStyleSheets(prefix: string = 'ult_'): void {
- const sheets: CSSStyleSheet[] = Array.from(document.styleSheets);
- sheets.forEach((sheet: CSSStyleSheet) => {
- const rules: CSSRule[] = Array.from(sheet.cssRules);
- rules.forEach((rule: CSSRule) => fetchRule(rule, prefix));
- });
- }
- export function updateSiteColors(side1: "Inside"|"Outside"|null): void {
- if (!side1) {
- return;
- }
- loadRegionColorsFromStyleSheets();
- if (regionColorMapFromCss.size == 0) {
- console.warn('Cannot read any region-related color rules');
- }
- siteCssNames.forEach((ultClassName, index) => {
- const color = regionColorMapFromCss.get(ultClassName);
- if (color != null) {
- siteColors[index] = color;
- }
- });
- const inside = regionColorMapFromCss.get("ult_inside");
- const outside = regionColorMapFromCss.get("ult_outside");
- if (side1 == "Inside") {
- siteColors[SiteIndexes.Side1] = inside;
- siteColors[SiteIndexes.Side2] = outside;
- } else if (side1 == "Outside") {
- siteColors[SiteIndexes.Side1] = outside;
- siteColors[SiteIndexes.Side2] = inside;
- }
- }
- function fetchRule(rule: CSSRule, prefix: string) {
- let styleRule = rule as CSSStyleRule;
- if (styleRule.selectorText?.startsWith('.' + prefix)) {
- const value = styleRule.style.getPropertyValue('fill');
- const color: Color = getStyleColor(value);
- const key = styleRule.selectorText.slice(1);
- regionColorMapFromCss.set(key, color);
- }
- }
- function getStyleColor(cssColorText: string): Color {
- const values = cssColorText?.match(/\d+/g);
- let intValues = values?.map(value => parseInt(value));
- if (!intValues) {
- intValues = [ 0, 0, 0 ];
- }
- return Color.fromArray(intValues, 0);
- }
|