瀏覽代碼

wip, color schemes: scales

Alexander Rose 6 年之前
父節點
當前提交
6cc6a76707

+ 3 - 2
src/apps/canvas/assembly-symmetry.ts

@@ -66,10 +66,10 @@ export function getClusterColorTheme(symmetryId: number, assemblySymmetry: Assem
     const DefaultColor = Color(0xCCCCCC)
     const s = assemblySymmetry.db.rcsb_assembly_symmetry
     const symmetry = Table.pickRow(s, i => s.id.value(i) === symmetryId)
-    if (!symmetry) return { granularity: 'uniform', color: () => DefaultColor }
+    if (!symmetry) return { features: {}, granularity: 'uniform', color: () => DefaultColor }
 
     const clusters = assemblySymmetry.getClusters(symmetryId)
-    if (!clusters._rowCount) return { granularity: 'uniform', color: () => DefaultColor }
+    if (!clusters._rowCount) return { features: {}, granularity: 'uniform', color: () => DefaultColor }
 
     const clusterByMember = new Map<string, number>()
     for (let i = 0, il = clusters._rowCount; i < il; ++i) {
@@ -83,6 +83,7 @@ export function getClusterColorTheme(symmetryId: number, assemblySymmetry: Assem
     const scale = ColorScale.create({ domain: [ 0, clusters._rowCount - 1 ] })
 
     return {
+        features: {},
         granularity: 'instance',
         color: (location: Location): Color => {
             if (StructureElement.isLocation(location)) {

+ 2 - 2
src/apps/canvas/index.ts

@@ -25,8 +25,8 @@ if (pdbId) app.loadPdbIdOrMmcifUrl(pdbId, { assemblyId })
 
 // app.loadPdbIdOrMmcifUrl('http://localhost:8091/ngl/data/1crn.cif')
 
-app.loadPdbIdOrMmcifUrl('3pqr')
-app.loadCcp4Url('http://localhost:8091/ngl/data/3pqr-mode0.ccp4')
+// app.loadPdbIdOrMmcifUrl('3pqr')
+// app.loadCcp4Url('http://localhost:8091/ngl/data/3pqr-mode0.ccp4')
 
 // app.loadPdbIdOrMmcifUrl('1lee')
 // app.loadCcp4Url('http://localhost:8091/ngl/data/1lee.ccp4')

+ 8 - 2
src/mol-geo/geometry/color-data.ts

@@ -6,11 +6,11 @@
 
 import { ValueCell } from 'mol-util';
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
-import { Color } from 'mol-util/color';
+import { Color, ColorMap } from 'mol-util/color';
 import { Vec2, Vec3 } from 'mol-math/linear-algebra';
 import { LocationIterator } from '../util/location-iterator';
 import { NullLocation } from 'mol-model/location';
-import { LocationColor, ColorThemeProps, ColorTheme, ColorThemeName, ScaleLegend, TableLegend } from 'mol-theme/color';
+import { LocationColor, ColorThemeProps, ColorTheme, ColorThemeName, ScaleLegend, TableLegend, ColorScaleName, getColorScaleFromName } from 'mol-theme/color';
 import { RuntimeContext } from 'mol-task';
 import { getGranularity } from './geometry';
 import { Structure } from 'mol-model/structure';
@@ -27,6 +27,8 @@ export type ColorData = {
 
 export interface ColorProps {
     colorTheme: ColorThemeName
+    colorList?: Color[] | ColorScaleName
+    colorMap?: ColorMap<any>
     colorDomain?: [number, number]
     colorValue?: Color
     colorFunction?: LocationColor,
@@ -41,6 +43,10 @@ export function getColorThemeProps(props: ColorProps): ColorThemeProps {
         name: props.colorTheme
     }
     if (props.colorDomain !== undefined) p.domain = props.colorDomain
+    if (props.colorList !== undefined) {
+        p.list = typeof props.colorList === 'string' ? getColorScaleFromName(props.colorList) : props.colorList
+    }
+    if (props.colorMap !== undefined) p.map = props.colorMap
     if (props.colorValue !== undefined) p.value = props.colorValue
     if (props.structure !== undefined) p.structure = props.structure
     if (props.colorFunction !== undefined) p.color = props.colorFunction

+ 1 - 1
src/mol-geo/geometry/direct-volume/transfer-function.ts

@@ -29,7 +29,7 @@ export function createTransferFunctionTexture(controlPoints: ControlPoint[], tex
     ]
     const scale = ColorScale.create({
         domain: [0, 1],
-        colors: ColorMatplotlib.viridis
+        list: ColorMatplotlib.viridis
     })
 
     const n = 256

+ 2 - 1
src/mol-geo/geometry/geometry.ts

@@ -10,7 +10,7 @@ import { RenderableState } from 'mol-gl/renderable';
 import { ValueCell } from 'mol-util';
 import { BaseValues } from 'mol-gl/renderable/schema';
 import { Color } from 'mol-util/color';
-import { ColorThemeOptions, ColorThemeName } from 'mol-theme/color';
+import { ColorThemeOptions, ColorThemeName, ColorScaleOptions, ColorScaleName } from 'mol-theme/color';
 import { LocationIterator } from '../util/location-iterator';
 import { ColorType } from './color-data';
 import { SizeType } from './size-data';
@@ -65,6 +65,7 @@ export namespace Geometry {
         useFog: BooleanParam('Use Fog', '', false),
         quality: SelectParam<VisualQuality>('Quality', '', 'auto', VisualQualityOptions),
         colorTheme: SelectParam<ColorThemeName>('Color Theme', '', 'uniform', ColorThemeOptions),
+        colorList: SelectParam<ColorScaleName>('Color Scale', '', 'default', ColorScaleOptions),
         colorValue: ColorParam('Color Value', '', Color(0xCCCCCC)),
     }
     export const DefaultProps = paramDefaultValues(Params)

+ 4 - 1
src/mol-repr/index.ts

@@ -11,6 +11,7 @@ import { Loci, isEmptyLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction } from '../mol-geo/geometry/marker-data';
 import { Params, MultiSelectParam } from 'mol-util/parameter';
 import { WebGLContext } from 'mol-gl/webgl/context';
+// import { ColorTheme } from 'mol-theme/color';
 
 // export interface RepresentationProps {
 //     visuals?: string[]
@@ -106,7 +107,9 @@ export namespace Representation {
 //
 
 export interface VisualContext extends RepresentationContext {
-    runtime: RuntimeContext
+    runtime: RuntimeContext,
+    // TODO
+    // colorTheme: ColorTheme,
 }
 
 export interface Visual<D, P extends RepresentationProps> {

+ 4 - 1
src/mol-repr/util.ts

@@ -44,7 +44,10 @@ export function sizeChanged(oldProps: SizeProps, newProps: SizeProps) {
 export function colorChanged(oldProps: ColorProps, newProps: ColorProps) {
     return (
         oldProps.colorTheme !== newProps.colorTheme ||
-        oldProps.colorValue !== newProps.colorValue
+        oldProps.colorValue !== newProps.colorValue ||
+        oldProps.colorDomain !== newProps.colorDomain ||
+        oldProps.colorList !== newProps.colorList ||
+        oldProps.colorMap !== newProps.colorMap
     )
 }
 

+ 43 - 1
src/mol-theme/color.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 { Structure } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
 import { ColorType } from 'mol-geo/geometry/color-data';
@@ -23,6 +23,7 @@ import { SequenceIdColorTheme } from './color/sequence-id';
 import { SecondaryStructureColorTheme } from './color/secondary-structure';
 import { MoleculeTypeColorTheme } from './color/molecule-type';
 import { PolymerIndexColorTheme } from './color/polymer-index';
+import { ColorMatplotlib, ColorBrewer, ColorOther } from 'mol-util/color/tables';
 
 export type LocationColor = (location: Location, isSecondary: boolean) => Color
 
@@ -36,6 +37,29 @@ export function ScaleLegend(minLabel: string, maxLabel: string, colors: Color[])
     return { kind: 'scale-legend', minLabel, maxLabel, colors }
 }
 
+export type ColorScaleName = (
+    'default' |
+    keyof typeof ColorBrewer | keyof typeof ColorMatplotlib | keyof typeof ColorOther
+)
+export const ColorScaleNames = [
+    'default',
+    ...Object.keys(ColorBrewer), ...Object.keys(ColorMatplotlib), ...Object.keys(ColorOther)
+]
+export const ColorScaleOptions = ColorScaleNames.map(n => [n, n] as [ColorScaleName, string])
+
+export function getColorScaleFromName(name: string) {
+    if (name === 'default') {
+        return
+    } else if (name in ColorBrewer) {
+        return ColorBrewer[name as keyof typeof ColorBrewer]
+    } else if (name in ColorMatplotlib) {
+        return ColorMatplotlib[name as keyof typeof ColorMatplotlib]
+    } else if (name in ColorOther) {
+        return ColorOther[name as keyof typeof ColorOther]
+    }
+    console.warn(`unknwon color list named '${name}'`)
+}
+
 export interface TableLegend {
     kind: 'table-legend'
     table: [ string, Color ][]
@@ -44,7 +68,23 @@ export function TableLegend(table: [ string, Color ][]): TableLegend {
     return { kind: 'table-legend', table }
 }
 
+export interface ColorThemeFeatures {
+    /** Does allow providing a structure object */
+    structure?: boolean
+    /** Does allow providing a volume object */
+    volume?: boolean
+    /** Does allow providing a list of colors (for creating a scale) */
+    list?: boolean
+    /** Does allow providing a map of colors */
+    map?: boolean
+    /** Does allow providing the boundaries for the scale */
+    domain?: boolean
+    /** Does allow providing a single/special color value */
+    value?: boolean
+}
+
 export interface ColorTheme {
+    features: ColorThemeFeatures
     granularity: ColorType
     color: LocationColor
     description?: string
@@ -74,6 +114,8 @@ export interface ColorThemeProps {
     name: ColorThemeName
     domain?: [number, number]
     value?: Color
+    list?: Color[]
+    map?: ColorMap<any>
     structure?: Structure
     color?: LocationColor
     granularity?: ColorType,

+ 1 - 0
src/mol-theme/color/carbohydrate-symbol.ts

@@ -47,6 +47,7 @@ export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme
     }
 
     return {
+        features: {},
         granularity: 'group',
         color: color,
         description: Description,

+ 1 - 0
src/mol-theme/color/chain-id.ts

@@ -63,6 +63,7 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 8 - 2
src/mol-theme/color/cross-link.ts

@@ -6,10 +6,12 @@
 
 import { Link } from 'mol-model/structure';
 
-import { Color, ColorScale, ColorBrewer } from 'mol-util/color';
+import { Color, ColorScale } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { ColorThemeProps, ColorTheme, LocationColor } from '../color';
 import { Vec3 } from 'mol-math/linear-algebra';
+import { ColorBrewer } from 'mol-util/color/tables';
+import { defaults } from 'mol-util';
 
 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`).'
@@ -27,7 +29,10 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
 
     if (props.structure) {
         const crosslinks = props.structure.crossLinkRestraints
-        scale = ColorScale.create({ domain: [ -10, 10 ], colors: ColorBrewer.RdYlBu })
+        scale = ColorScale.create({
+            domain: defaults(props.domain, [ -10, 10 ]),
+            list: defaults(props.list, ColorBrewer.RdYlBu)
+        })
         const scaleColor = scale.color
 
         color = (location: Location): Color => {
@@ -44,6 +49,7 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { list: true, domain: true },
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/custom.ts

@@ -13,6 +13,7 @@ const DefaultColor = Color(0xCCCCCC)
 export function CustomColorTheme(props: ColorThemeProps): ColorTheme {
     const value = defaults(props.value, DefaultColor)
     return {
+        features: {},
         granularity: defaults(props.granularity, 'uniform'),
         color: defaults(props.color, () => value),
         description: props.description,

+ 2 - 1
src/mol-theme/color/element-index.ts

@@ -29,7 +29,7 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
             elementCount += units[i].elements.length
             unitIdIndex.set(units[i].id, i)
         }
-        scale = ColorScale.create({ domain: [ 0, elementCount - 1 ] })
+        scale = ColorScale.create({ domain: [ 0, elementCount - 1 ], list: props.list })
         const scaleColor = scale.color
 
         color = (location: Location): Color => {
@@ -48,6 +48,7 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { list: true },
         granularity: 'groupInstance',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/element-symbol.ts

@@ -40,6 +40,7 @@ export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/molecule-type.ts

@@ -49,6 +49,7 @@ export function MoleculeTypeColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 2 - 1
src/mol-theme/color/polymer-index.ts

@@ -22,7 +22,7 @@ export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
         for (let i = 0, il = units.length; i <il; ++i) {
             if (units[i].polymerElements.length > 0) ++polymerCount
         }
-        scale = ColorScale.create({ domain: [ 0, polymerCount - 1 ] })
+        scale = ColorScale.create({ list: props.list, domain: [ 0, polymerCount - 1 ] })
         const unitIdColor = new Map<number, Color>()
         for (let i = 0, j = 0, il = units.length; i <il; ++i) {
             if (units[i].polymerElements.length > 0) {
@@ -45,6 +45,7 @@ export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { structure: true, list: true },
         granularity: 'instance',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/residue-name.ts

@@ -105,6 +105,7 @@ export function ResidueNameColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/secondary-structure.ts

@@ -72,6 +72,7 @@ export function SecondaryStructureColorTheme(props: ColorThemeProps): ColorTheme
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 3 - 1
src/mol-theme/color/sequence-id.ts

@@ -10,6 +10,7 @@ import { ColorScale, Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { ColorThemeProps, ColorTheme } from '../color';
 import { ColorOther } from 'mol-util/color/tables';
+import { defaults } from 'mol-util';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every polymer residue a color based on its `seq_id` value.'
@@ -57,7 +58,7 @@ function getSequenceLength(unit: Unit, element: ElementIndex) {
 export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme {
     const p = {
         ...props,
-        colors: ColorOther.rainbow,
+        list: defaults(props.list, ColorOther.rainbow),
         minLabel: 'Start',
         maxLabel: 'End',
     }
@@ -83,6 +84,7 @@ export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { list: true },
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/shape-group.ts

@@ -13,6 +13,7 @@ const DefaultColor = Color(0xCCCCCC)
 
 export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme {
     return {
+        features: {},
         granularity: 'group',
         color: (location: Location): Color => {
             if (Shape.isLocation(location)) {

+ 1 - 0
src/mol-theme/color/uniform.ts

@@ -14,6 +14,7 @@ export function UniformColorTheme(props: ColorThemeProps): ColorTheme {
     const color = props.value || DefaultColor
 
     return {
+        features: {},
         granularity: 'uniform',
         color: () => color,
         description: Description,

+ 1 - 0
src/mol-theme/color/unit-index.ts

@@ -37,6 +37,7 @@ export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'instance',
         color,
         description: Description,

+ 2 - 2
src/mol-util/color/color.ts

@@ -65,8 +65,8 @@ export namespace Color {
     }
 }
 
-type ColorTable<T extends { [k: string]: number[] }> = { [k in keyof T]: Color[] }
+export type ColorTable<T extends { [k: string]: number[] }> = { [k in keyof T]: Color[] }
 export function ColorTable<T extends { [k: string]: number[] }>(o: T) { return o as ColorTable<T> }
 
-type ColorMap<T extends { [k: string]: number }> = { [k in keyof T]: Color }
+export type ColorMap<T extends { [k: string]: number }> = { [k in keyof T]: Color }
 export function ColorMap<T extends { [k: string]: number }>(o: T) { return o as ColorMap<T> }

+ 2 - 3
src/mol-util/color/index.ts

@@ -4,6 +4,5 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-export { Color, ColorMap, ColorTable } from './color'
-export { ColorScale } from './scale';
-export { ColorBrewer, ColorNames } from './tables'
+export { Color, ColorMap, ColorTable } from './color';
+export { ColorScale } from './scale';

+ 5 - 3
src/mol-util/color/scale.ts

@@ -25,7 +25,7 @@ export interface ColorScale {
 export const DefaultColorScale = {
     domain: [0, 1],
     reverse: false,
-    colors: ColorBrewer.RdYlBu,
+    list: ColorBrewer.RdYlBu,
     minLabel: '' as string | undefined,
     maxLabel: '' as string | undefined,
 }
@@ -33,8 +33,10 @@ export type ColorScaleProps = Partial<typeof DefaultColorScale>
 
 export namespace ColorScale {
     export function create(props: ColorScaleProps): ColorScale {
-        const { domain, reverse, colors: _colors } = { ...DefaultColorScale, ...props }
-        const colors = reverse ? _colors.slice().reverse() : _colors
+        // ensure that no undefined .list property exists so that the default assignment works
+        if (props.list === undefined) delete props.list
+        const { domain, reverse, list } = { ...DefaultColorScale, ...props }
+        const colors = reverse ? list.slice().reverse() : list
         const count1 = colors.length - 1
 
         let diff = 0, min = 0, max = 0