Procházet zdrojové kódy

add custom theme colors

- element-symbol
- molecule-type
- residue-name
- secondary-structure
Alexander Rose před 3 roky
rodič
revize
5900e27e39

+ 1 - 0
CHANGELOG.md

@@ -15,6 +15,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Bugfix: Automatically treat empty string as "non-present" value in BinaryCIF writer.
 - Fix coarse model support in entity-id color theme
 - Fix marking of carbohydrate visuals (whole chain could get marked instead of single residue)
+- Add custom colors to "element-symbol", "molecule-type", "residue-name", and "secondary-structure" themes
 
 ## [v3.0.0-dev.10] - 2022-01-17
 

+ 9 - 5
src/mol-theme/color/element-symbol.ts

@@ -12,7 +12,7 @@ import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ThemeDataContext } from '../theme';
 import { TableLegend } from '../../mol-util/legend';
-import { getAdjustedColorMap } from '../../mol-util/color/color';
+import { getAdjustedColorMap, getColorMapParams } from '../../mol-util/color/color';
 import { ChainIdColorTheme, ChainIdColorThemeParams } from './chain-id';
 import { OperatorNameColorThemeParams, OperatorNameColorTheme } from './operator-name';
 
@@ -34,7 +34,11 @@ export const ElementSymbolColorThemeParams = {
         'element-symbol': PD.Group({})
     }, { description: 'Use chain-id coloring for carbon atoms.' }),
     saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
-    lightness: PD.Numeric(0.2, { min: -6, max: 6, step: 0.1 })
+    lightness: PD.Numeric(0.2, { min: -6, max: 6, step: 0.1 }),
+    colors: PD.MappedStatic('default', {
+        'default': PD.EmptyGroup(),
+        'custom': PD.Group(getColorMapParams(ElementSymbolColors))
+    })
 };
 export type ElementSymbolColorThemeParams = typeof ElementSymbolColorThemeParams
 export function getElementSymbolColorThemeParams(ctx: ThemeDataContext) {
@@ -47,7 +51,7 @@ export function elementSymbolColor(colorMap: ElementSymbolColors, element: Eleme
 }
 
 export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<ElementSymbolColorThemeParams>): ColorTheme<ElementSymbolColorThemeParams> {
-    const colorMap = getAdjustedColorMap(ElementSymbolColors, props.saturation, props.lightness);
+    const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? ElementSymbolColors : props.colors.params, props.saturation, props.lightness);
 
     const carbonColor = props.carbonColor.name === 'chain-id'
         ? ChainIdColorTheme(ctx, props.carbonColor.params).color
@@ -86,8 +90,8 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<
         color,
         props,
         description: Description,
-        legend: TableLegend(Object.keys(ElementSymbolColors).map(name => {
-            return [name, (ElementSymbolColors as any)[name] as Color] as [string, Color];
+        legend: TableLegend(Object.keys(colorMap).map(name => {
+            return [name, (colorMap as any)[name] as Color] as [string, Color];
         }))
     };
 }

+ 30 - 28
src/mol-theme/color/molecule-type.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Color } from '../../mol-util/color';
+import { Color, ColorMap } from '../../mol-util/color';
 import { StructureElement, Unit, Bond, ElementIndex } from '../../mol-model/structure';
 import { Location } from '../../mol-model/location';
 import { ColorTheme } from '../color';
@@ -13,6 +13,18 @@ import { getElementMoleculeType } from '../../mol-model/structure/util';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ThemeDataContext } from '../theme';
 import { TableLegend } from '../../mol-util/legend';
+import { getAdjustedColorMap, getColorMapParams } from '../../mol-util/color/color';
+
+export const MoleculeTypeColors = ColorMap({
+    water: 0x386cb0,
+    ion: 0xf0027f,
+    protein: 0xbeaed4,
+    RNA: 0xfdc086,
+    DNA: 0xbf5b17,
+    PNA: 0x42A49A,
+    saccharide: 0x7fc97f,
+});
+export type MoleculeTypeColors = typeof MoleculeTypeColors
 
 const DefaultMoleculeTypeColor = Color(0xffff99);
 const Description = 'Assigns a color based on the molecule type of a residue.';
@@ -20,15 +32,9 @@ const Description = 'Assigns a color based on the molecule type of a residue.';
 export const MoleculeTypeColorThemeParams = {
     saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
     lightness: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
-    colors: PD.Group({
-        water: PD.Color(Color(0x386cb0)),
-        ion: PD.Color(Color(0xf0027f)),
-        protein: PD.Color(Color(0xbeaed4)),
-        rna: PD.Color(Color(0xfdc086)),
-        dna: PD.Color(Color(0xbf5b17)),
-        pna: PD.Color(Color(0x42A49A)),
-        saccharide: PD.Color(Color(0x7fc97f)),
-        lipid: PD.Color(Color(0xcccccc)),
+    colors: PD.MappedStatic('default', {
+        'default': PD.EmptyGroup(),
+        'custom': PD.Group(getColorMapParams(MoleculeTypeColors))
     })
 };
 export type MoleculeTypeColorThemeParams = typeof MoleculeTypeColorThemeParams
@@ -36,32 +42,28 @@ export function getMoleculeTypeColorThemeParams(ctx: ThemeDataContext) {
     return MoleculeTypeColorThemeParams; // TODO return copy
 }
 
-type MoleculeTypeColorThemeProps = PD.Values<MoleculeTypeColorThemeParams>;
-export function moleculeTypeColor(props: MoleculeTypeColorThemeProps, unit: Unit, element: ElementIndex): Color {
-    let c = DefaultMoleculeTypeColor;
+export function moleculeTypeColor(colorMap: MoleculeTypeColors, unit: Unit, element: ElementIndex): Color {
     const moleculeType = getElementMoleculeType(unit, element);
     switch (moleculeType) {
-        case MoleculeType.Water: c = props.colors.water; break;
-        case MoleculeType.Ion: c = props.colors.ion; break;
-        case MoleculeType.Protein: c = props.colors.protein; break;
-        case MoleculeType.RNA: c = props.colors.rna; break;
-        case MoleculeType.DNA: c = props.colors.dna; break;
-        case MoleculeType.PNA: c = props.colors.pna; break;
-        case MoleculeType.Saccharide: c = props.colors.saccharide; break;
-        case MoleculeType.Lipid: c = props.colors.lipid; break;
+        case MoleculeType.Water: return colorMap.water;
+        case MoleculeType.Ion: return colorMap.ion;
+        case MoleculeType.Protein: return colorMap.protein;
+        case MoleculeType.RNA: return colorMap.RNA;
+        case MoleculeType.DNA: return colorMap.DNA;
+        case MoleculeType.PNA: return colorMap.PNA;
+        case MoleculeType.Saccharide: return colorMap.saccharide;
     }
-    c = Color.saturate(c, props.saturation);
-    c = Color.darken(c, -props.lightness);
-    return c;
+    return DefaultMoleculeTypeColor;
 }
 
 export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: PD.Values<MoleculeTypeColorThemeParams>): ColorTheme<MoleculeTypeColorThemeParams> {
+    const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? MoleculeTypeColors : props.colors.params, props.saturation, props.lightness);
 
     function color(location: Location): Color {
         if (StructureElement.Location.is(location)) {
-            return moleculeTypeColor(props, location.unit, location.element);
+            return moleculeTypeColor(colorMap, location.unit, location.element);
         } else if (Bond.isLocation(location)) {
-            return moleculeTypeColor(props, location.aUnit, location.aUnit.elements[location.aIndex]);
+            return moleculeTypeColor(colorMap, location.aUnit, location.aUnit.elements[location.aIndex]);
         }
         return DefaultMoleculeTypeColor;
     }
@@ -72,8 +74,8 @@ export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: PD.Values<M
         color,
         props,
         description: Description,
-        legend: TableLegend(Object.keys(props.colors).map(name => {
-            return [name, (props.colors as any)[name] as Color] as [string, Color];
+        legend: TableLegend(Object.keys(colorMap).map(name => {
+            return [name, (colorMap as any)[name] as Color] as [string, Color];
         }).concat([['Other/unknown', DefaultMoleculeTypeColor]]))
     };
 }

+ 9 - 5
src/mol-theme/color/residue-name.ts

@@ -11,7 +11,7 @@ import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ThemeDataContext } from '../theme';
 import { TableLegend } from '../../mol-util/legend';
-import { getAdjustedColorMap } from '../../mol-util/color/color';
+import { getAdjustedColorMap, getColorMapParams } from '../../mol-util/color/color';
 
 // protein colors from Jmol http://jmol.sourceforge.net/jscolors/
 export const ResidueNameColors = ColorMap({
@@ -66,7 +66,11 @@ const Description = 'Assigns a color to every residue according to its name.';
 
 export const ResidueNameColorThemeParams = {
     saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
-    lightness: PD.Numeric(1, { min: -6, max: 6, step: 0.1 })
+    lightness: PD.Numeric(1, { min: -6, max: 6, step: 0.1 }),
+    colors: PD.MappedStatic('default', {
+        'default': PD.EmptyGroup(),
+        'custom': PD.Group(getColorMapParams(ResidueNameColors))
+    })
 };
 export type ResidueNameColorThemeParams = typeof ResidueNameColorThemeParams
 export function getResidueNameColorThemeParams(ctx: ThemeDataContext) {
@@ -93,7 +97,7 @@ export function residueNameColor(colorMap: ResidueNameColors, residueName: strin
 }
 
 export function ResidueNameColorTheme(ctx: ThemeDataContext, props: PD.Values<ResidueNameColorThemeParams>): ColorTheme<ResidueNameColorThemeParams> {
-    const colorMap = getAdjustedColorMap(ResidueNameColors, props.saturation, props.lightness);
+    const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? ResidueNameColors : props.colors.params, props.saturation, props.lightness);
 
     function color(location: Location): Color {
         if (StructureElement.Location.is(location)) {
@@ -123,8 +127,8 @@ export function ResidueNameColorTheme(ctx: ThemeDataContext, props: PD.Values<Re
         color,
         props,
         description: Description,
-        legend: TableLegend(Object.keys(ResidueNameColors).map(name => {
-            return [name, (ResidueNameColors as any)[name] as Color] as [string, Color];
+        legend: TableLegend(Object.keys(colorMap).map(name => {
+            return [name, (colorMap as any)[name] as Color] as [string, Color];
         }).concat([['Unknown', DefaultResidueNameColor]]))
     };
 }

+ 10 - 6
src/mol-theme/color/secondary-structure.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -14,7 +14,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ThemeDataContext } from '../theme';
 import { TableLegend } from '../../mol-util/legend';
 import { SecondaryStructureProvider, SecondaryStructureValue } from '../../mol-model-props/computed/secondary-structure';
-import { getAdjustedColorMap } from '../../mol-util/color/color';
+import { getAdjustedColorMap, getColorMapParams } from '../../mol-util/color/color';
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
 import { hash2 } from '../../mol-data/util';
 
@@ -41,7 +41,11 @@ const Description = 'Assigns a color based on the type of secondary structure an
 
 export const SecondaryStructureColorThemeParams = {
     saturation: PD.Numeric(-1, { min: -6, max: 6, step: 0.1 }),
-    lightness: PD.Numeric(0, { min: -6, max: 6, step: 0.1 })
+    lightness: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
+    colors: PD.MappedStatic('default', {
+        'default': PD.EmptyGroup(),
+        'custom': PD.Group(getColorMapParams(SecondaryStructureColors))
+    })
 };
 export type SecondaryStructureColorThemeParams = typeof SecondaryStructureColorThemeParams
 export function getSecondaryStructureColorThemeParams(ctx: ThemeDataContext) {
@@ -88,7 +92,7 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Va
     const computedSecondaryStructure = ctx.structure && SecondaryStructureProvider.get(ctx.structure);
     const contextHash = computedSecondaryStructure ? hash2(computedSecondaryStructure.id, computedSecondaryStructure.version) : -1;
 
-    const colorMap = getAdjustedColorMap(SecondaryStructureColors, props.saturation, props.lightness);
+    const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? SecondaryStructureColors : props.colors.params, props.saturation, props.lightness);
 
     function color(location: Location): Color {
         if (StructureElement.Location.is(location)) {
@@ -107,8 +111,8 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Va
         props,
         contextHash,
         description: Description,
-        legend: TableLegend(Object.keys(SecondaryStructureColors).map(name => {
-            return [name, (SecondaryStructureColors as any)[name] as Color] as [string, Color];
+        legend: TableLegend(Object.keys(colorMap).map(name => {
+            return [name, (colorMap as any)[name] as Color] as [string, Color];
         }).concat([['Other', DefaultSecondaryStructureColor]]))
     };
 }

+ 11 - 1
src/mol-util/color/color.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -8,6 +8,8 @@ import { NumberArray } from '../../mol-util/type-helpers';
 import { Vec3 } from '../../mol-math/linear-algebra';
 import { Hcl } from './spaces/hcl';
 import { Lab } from './spaces/lab';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { objectForEach } from '../object';
 
 /** RGB color triplet expressed as a single number */
 export type Color = { readonly '@type': 'color' } & number
@@ -174,5 +176,13 @@ export function getAdjustedColorMap<T extends { [k: string]: number }>(map: Colo
     return adjustedMap as ColorMap<T>;
 }
 
+export function getColorMapParams<T extends { [k: string]: number }>(map: ColorMap<T>) {
+    const colors: Record<string, PD.Color> = {};
+    objectForEach(map, (_, k) => {
+        colors[k] = PD.Color(map[k]);
+    });
+    return colors as { [k in keyof T]: PD.Color };
+}
+
 export type ColorSwatch = [string, Color][]
 export function ColorSwatch(l: [string, number][]) { return l as unknown as ColorSwatch; }