From aa898f39ddb2f24a6a2c75c402574c1a1d29dfb2 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 7 Sep 2018 18:46:14 -0700 Subject: [PATCH] basic support for color-theme description and legend --- src/apps/canvas/component/app.tsx | 12 ++-- .../component/structure-representation.tsx | 40 +++++++++-- src/apps/canvas/component/structure-view.tsx | 10 ++- src/apps/canvas/index.html | 10 +++ .../structure/carbohydrates/constants.ts | 13 ++++ src/mol-util/color/color.ts | 4 ++ src/mol-util/color/scale.ts | 11 ++- src/mol-view/theme/color.ts | 24 ++++++- .../theme/color/carbohydrate-symbol.ts | 12 +++- src/mol-view/theme/color/chain-id.ts | 68 ++++++++++++------- src/mol-view/theme/color/cross-link.ts | 14 +++- src/mol-view/theme/color/custom.ts | 4 +- src/mol-view/theme/color/element-index.ts | 16 +++-- src/mol-view/theme/color/element-symbol.ts | 14 +++- src/mol-view/theme/color/shape-group.ts | 4 +- src/mol-view/theme/color/uniform.ts | 7 +- src/mol-view/theme/color/unit-index.ts | 11 ++- 17 files changed, 212 insertions(+), 62 deletions(-) diff --git a/src/apps/canvas/component/app.tsx b/src/apps/canvas/component/app.tsx index 2186e90c6..6fe5f0872 100644 --- a/src/apps/canvas/component/app.tsx +++ b/src/apps/canvas/component/app.tsx @@ -46,13 +46,13 @@ export class AppComponent extends React.Component<AppProps, AppState> { const { structureView } = this.state return <div style={{width: '100%', height: '100%'}}> - <div style={{float: 'left', width: '70%', height: '100%'}}> + <div style={{left: '0px', right: '350px', height: '100%', position: 'absolute'}}> <Viewport app={this.props.app} /> </div> - <div style={{float: 'right', width: '25%', height: '100%'}}> - <div> - <span>Load PDB ID</span> + <div style={{width: '330px', paddingLeft: '10px', paddingRight: '10px', right: '0px', height: '100%', position: 'absolute', overflow: 'auto'}}> + <div style={{marginTop: '10px'}}> + <span>Load PDB ID </span> <input type='text' onKeyDown={e => { @@ -66,7 +66,7 @@ export class AppComponent extends React.Component<AppProps, AppState> { /> </div> <div> - <span>Load CIF file</span> + <span>Load CIF file </span> <input accept='*.cif' type='file' @@ -76,7 +76,7 @@ export class AppComponent extends React.Component<AppProps, AppState> { /> </div> <hr/> - <div> + <div style={{marginBottom: '10px'}}> {structureView ? <StructureViewComponent structureView={structureView} /> : ''} </div> </div> diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx index f91bfb00a..25eb022ae 100644 --- a/src/apps/canvas/component/structure-representation.tsx +++ b/src/apps/canvas/component/structure-representation.tsx @@ -8,7 +8,8 @@ import * as React from 'react' import { StructureRepresentation, StructureProps } from 'mol-geo/representation/structure'; import Viewer from 'mol-view/viewer'; import { VisualQuality, VisualQualityNames } from 'mol-geo/representation/util'; -import { ColorThemeProps, ColorThemeName, ColorThemeNames } from 'mol-view/theme/color'; +import { ColorThemeProps, ColorThemeName, ColorThemeNames, ColorTheme } from 'mol-view/theme/color'; +import { Color } from 'mol-util/color'; export interface StructureRepresentationComponentProps { viewer: Viewer @@ -67,28 +68,59 @@ export class StructureRepresentationComponent extends React.Component<StructureR render() { const { label, visible, quality, colorTheme } = this.state + const ct = ColorTheme(colorTheme) + + if (ct.legend && ct.legend.kind === 'scale-legend') { + // console.log(`linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})`) + } + return <div> <div> <h4>{label}</h4> </div> <div> <div> - <span>Visible</span> + <span>Visible </span> <button onClick={(e) => this.update({ visible: !visible }) }> {visible ? 'Hide' : 'Show'} </button> </div> <div> - <span>Quality</span> + <span>Quality </span> <select value={quality} onChange={(e) => this.update({ quality: e.target.value as VisualQuality }) }> {VisualQualityNames.map(name => <option key={name} value={name}>{name}</option>)} </select> </div> <div> - <span>Color Theme</span> + <span>Color Theme </span> <select value={colorTheme.name} onChange={(e) => this.update({ colorTheme: { name: e.target.value as ColorThemeName } }) }> {ColorThemeNames.map(name => <option key={name} value={name}>{name}</option>)} </select> + {ct.description ? <div><i>{ct.description}</i></div> : ''} + { + ct.legend && ct.legend.kind === 'scale-legend' + ? <div + style={{ + width: '100%', + height: '30px', + background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})` + }} + > + <span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.min}</span> + <span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.max}</span> + </div> + : ct.legend && ct.legend.kind === 'table-legend' + ? <div> + {ct.legend.table.map((value, i) => { + const [name, color] = value + return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}> + <div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div> + {name} + </div> + })} + </div> + : '' + } </div> </div> </div>; diff --git a/src/apps/canvas/component/structure-view.tsx b/src/apps/canvas/component/structure-view.tsx index c00eb8e83..6f05f7403 100644 --- a/src/apps/canvas/component/structure-view.tsx +++ b/src/apps/canvas/component/structure-view.tsx @@ -112,8 +112,9 @@ export class StructureViewComponent extends React.Component<StructureViewCompone </div> <div> <div> - <span>Model</span> + <span>Model </span> <select + style={{width: '100px'}} value={this.state.modelId} onChange={(e) => { this.update({ modelId: parseInt(e.target.value) }) @@ -121,6 +122,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone > {modelIdOptions} </select> + <span> </span> <input type='range' value={this.state.modelId} min={Math.min(...modelIds.map(m => m.id))} @@ -133,8 +135,9 @@ export class StructureViewComponent extends React.Component<StructureViewCompone </input> </div> <div> - <span>Assembly</span> + <span>Assembly </span> <select + style={{width: '150px'}} value={this.state.assemblyId} onChange={(e) => { this.update({ assemblyId: e.target.value }) @@ -144,8 +147,9 @@ export class StructureViewComponent extends React.Component<StructureViewCompone </select> </div> <div> - <span>Symmetry Feature</span> + <span>Symmetry Feature </span> <select + style={{width: '150px'}} value={this.state.symmetryFeatureId} onChange={(e) => { this.update({ symmetryFeatureId: parseInt(e.target.value) }) diff --git a/src/apps/canvas/index.html b/src/apps/canvas/index.html index 8bd125a03..bc9f73aa6 100644 --- a/src/apps/canvas/index.html +++ b/src/apps/canvas/index.html @@ -14,6 +14,16 @@ height: 100%; overflow: hidden; } + hr { + margin: 10px; + } + h1, h2, h3, h4, h5 { + margin-top: 5px; + margin-bottom: 3px; + } + button { + padding: 2px; + } </style> </head> <body> diff --git a/src/mol-model/structure/structure/carbohydrates/constants.ts b/src/mol-model/structure/structure/carbohydrates/constants.ts index cad746542..ed3949f12 100644 --- a/src/mol-model/structure/structure/carbohydrates/constants.ts +++ b/src/mol-model/structure/structure/carbohydrates/constants.ts @@ -174,6 +174,19 @@ const Monosaccharides: SaccharideComponent[] = [ { abbr: 'Psi', name: 'Psicose', color: SaccharideColors.Pink, type: SaccharideType.Assigned }, ] +export const MonosaccharidesColorTable: [string, Color][] = [ + ['Glc-family', SaccharideColors.Blue], + ['Man-family', SaccharideColors.Green], + ['Gal-family', SaccharideColors.Yellow], + ['Gul-family', SaccharideColors.Orange], + ['Alt-family', SaccharideColors.Pink], + ['All-family', SaccharideColors.Purple], + ['Tal-family', SaccharideColors.LightBlue], + ['Ido-family', SaccharideColors.Blue], + ['Fuc-family', SaccharideColors.Red], + ['Generic/Unknown/Secondary', SaccharideColors.Secondary], +] + const CommonSaccharideNames: { [k: string]: string[] } = { // Hexose Glc: [ diff --git a/src/mol-util/color/color.ts b/src/mol-util/color/color.ts index ea0c4efc0..764875f94 100644 --- a/src/mol-util/color/color.ts +++ b/src/mol-util/color/color.ts @@ -10,6 +10,10 @@ export type Color = { readonly '@type': 'color' } & number export function Color(hex: number) { return hex as Color } export namespace Color { + export function toStyle(hexColor: Color) { + return `rgb(${hexColor >> 16 & 255}, ${hexColor >> 8 & 255}, ${hexColor & 255})` + } + export function toRgb(hexColor: Color) { return [ hexColor >> 16 & 255, hexColor >> 8 & 255, hexColor & 255 ] } diff --git a/src/mol-util/color/scale.ts b/src/mol-util/color/scale.ts index f03b75399..ed486d44d 100644 --- a/src/mol-util/color/scale.ts +++ b/src/mol-util/color/scale.ts @@ -6,6 +6,7 @@ import { Color } from './color' import { ColorBrewer } from './tables' +import { ScaleLegend } from 'mol-view/theme/color'; export interface ColorScale { /** Returns hex color for given value */ @@ -14,19 +15,22 @@ export interface ColorScale { colorToArray: (value: number, array: Helpers.NumberArray, offset: number) => void /** Copies normalized (0 to 1) hex color to rgb array */ normalizedColorToArray: (value: number, array: Helpers.NumberArray, offset: number) => void + /** */ + readonly legend: ScaleLegend } export const DefaultColorScale = { domain: [0, 1], reverse: false, - colors: ColorBrewer.RdYlBu as Color[] + colors: ColorBrewer.RdYlBu, } export type ColorScaleProps = Partial<typeof DefaultColorScale> export namespace ColorScale { export function create(props: ColorScaleProps): ColorScale { - const { domain, reverse, colors } = { ...DefaultColorScale, ...props } - const [ min, max ] = reverse ? domain.slice().reverse() : domain + const { domain, reverse, colors: _colors } = { ...DefaultColorScale, ...props } + const [ min, max ] = domain + const colors = reverse ? _colors.slice().reverse() : _colors const count1 = colors.length - 1 const diff = (max - min) || 1 @@ -45,6 +49,7 @@ export namespace ColorScale { normalizedColorToArray: (value: number, array: Helpers.NumberArray, offset: number) => { Color.toArrayNormalized(color(value), array, offset) }, + get legend() { return ScaleLegend(min, max, colors) } } } } diff --git a/src/mol-view/theme/color.ts b/src/mol-view/theme/color.ts index 55ef2d83a..7b1f0e4d2 100644 --- a/src/mol-view/theme/color.ts +++ b/src/mol-view/theme/color.ts @@ -21,9 +21,29 @@ import { ColorType } from 'mol-geo/util/color-data'; export type LocationColor = (location: Location, isSecondary: boolean) => Color +export interface ScaleLegend { + kind: 'scale-legend' + min: number, + max: number, + colors: Color[] +} +export function ScaleLegend(min: number, max: number, colors: Color[]): ScaleLegend { + return { kind: 'scale-legend', min, max, colors } +} + +export interface TableLegend { + kind: 'table-legend' + table: [ string, Color ][] +} +export function TableLegend(table: [ string, Color ][]): TableLegend { + return { kind: 'table-legend', table } +} + export interface ColorTheme { granularity: ColorType color: LocationColor + description?: string + legend?: ScaleLegend | TableLegend } export function ColorTheme(props: ColorThemeProps): ColorTheme { @@ -46,7 +66,9 @@ export interface ColorThemeProps { value?: Color structure?: Structure color?: LocationColor - granularity?: ColorType + granularity?: ColorType, + description?: string, + legend?: ScaleLegend | TableLegend } export const ColorThemeInfo = { diff --git a/src/mol-view/theme/color/carbohydrate-symbol.ts b/src/mol-view/theme/color/carbohydrate-symbol.ts index dd9000296..c777c91d5 100644 --- a/src/mol-view/theme/color/carbohydrate-symbol.ts +++ b/src/mol-view/theme/color/carbohydrate-symbol.ts @@ -6,12 +6,13 @@ import { StructureElement, Link, ElementIndex, Unit } from 'mol-model/structure'; -import { SaccharideColors } from 'mol-model/structure/structure/carbohydrates/constants'; +import { SaccharideColors, MonosaccharidesColorTable } from 'mol-model/structure/structure/carbohydrates/constants'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme, LocationColor } from '../color'; +import { ColorThemeProps, ColorTheme, LocationColor, TableLegend } from '../color'; import { Color } from 'mol-util/color'; const DefaultColor = Color(0xCCCCCC) +const Description = 'Assigns colors according to the Symbol Nomenclature for Glycans (SNFG).' export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme { let color: LocationColor @@ -45,5 +46,10 @@ export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme color = () => DefaultColor } - return { granularity: 'group', color: color } + return { + granularity: 'group', + color: color, + description: Description, + legend: TableLegend(MonosaccharidesColorTable) + } } \ No newline at end of file diff --git a/src/mol-view/theme/color/chain-id.ts b/src/mol-view/theme/color/chain-id.ts index fa8e7b57d..e281dc9a2 100644 --- a/src/mol-view/theme/color/chain-id.ts +++ b/src/mol-view/theme/color/chain-id.ts @@ -8,9 +8,10 @@ import { Unit, StructureProperties, StructureElement, Link } from 'mol-model/str import { ColorScale, Color } from 'mol-util/color'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme } from '../color'; +import { ColorThemeProps, ColorTheme, LocationColor } from '../color'; const DefaultColor = Color(0xCCCCCC) +const Description = 'Gives every chain a color based on its `asym_id` value.' function getAsymId(unit: Unit): StructureElement.Property<string> { switch (unit.kind) { @@ -23,34 +24,49 @@ function getAsymId(unit: Unit): StructureElement.Property<string> { } export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme { - const l = StructureElement.create() - - const scaleMap = new Map<number, ColorScale>() - function getScale(size: number) { - let scale = scaleMap.get(size) - if (!scale) { - scale = ColorScale.create({ domain: [ 0, size - 1 ] }) - scaleMap.set(size, scale) + let color: LocationColor + let scale: ColorScale | undefined = undefined + // const table: [string, Color][] = [] + + if (props.structure) { + const l = StructureElement.create() + const { models } = props.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 + } + }) } - return scale - } + scale = ColorScale.create({ domain: [ 0, asymIdSerialMap.size - 1 ] }) + const scaleColor = scale.color - function color(location: Location): Color { - if (StructureElement.isLocation(location)) { - const map = location.unit.model.properties.asymIdSerialMap - const scale = getScale(map.size) - const asym_id = getAsymId(location.unit) - return scale.color(map.get(asym_id(location)) || 0) - } else if (Link.isLocation(location)) { - const map = location.aUnit.model.properties.asymIdSerialMap - const scale = getScale(map.size) - const asym_id = getAsymId(location.aUnit) - l.unit = location.aUnit - l.element = location.aUnit.elements[location.aIndex] - return scale.color(map.get(asym_id(l)) || 0) + // asymIdSerialMap.forEach((v, k) => table.push([k, scaleColor(v)])) + + color = (location: Location): Color => { + if (StructureElement.isLocation(location)) { + const asym_id = getAsymId(location.unit) + return scaleColor(asymIdSerialMap.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(asymIdSerialMap.get(asym_id(l)) || 0) + } + return DefaultColor } - return DefaultColor + } else { + color = () => DefaultColor } - return { granularity: 'group', color } + return { + granularity: 'group', + color, + description: Description, + // legend: scale ? TableLegend(table) : undefined + legend: scale ? scale.legend : undefined + } } \ No newline at end of file diff --git a/src/mol-view/theme/color/cross-link.ts b/src/mol-view/theme/color/cross-link.ts index b869b3f5d..6f8ab371e 100644 --- a/src/mol-view/theme/color/cross-link.ts +++ b/src/mol-view/theme/color/cross-link.ts @@ -12,6 +12,7 @@ import { ColorThemeProps, ColorTheme, LocationColor } from '../color'; import { Vec3 } from 'mol-math/linear-algebra'; const DefaultColor = Color(0xCCCCCC) +const Description = 'Colors cross-links by the deviation of the observed distance versus the modeled distance (e.g. `ihm_cross_link_restraint.distance_threshold`).' const distVecA = Vec3.zero(), distVecB = Vec3.zero() function linkDistance(link: Link.Location) { @@ -22,16 +23,18 @@ function linkDistance(link: Link.Location) { export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme { let color: LocationColor + let scale: ColorScale | undefined = undefined if (props.structure) { const crosslinks = props.structure.crossLinkRestraints - const scale = ColorScale.create({ domain: [ -10, 10 ], colors: ColorBrewer.RdYlBu }) + scale = ColorScale.create({ domain: [ -10, 10 ], colors: ColorBrewer.RdYlBu }) + const scaleColor = scale.color color = (location: Location): Color => { if (Link.isLocation(location)) { const pairs = crosslinks.getPairs(location.aIndex, location.aUnit, location.bIndex, location.bUnit) if (pairs) { - return scale.color(linkDistance(location) - pairs[0].distanceThreshold) + return scaleColor(linkDistance(location) - pairs[0].distanceThreshold) } } return DefaultColor @@ -40,5 +43,10 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme { color = () => DefaultColor } - return { granularity: 'group', color } + return { + granularity: 'group', + color, + description: Description, + legend: scale ? scale.legend : undefined + } } \ No newline at end of file diff --git a/src/mol-view/theme/color/custom.ts b/src/mol-view/theme/color/custom.ts index 08c8781b4..4901ac06f 100644 --- a/src/mol-view/theme/color/custom.ts +++ b/src/mol-view/theme/color/custom.ts @@ -14,6 +14,8 @@ export function CustomColorTheme(props: ColorThemeProps): ColorTheme { const value = defaults(props.value, DefaultColor) return { granularity: defaults(props.granularity, 'uniform'), - color: defaults(props.color, () => value) + color: defaults(props.color, () => value), + description: props.description, + legend: props.legend } } \ No newline at end of file diff --git a/src/mol-view/theme/color/element-index.ts b/src/mol-view/theme/color/element-index.ts index 3440f402a..8ca2d273a 100644 --- a/src/mol-view/theme/color/element-index.ts +++ b/src/mol-view/theme/color/element-index.ts @@ -11,9 +11,11 @@ import { OrderedSet } from 'mol-data/int'; import { ColorThemeProps, ColorTheme, LocationColor } from '../color'; const DefaultColor = Color(0xCCCCCC) +const Description = 'Gives every element (atom or coarse sphere/gaussian) a unique color based on the position (index) of the element in the list of elements in the structure.' export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme { let color: LocationColor + let scale: ColorScale | undefined = undefined if (props.structure) { const { units } = props.structure @@ -27,16 +29,17 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme { elementCount += units[i].elements.length unitIdIndex.set(units[i].id, i) } - const scale = ColorScale.create({ domain: [ 0, elementCount - 1 ] }) + scale = ColorScale.create({ domain: [ 0, elementCount - 1 ] }) + const scaleColor = scale.color color = (location: Location): Color => { if (StructureElement.isLocation(location)) { const unitIndex = unitIdIndex.get(location.unit.id)! const unitElementIndex = OrderedSet.findPredecessorIndex(location.unit.elements, location.element) - return scale.color(cummulativeElementCount.get(unitIndex)! + unitElementIndex) + return scaleColor(cummulativeElementCount.get(unitIndex)! + unitElementIndex) } else if (Link.isLocation(location)) { const unitIndex = unitIdIndex.get(location.aUnit.id)! - return scale.color(cummulativeElementCount.get(unitIndex)! + location.aIndex) + return scaleColor(cummulativeElementCount.get(unitIndex)! + location.aIndex) } return DefaultColor } @@ -44,5 +47,10 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme { color = () => DefaultColor } - return { granularity: 'groupInstance', color } + return { + granularity: 'groupInstance', + color, + description: Description, + legend: scale ? scale.legend : undefined + } } \ No newline at end of file diff --git a/src/mol-view/theme/color/element-symbol.ts b/src/mol-view/theme/color/element-symbol.ts index 1922afc99..c73ae2d46 100644 --- a/src/mol-view/theme/color/element-symbol.ts +++ b/src/mol-view/theme/color/element-symbol.ts @@ -8,14 +8,15 @@ import { ElementSymbol } from 'mol-model/structure/model/types'; import { Color, ColorMap } from 'mol-util/color'; import { StructureElement, Unit, Link } from 'mol-model/structure'; import { Location } from 'mol-model/location'; -import { ColorThemeProps, ColorTheme } from '../color'; +import { ColorThemeProps, ColorTheme, TableLegend } from '../color'; // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF) export const ElementSymbolColors = ColorMap({ - 'H': 0xFFFFFF, 'HE': 0xD9FFFF, 'LI': 0xCC80FF, 'BE': 0xC2FF00, 'B': 0xFFB5B5, 'C': 0x909090, 'N': 0x3050F8, 'O': 0xFF0D0D, 'F': 0x90E050, 'NE': 0xB3E3F5, 'NA': 0xAB5CF2, 'MG': 0x8AFF00, 'AL': 0xBFA6A6, 'SI': 0xF0C8A0, 'P': 0xFF8000, 'S': 0xFFFF30, 'CL': 0x1FF01F, 'AR': 0x80D1E3, 'K': 0x8F40D4, 'CA': 0x3DFF00, 'SC': 0xE6E6E6, 'TI': 0xBFC2C7, 'V': 0xA6A6AB, 'CR': 0x8A99C7, 'MN': 0x9C7AC7, 'FE': 0xE06633, 'CO': 0xF090A0, 'NI': 0x50D050, 'CU': 0xC88033, 'ZN': 0x7D80B0, 'GA': 0xC28F8F, 'GE': 0x668F8F, 'AS': 0xBD80E3, 'SE': 0xFFA100, 'BR': 0xA62929, 'KR': 0x5CB8D1, 'RB': 0x702EB0, 'SR': 0x00FF00, 'Y': 0x94FFFF, 'ZR': 0x94E0E0, 'NB': 0x73C2C9, 'MO': 0x54B5B5, 'TC': 0x3B9E9E, 'RU': 0x248F8F, 'RH': 0x0A7D8C, 'PD': 0x006985, 'AG': 0xC0C0C0, 'CD': 0xFFD98F, 'IN': 0xA67573, 'SN': 0x668080, 'SB': 0x9E63B5, 'TE': 0xD47A00, 'I': 0x940094, 'XE': 0x940094, 'CS': 0x57178F, 'BA': 0x00C900, 'LA': 0x70D4FF, 'CE': 0xFFFFC7, 'PR': 0xD9FFC7, 'ND': 0xC7FFC7, 'PM': 0xA3FFC7, 'SM': 0x8FFFC7, 'EU': 0x61FFC7, 'GD': 0x45FFC7, 'TB': 0x30FFC7, 'DY': 0x1FFFC7, 'HO': 0x00FF9C, 'ER': 0x00E675, 'TM': 0x00D452, 'YB': 0x00BF38, 'LU': 0x00AB24, 'HF': 0x4DC2FF, 'TA': 0x4DA6FF, 'W': 0x2194D6, 'RE': 0x267DAB, 'OS': 0x266696, 'IR': 0x175487, 'PT': 0xD0D0E0, 'AU': 0xFFD123, 'HG': 0xB8B8D0, 'TL': 0xA6544D, 'PB': 0x575961, 'BI': 0x9E4FB5, 'PO': 0xAB5C00, 'AT': 0x754F45, 'RN': 0x428296, 'FR': 0x420066, 'RA': 0x007D00, 'AC': 0x70ABFA, 'TH': 0x00BAFF, 'PA': 0x00A1FF, 'U': 0x008FFF, 'NP': 0x0080FF, 'PU': 0x006BFF, 'AM': 0x545CF2, 'CM': 0x785CE3, 'BK': 0x8A4FE3, 'CF': 0xA136D4, 'ES': 0xB31FD4, 'FM': 0xB31FBA, 'MD': 0xB30DA6, 'NO': 0xBD0D87, 'LR': 0xC70066, 'RF': 0xCC0059, 'DB': 0xD1004F, 'SG': 0xD90045, 'BH': 0xE00038, 'HS': 0xE6002E, 'MT': 0xEB0026, 'DS': 0xFFFFFF, 'RG': 0xFFFFFF, 'CN': 0xFFFFFF, 'UUT': 0xFFFFFF, 'FL': 0xFFFFFF, 'UUP': 0xFFFFFF, 'LV': 0xFFFFFF, 'UUH': 0xFFFFFF, 'D': 0xFFFFC0, 'T': 0xFFFFA0 + 'H': 0xFFFFFF, 'D': 0xFFFFC0, 'T': 0xFFFFA0, 'HE': 0xD9FFFF, 'LI': 0xCC80FF, 'BE': 0xC2FF00, 'B': 0xFFB5B5, 'C': 0x909090, 'N': 0x3050F8, 'O': 0xFF0D0D, 'F': 0x90E050, 'NE': 0xB3E3F5, 'NA': 0xAB5CF2, 'MG': 0x8AFF00, 'AL': 0xBFA6A6, 'SI': 0xF0C8A0, 'P': 0xFF8000, 'S': 0xFFFF30, 'CL': 0x1FF01F, 'AR': 0x80D1E3, 'K': 0x8F40D4, 'CA': 0x3DFF00, 'SC': 0xE6E6E6, 'TI': 0xBFC2C7, 'V': 0xA6A6AB, 'CR': 0x8A99C7, 'MN': 0x9C7AC7, 'FE': 0xE06633, 'CO': 0xF090A0, 'NI': 0x50D050, 'CU': 0xC88033, 'ZN': 0x7D80B0, 'GA': 0xC28F8F, 'GE': 0x668F8F, 'AS': 0xBD80E3, 'SE': 0xFFA100, 'BR': 0xA62929, 'KR': 0x5CB8D1, 'RB': 0x702EB0, 'SR': 0x00FF00, 'Y': 0x94FFFF, 'ZR': 0x94E0E0, 'NB': 0x73C2C9, 'MO': 0x54B5B5, 'TC': 0x3B9E9E, 'RU': 0x248F8F, 'RH': 0x0A7D8C, 'PD': 0x006985, 'AG': 0xC0C0C0, 'CD': 0xFFD98F, 'IN': 0xA67573, 'SN': 0x668080, 'SB': 0x9E63B5, 'TE': 0xD47A00, 'I': 0x940094, 'XE': 0x940094, 'CS': 0x57178F, 'BA': 0x00C900, 'LA': 0x70D4FF, 'CE': 0xFFFFC7, 'PR': 0xD9FFC7, 'ND': 0xC7FFC7, 'PM': 0xA3FFC7, 'SM': 0x8FFFC7, 'EU': 0x61FFC7, 'GD': 0x45FFC7, 'TB': 0x30FFC7, 'DY': 0x1FFFC7, 'HO': 0x00FF9C, 'ER': 0x00E675, 'TM': 0x00D452, 'YB': 0x00BF38, 'LU': 0x00AB24, 'HF': 0x4DC2FF, 'TA': 0x4DA6FF, 'W': 0x2194D6, 'RE': 0x267DAB, 'OS': 0x266696, 'IR': 0x175487, 'PT': 0xD0D0E0, 'AU': 0xFFD123, 'HG': 0xB8B8D0, 'TL': 0xA6544D, 'PB': 0x575961, 'BI': 0x9E4FB5, 'PO': 0xAB5C00, 'AT': 0x754F45, 'RN': 0x428296, 'FR': 0x420066, 'RA': 0x007D00, 'AC': 0x70ABFA, 'TH': 0x00BAFF, 'PA': 0x00A1FF, 'U': 0x008FFF, 'NP': 0x0080FF, 'PU': 0x006BFF, 'AM': 0x545CF2, 'CM': 0x785CE3, 'BK': 0x8A4FE3, 'CF': 0xA136D4, 'ES': 0xB31FD4, 'FM': 0xB31FBA, 'MD': 0xB30DA6, 'NO': 0xBD0D87, 'LR': 0xC70066, 'RF': 0xCC0059, 'DB': 0xD1004F, 'SG': 0xD90045, 'BH': 0xE00038, 'HS': 0xE6002E, 'MT': 0xEB0026, 'DS': 0xFFFFFF, 'RG': 0xFFFFFF, 'CN': 0xFFFFFF, 'UUT': 0xFFFFFF, 'FL': 0xFFFFFF, 'UUP': 0xFFFFFF, 'LV': 0xFFFFFF, 'UUH': 0xFFFFFF }) const DefaultElementSymbolColor = Color(0xFFFFFF) +const Description = 'Assigns a color to every atom according to its chemical element.' export function elementSymbolColor(element: ElementSymbol): Color { const c = (ElementSymbolColors as { [k: string]: Color })[element]; @@ -38,5 +39,12 @@ export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme { return DefaultElementSymbolColor } - return { granularity: 'group', color } + return { + granularity: 'group', + color, + description: Description, + legend: TableLegend(Object.keys(ElementSymbolColors).map(name => { + return [name, (ElementSymbolColors as any)[name] as Color] as [string, Color] + })) + } } \ No newline at end of file diff --git a/src/mol-view/theme/color/shape-group.ts b/src/mol-view/theme/color/shape-group.ts index 0615daa35..6743791a3 100644 --- a/src/mol-view/theme/color/shape-group.ts +++ b/src/mol-view/theme/color/shape-group.ts @@ -19,6 +19,8 @@ export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme { return location.shape.colors.ref.value[location.group] } return DefaultColor - } + }, + description: props.description, + legend: props.legend } } \ No newline at end of file diff --git a/src/mol-view/theme/color/uniform.ts b/src/mol-view/theme/color/uniform.ts index a516c6e6d..d9ab15c4d 100644 --- a/src/mol-view/theme/color/uniform.ts +++ b/src/mol-view/theme/color/uniform.ts @@ -4,16 +4,19 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { ColorTheme, ColorThemeProps } from '../color'; +import { ColorTheme, ColorThemeProps, TableLegend } from '../color'; import { Color } from 'mol-util/color'; const DefaultColor = Color(0xCCCCCC) +const Description = 'Gives everything the same, uniform color.' export function UniformColorTheme(props: ColorThemeProps): ColorTheme { const color = props.value || DefaultColor return { granularity: 'uniform', - color: () => color + color: () => color, + description: Description, + legend: TableLegend([['uniform', color]]) } } \ No newline at end of file diff --git a/src/mol-view/theme/color/unit-index.ts b/src/mol-view/theme/color/unit-index.ts index 42786deda..92d4f53bb 100644 --- a/src/mol-view/theme/color/unit-index.ts +++ b/src/mol-view/theme/color/unit-index.ts @@ -10,13 +10,15 @@ import { StructureElement, Link } from 'mol-model/structure'; import { ColorTheme, ColorThemeProps, LocationColor } from '../color'; const DefaultColor = Color(0xCCCCCC) +const Description = 'Gives every unit (single chain or collection of single elements) a unique color based on the position (index) of the unit in the list of units in the structure.' export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme { let color: LocationColor + let scale: ColorScale | undefined = undefined if (props.structure) { const { units } = props.structure - const scale = ColorScale.create({ domain: [ 0, units.length - 1 ] }) + scale = ColorScale.create({ domain: [ 0, units.length - 1 ] }) const unitIdColor = new Map<number, Color>() for (let i = 0, il = units.length; i <il; ++i) { unitIdColor.set(units[i].id, scale.color(units[i].id)) @@ -34,5 +36,10 @@ export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme { color = () => DefaultColor } - return { granularity: 'instance', color } + return { + granularity: 'instance', + color, + description: Description, + legend: scale ? scale.legend : undefined + } } \ No newline at end of file -- GitLab