diff --git a/src/apps/state-docs/pd-to-md.ts b/src/apps/state-docs/pd-to-md.ts index b068acaaf33f02d271d1bd8fe82f13e2aa90e1b9..e1a6ceac44e82037a389d8d331c2676ae347ab15 100644 --- a/src/apps/state-docs/pd-to-md.ts +++ b/src/apps/state-docs/pd-to-md.ts @@ -19,7 +19,7 @@ function paramInfo(param: PD.Any, offset: number): string { case 'conditioned': return getParams(param.conditionParams, offset); case 'multi-select': return `Array of ${oToS(param.options)}`; case 'color': return 'Color as 0xrrggbb'; - case 'color-scale': return `One of ${oToS(param.options)}`; + case 'color-list': return `One of ${oToS(param.options)}`; case 'vec3': return `3D vector [x, y, z]`; case 'file': return `JavaScript File Handle`; case 'select': return `One of ${oToS(param.options)}`; diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index 3b3b89568931cfdc08ed8319c157525e7023def3..b87cfee41de95789c1e5b6587e688f028f97b5bd 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -81,7 +81,7 @@ export namespace DirectVolume { Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0), Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0), ]), - list: PD.ColorScale<ColorListName>('red-yellow-blue', ColorListOptions), + list: PD.ColorList<ColorListName>('red-yellow-blue', ColorListOptions), } export type Params = typeof Params diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index dbb5854d70468fab139a2956bcf08c9513f53130..46451b599b9ebc56a083a4e384c33263ca068aec 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -7,8 +7,8 @@ import { Vec2, Vec3 } from '../../../mol-math/linear-algebra'; import { Color } from '../../../mol-util/color'; -import { ColorListName, getColorListFromName } from '../../../mol-util/color/scale'; -import { ColorNames, ColorNamesValueMap } from '../../../mol-util/color/tables'; +import { ColorListName, getColorListFromName } from '../../../mol-util/color/lists'; +import { ColorNames, ColorNamesValueMap } from '../../../mol-util/color/names'; import { memoize1 } from '../../../mol-util/memoize'; import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { camelCaseToWords } from '../../../mol-util/string'; @@ -54,7 +54,7 @@ function controlFor(param: PD.Any): ParamControl | undefined { case 'conditioned': return ConditionedControl; case 'multi-select': return MultiSelectControl; case 'color': return ColorControl; - case 'color-scale': return ColorScaleControl; + case 'color-list': return ColorListControl; case 'vec3': return Vec3Control; case 'file': return FileControl; case 'select': return SelectControl; @@ -298,14 +298,35 @@ export class ColorControl extends SimpleParam<PD.Color> { } } -const colorScaleGradient = memoize1((n: ColorListName) => `linear-gradient(to right, ${getColorListFromName(n).map(c => Color.toStyle(c)).join(', ')})`); +const colorGradientInterpolated = memoize1((colors: Color[]) => { + const styles = colors.map(c => Color.toStyle(c)) + return `linear-gradient(to right, ${styles.join(', ')})` +}); + +const colorGradientBanded = memoize1((colors: Color[]) => { + const n = colors.length + const styles: string[] = [`${Color.toStyle(colors[0])} ${100 * (1 / n)}%`] + for (let i = 1, il = n - 1; i < il; ++i) { + styles.push( + `${Color.toStyle(colors[i])} ${100 * (i / n)}%`, + `${Color.toStyle(colors[i])} ${100 * ((i + 1) / n)}%` + ) + } + styles.push(`${Color.toStyle(colors[n - 1])} ${100 * ((n - 1) / n)}%`) + return `linear-gradient(to right, ${styles.join(', ')})` +}); + +function colorGradient(name: ColorListName, banded: boolean) { + const { list, type } = getColorListFromName(name) + return type === 'qualitative' ? colorGradientBanded(list) : colorGradientInterpolated(list) +} -export class ColorScaleControl extends SimpleParam<PD.ColorScale<any>> { +export class ColorListControl extends SimpleParam<PD.ColorList<any>> { onChange = (e: React.ChangeEvent<HTMLSelectElement>) => { this.update(e.target.value); } stripStyle(): React.CSSProperties { return { - background: colorScaleGradient(this.props.value), + background: colorGradient(this.props.value, true), position: 'absolute', bottom: '0', height: '4px', diff --git a/src/mol-theme/color/cross-link.ts b/src/mol-theme/color/cross-link.ts index e4f31dd014d548924899077f57ffd976e28034da..62d3d9a28171ebc0b80c3087c3211e7254253bf8 100644 --- a/src/mol-theme/color/cross-link.ts +++ b/src/mol-theme/color/cross-link.ts @@ -11,14 +11,14 @@ import { ColorTheme, LocationColor } from '../color'; import { Vec3 } from '../../mol-math/linear-algebra'; import { ParamDefinition as PD } from '../../mol-util/param-definition' import { ThemeDataContext } from '../../mol-theme/theme'; -import { ColorListName, ColorListOptions } from '../../mol-util/color/lists'; +import { ColorListName, ColorListOptionsScale } from '../../mol-util/color/lists'; 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`).' export const CrossLinkColorThemeParams = { domain: PD.Interval([-10, 10]), - list: PD.ColorScale<ColorListName>('red-grey', ColorListOptions), + list: PD.ColorList<ColorListName>('red-grey', ColorListOptionsScale), } export type CrossLinkColorThemeParams = typeof CrossLinkColorThemeParams export function getCrossLinkColorThemeParams(ctx: ThemeDataContext) { diff --git a/src/mol-theme/color/element-index.ts b/src/mol-theme/color/element-index.ts index ed84607c7892eab070a7bf7499812899bd0c5969..8e5fa08efb2e28de4c8f44a3c4e501bca293b60e 100644 --- a/src/mol-theme/color/element-index.ts +++ b/src/mol-theme/color/element-index.ts @@ -1,23 +1,25 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { ColorScale, Color } from '../../mol-util/color'; +import { Color } from '../../mol-util/color'; import { Location } from '../../mol-model/location'; import { StructureElement, Link } from '../../mol-model/structure'; import { OrderedSet } from '../../mol-data/int'; 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/lists'; +import { getPaletteParams, getPalette } from './util'; +import { TableLegend } from '../../mol-util/color/lists'; +import { ScaleLegend } from '../../mol-util/color/scale'; 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 const ElementIndexColorThemeParams = { - list: PD.ColorScale<ColorListName>('red-yellow-blue', ColorListOptions), + ...getPaletteParams({ scaleList: 'red-yellow-blue' }), } export type ElementIndexColorThemeParams = typeof ElementIndexColorThemeParams export function getElementIndexColorThemeParams(ctx: ThemeDataContext) { @@ -26,7 +28,7 @@ export function getElementIndexColorThemeParams(ctx: ThemeDataContext) { export function ElementIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<ElementIndexColorThemeParams>): ColorTheme<ElementIndexColorThemeParams> { let color: LocationColor - let scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' }) + let legend: ScaleLegend | TableLegend | undefined if (ctx.structure) { const { units } = ctx.structure @@ -40,17 +42,18 @@ export function ElementIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<E elementCount += units[i].elements.length unitIdIndex.set(units[i].id, i) } - scale.setDomain(0, elementCount - 1) - const scaleColor = scale.color + + const palette = getPalette(elementCount, props) + legend = palette.legend 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 scaleColor(cummulativeElementCount.get(unitIndex)! + unitElementIndex) + return palette.color(cummulativeElementCount.get(unitIndex)! + unitElementIndex) } else if (Link.isLocation(location)) { const unitIndex = unitIdIndex.get(location.aUnit.id)! - return scaleColor(cummulativeElementCount.get(unitIndex)! + location.aIndex) + return palette.color(cummulativeElementCount.get(unitIndex)! + location.aIndex) } return DefaultColor } @@ -64,7 +67,7 @@ export function ElementIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<E color, props, description: Description, - legend: scale ? scale.legend : undefined + legend } } diff --git a/src/mol-theme/color/hydrophobicity.ts b/src/mol-theme/color/hydrophobicity.ts index eb171a21fe3b9f969162ccda36bac473ed502cc7..88306bab0f31cb99fd8d1cbaded9417a2eb9eb26 100644 --- a/src/mol-theme/color/hydrophobicity.ts +++ b/src/mol-theme/color/hydrophobicity.ts @@ -11,12 +11,12 @@ import { ColorTheme } from '../color'; import { ParamDefinition as PD } from '../../mol-util/param-definition' import { ThemeDataContext } from '../theme'; import { ResidueHydrophobicity } from '../../mol-model/structure/model/types'; -import { ColorListName, ColorListOptions } from '../../mol-util/color/lists'; +import { ColorListName, ColorListOptionsScale } from '../../mol-util/color/lists'; const Description = 'Assigns a color to every amino acid according to the "Experimentally determined hydrophobicity scale for proteins at membrane interfaces" by Wimely and White (doi:10.1038/nsb1096-842).' export const HydrophobicityColorThemeParams = { - list: PD.ColorScale<ColorListName>('red-yellow-green', ColorListOptions), + list: PD.ColorList<ColorListName>('red-yellow-green', ColorListOptionsScale), scale: PD.Select('DGwif', [['DGwif', 'DG water-membrane'], ['DGwoct', 'DG water-octanol'], ['Oct-IF', 'DG difference']]) } export type HydrophobicityColorThemeParams = typeof HydrophobicityColorThemeParams diff --git a/src/mol-theme/color/sequence-id.ts b/src/mol-theme/color/sequence-id.ts index 2219068c000a2742611ae387afbdd114c7e7a4ee..30b2b4ac542a91f01099d716d558af5551399c72 100644 --- a/src/mol-theme/color/sequence-id.ts +++ b/src/mol-theme/color/sequence-id.ts @@ -11,13 +11,13 @@ import { Location } from '../../mol-model/location'; import { ColorTheme } from '../color'; import { ParamDefinition as PD } from '../../mol-util/param-definition' import { ThemeDataContext } from '../../mol-theme/theme'; -import { ColorListOptions, ColorListName } from '../../mol-util/color/lists'; +import { ColorListOptionsScale, ColorListName } from '../../mol-util/color/lists'; const DefaultColor = Color(0xCCCCCC) const Description = 'Gives every polymer residue a color based on its `seq_id` value.' export const SequenceIdColorThemeParams = { - list: PD.ColorScale<ColorListName>('rainbow', ColorListOptions), + list: PD.ColorList<ColorListName>('rainbow', ColorListOptionsScale), } export type SequenceIdColorThemeParams = typeof SequenceIdColorThemeParams export function getSequenceIdColorThemeParams(ctx: ThemeDataContext) { diff --git a/src/mol-theme/color/uncertainty.ts b/src/mol-theme/color/uncertainty.ts index ea69bf70c4b5e9e08af08ca4b215857bae746a56..47076c16333d980e8c86229a155455d4b755de4b 100644 --- a/src/mol-theme/color/uncertainty.ts +++ b/src/mol-theme/color/uncertainty.ts @@ -10,14 +10,14 @@ import { Location } from '../../mol-model/location'; import { ColorTheme } from '../color'; import { ParamDefinition as PD } from '../../mol-util/param-definition' import { ThemeDataContext } from '../theme'; -import { ColorListName, ColorListOptions } from '../../mol-util/color/lists'; +import { ColorListName, ColorListOptionsScale } from '../../mol-util/color/lists'; const DefaultUncertaintyColor = Color(0xffff99) const Description = `Assigns a color based on the uncertainty of an element's position, , e.g. B-factor or RMSF, depending on the data availability and experimental technique.` export const UncertaintyColorThemeParams = { domain: PD.Interval([0, 100]), - list: PD.ColorScale<ColorListName>('red-white-blue', ColorListOptions), + list: PD.ColorList<ColorListName>('red-white-blue', ColorListOptionsScale), } export type UncertaintyColorThemeParams = typeof UncertaintyColorThemeParams export function getUncertaintyColorThemeParams(ctx: ThemeDataContext) { diff --git a/src/mol-theme/color/util.ts b/src/mol-theme/color/util.ts index 9119e1729c77b967fedad05c0890d6230bc5cfcf..7925c82f882cc262f1ea960a803b4a9c381eeb1b 100644 --- a/src/mol-theme/color/util.ts +++ b/src/mol-theme/color/util.ts @@ -21,10 +21,10 @@ export function getPaletteParams(props: Partial<GetPaletteProps> = {}) { return { palette: PD.MappedStatic('generate', { scale: PD.Group({ - list: PD.ColorScale<ColorListName>(p.scaleList, ColorListOptionsScale), + list: PD.ColorList<ColorListName>(p.scaleList, ColorListOptionsScale), }, { isFlat: true }), set: PD.Group({ - list: PD.ColorScale<ColorListName>(p.setList, ColorListOptionsSet), + list: PD.ColorList<ColorListName>(p.setList, ColorListOptionsSet), }, { isFlat: true }), generate: PD.Group({ ...DistinctColorsParams, @@ -32,7 +32,7 @@ export function getPaletteParams(props: Partial<GetPaletteProps> = {}) { }, { isFlat: true }) }, { options: [ - ['scale', 'From Scale'], + ['scale', 'Interpolate'], ['set', 'From Set'], ['generate', 'Generate Distinct'] ] @@ -62,7 +62,7 @@ export function getPalette(count: number, props: PaletteProps) { let colors: Color[] if (props.palette.name === 'set') { const listOrName = props.palette.params.list - colors = typeof listOrName === 'string' ? getColorListFromName(listOrName) : listOrName + colors = typeof listOrName === 'string' ? getColorListFromName(listOrName).list : listOrName } else { count = Math.min(count, props.palette.params.maxCount) colors = distinctColors(count, props.palette.params) diff --git a/src/mol-util/color/lists.ts b/src/mol-util/color/lists.ts index c07110133c9ce3780d724a0127a13701054e2794..2ca8648f37267c5496f817d0f8710364b759a70b 100644 --- a/src/mol-util/color/lists.ts +++ b/src/mol-util/color/lists.ts @@ -125,7 +125,7 @@ export const ColorLists = { '', [0x67001f, 0xb2182b, 0xd6604d, 0xf4a582, 0xfddbc7, 0xffffff, 0xe0e0e0, 0xbababa, 0x878787, 0x4d4d4d, 0x1a1a1a] ), - 'purple-orange': ColorList('Purple-Orange', 'diverging', + 'orange-purple': ColorList('Orange-Purple', 'diverging', '', [0x7f3b08, 0xb35806, 0xe08214, 0xfdb863, 0xfee0b6, 0xf7f7f7, 0xd8daeb, 0xb2abd2, 0x8073ac, 0x542788, 0x2d004b] ), @@ -209,7 +209,7 @@ export const ColorListOptionsScale = ColorListOptions.filter(v => ColorLists[v[0 export const ColorListOptionsSet = ColorListOptions.filter(v => ColorLists[v[0]].type === 'qualitative') export function getColorListFromName(name: ColorListName) { - if (name in ColorLists) return ColorLists[name as ColorListName].list + if (name in ColorLists) return ColorLists[name as ColorListName] console.warn(`unknown color list named '${name}'`) - return ColorLists['red-yellow-blue'].list + return ColorLists['red-yellow-blue'] } diff --git a/src/mol-util/color/scale.ts b/src/mol-util/color/scale.ts index 0cf4c5b39a19e50c2eccc7e189034e88b450e943..31b33f774487522593ba480d83a8949b3105de99 100644 --- a/src/mol-util/color/scale.ts +++ b/src/mol-util/color/scale.ts @@ -44,7 +44,7 @@ export type ColorScaleProps = Partial<typeof DefaultColorScaleProps> export namespace ColorScale { export function create(props: ColorScaleProps): ColorScale { const { domain, reverse, listOrName } = { ...DefaultColorScaleProps, ...props } - const list = typeof listOrName === 'string' ? getColorListFromName(listOrName) : listOrName + const list = typeof listOrName === 'string' ? getColorListFromName(listOrName).list : listOrName const colors = reverse ? list.slice().reverse() : list const count1 = colors.length - 1 diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index 1b4fe25f3ba0fa39e564b0073d1504184c2c64b3..c83bf1ea79d49e9c41fa7173536b3f2c85f2f7c4 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -62,13 +62,13 @@ export namespace ParamDefinition { return setInfo<Select<T>>({ type: 'select', defaultValue, options }, info) } - export interface ColorScale<T extends string> extends Base<T> { - type: 'color-scale' + export interface ColorList<T extends string> extends Base<T> { + type: 'color-list' /** array of (value, label) tuples */ options: [T, string][] } - export function ColorScale<T extends string>(defaultValue: T, options: [T, string][], info?: Info): ColorScale<T> { - return setInfo<ColorScale<T>>({ type: 'color-scale', defaultValue, options }, info) + export function ColorList<T extends string>(defaultValue: T, options: [T, string][], info?: Info): ColorList<T> { + return setInfo<ColorList<T>>({ type: 'color-list', defaultValue, options }, info) } export interface MultiSelect<E extends string, T = E[]> extends Base<T> { @@ -242,7 +242,7 @@ export namespace ParamDefinition { export type Any = | Value<any> | Select<any> | MultiSelect<any> | BooleanParam | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph - | ColorScale<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | ScriptExpression | ObjectList + | ColorList<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | ScriptExpression | ObjectList export type Params = { [k: string]: Any } export type Values<T extends Params> = { [k in keyof T]: T[k]['defaultValue'] } diff --git a/src/tests/browser/render-asa.ts b/src/tests/browser/render-asa.ts index b877965e8861bb8f0dae187f49d368793b727d99..fd284c675401d938ac7f65c47ca37b73cd347e08 100644 --- a/src/tests/browser/render-asa.ts +++ b/src/tests/browser/render-asa.ts @@ -101,7 +101,7 @@ const DefaultColor = Color(0xFFFFFF) const Description = 'Assigns a color based on the relative accessible surface area of a residue.' export const AccessibleSurfaceAreaColorThemeParams = { - list: PD.ColorScale<ColorListName>('rainbow', ColorListOptions) + list: PD.ColorList<ColorListName>('rainbow', ColorListOptions) } export type AccessibleSurfaceAreaColorThemeParams = typeof AccessibleSurfaceAreaColorThemeParams export function getAccessibleSurfaceAreaColorThemeParams(ctx: ThemeDataContext) {