From 9954e7e7662a1007b13b2a879e4422c997a7b6c1 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Thu, 15 Nov 2018 16:48:58 -0800
Subject: [PATCH] use mapped params for color/size theme

---
 src/apps/canvas/index.ts                      |  6 +--
 src/apps/canvas/structure-view.ts             |  4 +-
 src/apps/canvas/volume-view.ts                |  2 +-
 src/mol-geo/geometry/geometry.ts              |  8 ++--
 src/mol-plugin/state/transforms/visuals.ts    |  7 +--
 src/mol-plugin/ui/controls/parameters.tsx     |  6 +--
 src/mol-repr/representation.ts                |  8 ++--
 src/mol-repr/shape/representation.ts          |  6 +--
 .../structure/complex-representation.ts       |  6 +--
 .../representation/ball-and-stick.ts          |  6 +--
 .../structure/representation/cartoon.ts       |  7 ++-
 .../structure/units-representation.ts         |  6 +--
 .../structure/visual/element-sphere.ts        |  4 +-
 src/mol-repr/volume/representation.ts         |  6 +--
 src/mol-theme/color.ts                        | 45 ++++++++-----------
 src/mol-theme/color/carbohydrate-symbol.ts    |  9 ++--
 src/mol-theme/color/chain-id.ts               |  4 +-
 src/mol-theme/color/cross-link.ts             |  4 +-
 src/mol-theme/color/element-index.ts          |  4 +-
 src/mol-theme/color/element-symbol.ts         |  9 ++--
 src/mol-theme/color/molecule-type.ts          |  9 ++--
 src/mol-theme/color/polymer-index.ts          |  4 +-
 src/mol-theme/color/residue-name.ts           |  9 ++--
 src/mol-theme/color/secondary-structure.ts    |  9 ++--
 src/mol-theme/color/sequence-id.ts            |  4 +-
 src/mol-theme/color/shape-group.ts            |  4 +-
 src/mol-theme/color/uniform.ts                |  9 ++--
 src/mol-theme/color/unit-index.ts             |  4 +-
 src/mol-theme/size.ts                         | 18 +++++---
 src/mol-theme/size/physical.ts                |  4 +-
 src/mol-theme/size/uniform.ts                 |  4 +-
 src/mol-theme/theme.ts                        | 19 ++++----
 src/mol-util/color/scale.ts                   | 11 ++++-
 src/mol-util/color/tables.ts                  | 10 ++++-
 34 files changed, 159 insertions(+), 116 deletions(-)

diff --git a/src/apps/canvas/index.ts b/src/apps/canvas/index.ts
index dd72f8aaa..16c12a720 100644
--- a/src/apps/canvas/index.ts
+++ b/src/apps/canvas/index.ts
@@ -28,14 +28,14 @@ if (pdbId) app.loadPdbIdOrMmcifUrl(pdbId, { assemblyId })
 // 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')
+app.loadPdbIdOrMmcifUrl('1lee')
+app.loadCcp4Url('http://localhost:8091/ngl/data/1lee.ccp4')
 
 // app.loadPdbIdOrMmcifUrl('6DRV')
 // app.loadCcp4Url('http://localhost:8091/ngl/data/betaGal.mrc')
 
 // app.loadPdbIdOrMmcifUrl('3pqr')
-// app.loadVolCifUrl('https://webchem.ncbr.muni.cz/DensityServer/x-ray/3pqr/cell?space=fractional', true)
+// app.loadVolcifUrl('https://webchem.ncbr.muni.cz/DensityServer/x-ray/3pqr/cell?space=fractional', true)
 
 // app.loadPdbIdOrMmcifUrl('5ire')
 // app.loadVolcifUrl('https://webchem.ncbr.muni.cz/DensityServer/em/emd-8116/cell?space=cartesian&detail=6', true)
diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts
index 95fbda73b..3d2c20fc4 100644
--- a/src/apps/canvas/structure-view.ts
+++ b/src/apps/canvas/structure-view.ts
@@ -202,7 +202,7 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon
                         structureRepresentations[k] = repr
                         canvas3d.add(repr)
                     }
-                    await app.runTask(repr.createOrUpdate(app.reprCtx, {}, {}, structure).run(
+                    await app.runTask(repr.createOrUpdate(app.reprCtx, {}, structure).run(
                         progress => app.log(progress)
                     ), 'Create/update representation')
                 } else {
@@ -261,7 +261,7 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon
                     //     colorFunction: colorTheme.color,
                     //     colorGranularity: colorTheme.granularity,
                     // }).run()
-                    await symmetryAxes.createOrUpdate(app.reprCtx, {}, {}, axesShape).run()
+                    await symmetryAxes.createOrUpdate(app.reprCtx, {}, axesShape).run()
                     canvas3d.add(symmetryAxes)
                 } else {
                     canvas3d.remove(symmetryAxes)
diff --git a/src/apps/canvas/volume-view.ts b/src/apps/canvas/volume-view.ts
index 850cba428..a0f39495f 100644
--- a/src/apps/canvas/volume-view.ts
+++ b/src/apps/canvas/volume-view.ts
@@ -54,7 +54,7 @@ export async function VolumeView(app: App, viewer: Canvas3D, volume: VolumeData,
     async function createVolumeRepr() {
         for (const k in volumeRepresentations) {
             if (active[k]) {
-                await app.runTask(volumeRepresentations[k].createOrUpdate(app.reprCtx, {}, {}, volume).run(
+                await app.runTask(volumeRepresentations[k].createOrUpdate(app.reprCtx, {}, volume).run(
                     progress => app.log(progress)
                 ), 'Create/update representation')
                 viewer.add(volumeRepresentations[k])
diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts
index 598e50977..190e21d27 100644
--- a/src/mol-geo/geometry/geometry.ts
+++ b/src/mol-geo/geometry/geometry.ts
@@ -15,8 +15,8 @@ import { SizeType } from './size-data';
 import { Lines } from './lines/lines';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
 import { DirectVolume } from './direct-volume/direct-volume';
-import { BuiltInSizeThemeOptions, BuiltInSizeThemeName } from 'mol-theme/size';
-import { BuiltInColorThemeName, BuiltInColorThemeOptions } from 'mol-theme/color';
+import { BuiltInSizeThemeOptions, getBuiltInSizeThemeParams } from 'mol-theme/size';
+import { BuiltInColorThemeOptions, getBuiltInColorThemeParams } from 'mol-theme/color';
 import { Color } from 'mol-util/color';
 import { Vec3 } from 'mol-math/linear-algebra';
 
@@ -69,8 +69,8 @@ export namespace Geometry {
 
         quality: PD.Select<VisualQuality>('auto', VisualQualityOptions),
 
-        colorTheme: PD.Select<BuiltInColorThemeName>('uniform', BuiltInColorThemeOptions),
-        sizeTheme: PD.Select<BuiltInSizeThemeName>('uniform', BuiltInSizeThemeOptions),
+        colorTheme: PD.Mapped('uniform', BuiltInColorThemeOptions, getBuiltInColorThemeParams),
+        sizeTheme: PD.Mapped('uniform', BuiltInSizeThemeOptions, getBuiltInSizeThemeParams),
     }
     export type Params = typeof Params
 
diff --git a/src/mol-plugin/state/transforms/visuals.ts b/src/mol-plugin/state/transforms/visuals.ts
index 81760fc7e..7c42b0490 100644
--- a/src/mol-plugin/state/transforms/visuals.ts
+++ b/src/mol-plugin/state/transforms/visuals.ts
@@ -15,9 +15,6 @@ export { CreateStructureRepresentation }
 namespace CreateStructureRepresentation {
     export interface Params {
         type: { name: string, params: any /** todo is there "common type" */ },
-        // TODO
-        // colorTheme: { name: string, params: any /** todo is there "common type" */ }
-        // sizeTheme: { name: string, params: any /** todo is there "common type" */ }
     }
 }
 const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, CreateStructureRepresentation.Params>({
@@ -47,7 +44,7 @@ const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.St
         return Task.create('Structure Representation', async ctx => {
             const provider = plugin.structureReprensentation.registry.get(params.type.name)
             const repr = provider.factory(provider.getParams)
-            await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl, ...plugin.structureReprensentation.themeCtx }, params.type.params || {}, {}, a.data).runInContext(ctx);
+            await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl, ...plugin.structureReprensentation.themeCtx }, params.type.params || {}, a.data).runInContext(ctx);
             return new SO.Molecule.Representation3D(repr, { label: provider.label });
         });
     },
@@ -55,7 +52,7 @@ const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.St
         return Task.create('Structure Representation', async ctx => {
             if (newParams.type.name !== oldParams.type.name) return Transformer.UpdateResult.Recreate;
 
-            await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl, ...plugin.structureReprensentation.themeCtx }, { ...b.data.props, ...newParams.type.params }, {}, a.data).runInContext(ctx);
+            await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl, ...plugin.structureReprensentation.themeCtx }, { ...b.data.props, ...newParams.type.params }, a.data).runInContext(ctx);
             return Transformer.UpdateResult.Updated;
         });
     }
diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx
index 9727a91c9..e118025d9 100644
--- a/src/mol-plugin/ui/controls/parameters.tsx
+++ b/src/mol-plugin/ui/controls/parameters.tsx
@@ -187,9 +187,9 @@ export class ConvertedControl extends React.PureComponent<ValueControlProps<PD.C
     }
 
     render() {
-        const Control: ValueControl = controlFor(this.props.param.param as PD.Any);
+        const Control: ValueControl = controlFor(this.props.param.convertedControl as PD.Any);
 
-        return <Control value={this.props.param.fromValue(this.props.value)} param={this.props.param.param} onChange={this.onChange} onEnter={this.props.onEnter} isEnabled={this.props.isEnabled} />
+        return <Control value={this.props.param.fromValue(this.props.value)} param={this.props.param.convertedControl} onChange={this.onChange} onEnter={this.props.onEnter} isEnabled={this.props.isEnabled} />
     }
 }
 
@@ -236,7 +236,7 @@ export class MappedControl extends React.PureComponent<MappedWrapperProps> {
         return <div>
             <ParamWrapper control={SelectControl} param={this.props.param.select}
                 isEnabled={this.props.isEnabled} onChange={this.onChangeName} onEnter={this.props.onEnter}
-                name={'name'} value={value.name} />
+                name={getLabel(this.props.name, this.props.param)} value={value.name} />
             <div style={{ borderLeft: '5px solid #777', paddingLeft: '5px' }}>
                 {param.type === 'group'
                     ? <GroupControl param={param} value={value} name='param' onChange={this.onChangeParam} onEnter={this.props.onEnter} isEnabled={this.props.isEnabled} />
diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts
index c456ffcfc..1325c1481 100644
--- a/src/mol-repr/representation.ts
+++ b/src/mol-repr/representation.ts
@@ -14,7 +14,7 @@ import { WebGLContext } from 'mol-gl/webgl/context';
 import { getQualityProps } from './util';
 import { ColorTheme } from 'mol-theme/color';
 import { SizeTheme } from 'mol-theme/size';
-import { ThemeProps, Theme, ThemeRegistryContext } from 'mol-theme/theme';
+import { Theme, ThemeRegistryContext } from 'mol-theme/theme';
 import { BehaviorSubject } from 'rxjs';
 
 // export interface RepresentationProps {
@@ -84,7 +84,7 @@ interface Representation<D, P extends PD.Params = {}> {
     readonly renderObjects: ReadonlyArray<RenderObject>
     readonly props: Readonly<PD.Values<P>>
     readonly params: Readonly<P>
-    createOrUpdate: (ctx: RepresentationContext, props?: Partial<PD.Values<P>>, themeProps?: ThemeProps, data?: D) => Task<void>
+    createOrUpdate: (ctx: RepresentationContext, props?: Partial<PD.Values<P>>, data?: D) => Task<void>
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     setVisibility: (value: boolean) => void
@@ -137,7 +137,7 @@ namespace Representation {
                 return props as P
             },
             get params() { return currentParams },
-            createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, data?: D) => {
+            createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, data?: D) => {
                 if (data && data !== currentData) {
                     currentParams = getParams(ctx, data)
                     currentData = data
@@ -150,7 +150,7 @@ namespace Representation {
                 return Task.create(`Creating '${label}' representation`, async runtime => {
                     for (let i = 0, il = reprList.length; i < il; ++i) {
                         if (!visuals || visuals.includes(reprMap[i])) {
-                            await reprList[i].createOrUpdate(ctx, currentProps, themeProps, currentData).runInContext(runtime)
+                            await reprList[i].createOrUpdate(ctx, currentProps, currentData).runInContext(runtime)
                         }
                     }
                     updated.next(updated.getValue() + 1)
diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts
index f9588891c..27c7d9e73 100644
--- a/src/mol-repr/shape/representation.ts
+++ b/src/mol-repr/shape/representation.ts
@@ -18,7 +18,7 @@ import { createRenderableState } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
-import { ThemeProps, createTheme } from 'mol-theme/theme';
+import { createTheme } from 'mol-theme/theme';
 import { BehaviorSubject } from 'rxjs';
 
 export interface ShapeRepresentation<P extends ShapeParams> extends Representation<Shape, P> { }
@@ -38,7 +38,7 @@ export function ShapeRepresentation<P extends ShapeParams>(): ShapeRepresentatio
     let currentProps: PD.Values<P> = PD.getDefaultValues(ShapeParams) as PD.Values<P>
     let currentParams: P
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, shape?: Shape) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, shape?: Shape) {
         currentProps = Object.assign({}, currentProps, props)
         if (shape) _shape = shape
 
@@ -49,7 +49,7 @@ export function ShapeRepresentation<P extends ShapeParams>(): ShapeRepresentatio
 
             const mesh = _shape.mesh
             const locationIt = ShapeGroupIterator.fromShape(_shape)
-            const theme = createTheme(ctx, currentProps, themeProps, {})
+            const theme = createTheme(ctx, currentProps, {})
             const transform = createIdentityTransform()
 
             const values = await Mesh.createValues(runtime, mesh, transform, locationIt, theme, currentProps)
diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts
index c99f9c32b..f74d05510 100644
--- a/src/mol-repr/structure/complex-representation.ts
+++ b/src/mol-repr/structure/complex-representation.ts
@@ -13,7 +13,7 @@ import { ComplexVisual } from './complex-visual';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { RepresentationContext, RepresentationParamsGetter } from 'mol-repr/representation';
-import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
+import { Theme, createTheme } from 'mol-theme/theme';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { BehaviorSubject } from 'rxjs';
 
@@ -26,14 +26,14 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
     let _props: PD.Values<P>
     let _theme: Theme
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, structure?: Structure) {
         if (structure && structure !== _structure) {
             _params = getParams(ctx, structure)
             _structure = structure
             if (!_props) _props = PD.getDefaultValues(_params)
         }
         _props = Object.assign({}, _props, props)
-        _theme = createTheme(ctx, { structure: _structure }, props, themeProps, _theme)
+        _theme = createTheme(ctx, { structure: _structure }, props, _theme)
 
         return Task.create('Creating or updating ComplexRepresentation', async runtime => {
             if (!visual) visual = visualCtor()
diff --git a/src/mol-repr/structure/representation/ball-and-stick.ts b/src/mol-repr/structure/representation/ball-and-stick.ts
index a5fe7d277..d28d47897 100644
--- a/src/mol-repr/structure/representation/ball-and-stick.ts
+++ b/src/mol-repr/structure/representation/ball-and-stick.ts
@@ -14,8 +14,7 @@ import { StructureRepresentation, StructureRepresentationProvider } from '../rep
 import { Representation, RepresentationParamsGetter } from 'mol-repr/representation';
 import { ThemeRegistryContext } from 'mol-theme/theme';
 import { Structure } from 'mol-model/structure';
-import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size';
-import { BuiltInColorThemeName, BuiltInColorThemeOptions } from 'mol-theme/color';
+import { BuiltInColorThemeOptions, BuiltInColorThemes, ColorTheme } from 'mol-theme/color';
 import { UnitKind, UnitKindOptions } from '../visual/util/common';
 
 const BallAndStickVisuals = {
@@ -32,8 +31,7 @@ export const BallAndStickParams = {
     ...InterUnitLinkParams,
     unitKinds: PD.MultiSelect<UnitKind>(['atomic'], UnitKindOptions),
     sizeFactor: PD.Numeric(0.2, { min: 0.01, max: 10, step: 0.01 }),
-    sizeTheme: PD.Select<BuiltInSizeThemeName>('uniform', BuiltInSizeThemeOptions),
-    colorTheme: PD.Select<BuiltInColorThemeName>('polymer-index', BuiltInColorThemeOptions),
+    colorTheme: PD.Mapped('polymer-index', BuiltInColorThemeOptions, name => PD.Group((BuiltInColorThemes as { [k: string]: ColorTheme.Provider<any> })[name].getParams({}))),
     visuals: PD.MultiSelect<BallAndStickVisualName>(['element-sphere', 'intra-link', 'inter-link'], BallAndStickVisualOptions),
 }
 export type BallAndStickParams = typeof BallAndStickParams
diff --git a/src/mol-repr/structure/representation/cartoon.ts b/src/mol-repr/structure/representation/cartoon.ts
index 1bdd3534b..ccb54f933 100644
--- a/src/mol-repr/structure/representation/cartoon.ts
+++ b/src/mol-repr/structure/representation/cartoon.ts
@@ -14,8 +14,7 @@ import { Representation, RepresentationParamsGetter } from 'mol-repr/representat
 import { PolymerDirectionVisual, PolymerDirectionParams } from '../visual/polymer-direction-wedge';
 import { Structure } from 'mol-model/structure';
 import { ThemeRegistryContext } from 'mol-theme/theme';
-import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size';
-import { BuiltInColorThemeOptions, BuiltInColorThemeName } from 'mol-theme/color';
+import { BuiltInColorThemeOptions, getBuiltInColorThemeParams } from 'mol-theme/color';
 
 const CartoonVisuals = {
     'polymer-trace': (getParams: RepresentationParamsGetter<Structure, PolymerTraceParams>) => UnitsRepresentation('Polymer trace mesh', getParams, PolymerTraceVisual),
@@ -32,10 +31,10 @@ export const CartoonParams = {
     ...NucleotideBlockParams,
     ...PolymerDirectionParams,
     sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
-    sizeTheme: PD.Select<BuiltInSizeThemeName>('uniform', BuiltInSizeThemeOptions),
-    colorTheme: PD.Select<BuiltInColorThemeName>('polymer-index', BuiltInColorThemeOptions),
+    colorTheme: PD.Mapped('polymer-index', BuiltInColorThemeOptions, getBuiltInColorThemeParams),
     visuals: PD.MultiSelect<CartoonVisualName>(['polymer-trace', 'polymer-gap', 'nucleotide-block'], CartoonVisualOptions),
 }
+PD.getDefaultValues(CartoonParams).colorTheme.name
 export type CartoonParams = typeof CartoonParams
 export function getCartoonParams(ctx: ThemeRegistryContext, structure: Structure) {
     return PD.clone(CartoonParams)
diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts
index b836cc849..42bb3fb86 100644
--- a/src/mol-repr/structure/units-representation.ts
+++ b/src/mol-repr/structure/units-representation.ts
@@ -14,7 +14,7 @@ import { StructureGroup } from './units-visual';
 import { StructureRepresentation, StructureParams } from './representation';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
-import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
+import { Theme, createTheme } from 'mol-theme/theme';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { UnitKind, UnitKindOptions } from './visual/util/common';
 import { BehaviorSubject } from 'rxjs';
@@ -37,13 +37,13 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, getPar
     let _props: PD.Values<P>
     let _theme: Theme
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, structure?: Structure) {
         if (structure && structure !== _structure) {
             _params = getParams(ctx, structure)
             if (!_props) _props = PD.getDefaultValues(_params)
         }
         _props = Object.assign({}, _props, props)
-        _theme = createTheme(ctx, { structure: structure || _structure }, props, themeProps, _theme)
+        _theme = createTheme(ctx, { structure: structure || _structure }, props, _theme)
 
         return Task.create('Creating or updating UnitsRepresentation', async runtime => {
             if (!_structure && !structure) {
diff --git a/src/mol-repr/structure/visual/element-sphere.ts b/src/mol-repr/structure/visual/element-sphere.ts
index 4c1832195..8f8002230 100644
--- a/src/mol-repr/structure/visual/element-sphere.ts
+++ b/src/mol-repr/structure/visual/element-sphere.ts
@@ -10,11 +10,11 @@ import { VisualUpdateState } from '../../util';
 import { createElementSphereMesh, markElement, getElementLoci, StructureElementIterator } from './util/element';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size';
+import { BuiltInSizeThemeOptions, getBuiltInSizeThemeParams } from 'mol-theme/size';
 
 export const ElementSphereParams = {
     ...UnitsMeshParams,
-    sizeTheme: PD.Select<BuiltInSizeThemeName>('physical', BuiltInSizeThemeOptions),
+    sizeTheme: PD.Mapped('physical', BuiltInSizeThemeOptions, getBuiltInSizeThemeParams),
     sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
     detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
 }
diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts
index c252534c8..12e24cdf1 100644
--- a/src/mol-repr/volume/representation.ts
+++ b/src/mol-repr/volume/representation.ts
@@ -19,7 +19,7 @@ import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { NullLocation } from 'mol-model/location';
 import { VisualUpdateState } from 'mol-repr/util';
 import { ValueCell } from 'mol-util';
-import { ThemeProps, Theme, createTheme } from 'mol-theme/theme';
+import { Theme, createTheme } from 'mol-theme/theme';
 import { BehaviorSubject } from 'rxjs';
 
 export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
@@ -149,14 +149,14 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, getP
     let _theme: Theme
     let busy = false
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
         if (volume && volume !== _volume) {
             _params = getParams(ctx, volume)
             _volume = volume
             if (!_props) _props = PD.getDefaultValues(_params)
         }
         _props = Object.assign({}, _props, props)
-        _theme = createTheme(ctx, _props, themeProps, {}, _theme)
+        _theme = createTheme(ctx, _props, {}, _theme)
 
         return Task.create('VolumeRepresentation.create', async runtime => {
             // TODO queue it somehow
diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts
index 028de8ef2..31c5b3fd5 100644
--- a/src/mol-theme/color.ts
+++ b/src/mol-theme/color.ts
@@ -11,7 +11,7 @@ import { CarbohydrateSymbolColorThemeProvider } from './color/carbohydrate-symbo
 import { UniformColorTheme, UniformColorThemeProvider } from './color/uniform';
 import { deepEqual } from 'mol-util';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext } from './theme';
 import { ChainIdColorThemeProvider } from './color/chain-id';
 import { CrossLinkColorThemeProvider } from './color/cross-link';
 import { ElementIndexColorThemeProvider } from './color/element-index';
@@ -23,27 +23,11 @@ import { SecondaryStructureColorThemeProvider } from './color/secondary-structur
 import { SequenceIdColorThemeProvider } from './color/sequence-id';
 import { ShapeGroupColorThemeProvider } from './color/shape-group';
 import { UnitIndexColorThemeProvider } from './color/unit-index';
+import { ScaleLegend } from 'mol-util/color/scale';
+import { TableLegend } from 'mol-util/color/tables';
 
 export type LocationColor = (location: Location, isSecondary: boolean) => Color
 
-export interface ScaleLegend {
-    kind: 'scale-legend'
-    minLabel: string,
-    maxLabel: string,
-    colors: Color[]
-}
-export function ScaleLegend(minLabel: string, maxLabel: string, colors: Color[]): ScaleLegend {
-    return { kind: 'scale-legend', minLabel, maxLabel, colors }
-}
-
-export interface TableLegend {
-    kind: 'table-legend'
-    table: [ string, Color ][]
-}
-export function TableLegend(table: [ string, Color ][]): TableLegend {
-    return { kind: 'table-legend', table }
-}
-
 export type ColorThemeProps = { [k: string]: any }
 
 export { ColorTheme }
@@ -63,34 +47,40 @@ namespace ColorTheme {
     }
 
     export interface Provider<P extends PD.Params> {
+        readonly label: string
         readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<PD.Values<P>>
-        readonly params: (ctx: ThemeDataContext) => P
+        readonly getParams: (ctx: ThemeDataContext) => P
     }
+    export const EmptyProvider: Provider<{}> = { label: '', factory: () => Empty, getParams: () => ({}) }
 
     export class Registry {
         private _list: { name: string, provider: Provider<any> }[] = []
         private _map = new Map<string, Provider<any>>()
 
+        get default() { return this._list[0]; }
+        get types(): [string, string][] {
+            return this._list.map(e => [e.name, e.provider.label] as [string, string]);
+        }
+
         constructor() {
             Object.keys(BuiltInColorThemes).forEach(name => {
                 const p = (BuiltInColorThemes as { [k: string]: Provider<any> })[name]
-                this.add(name, p.factory, p.params)
+                this.add(name, p)
             })
         }
 
-        add<P extends PD.Params>(name: string, factory: Provider<P>['factory'], params: Provider<P>['params']) {
-            const provider = { factory, params } as Provider<P>
+        add<P extends PD.Params>(name: string, provider: Provider<P>) {
             this._list.push({ name, provider })
             this._map.set(name, provider)
         }
 
-        get(id: string) {
-            return this._map.get(id)
+        get<P extends PD.Params>(name: string): Provider<P> {
+            return this._map.get(name) || EmptyProvider as unknown as Provider<P>
         }
 
         create(id: string, ctx: ThemeDataContext, props = {}) {
             const provider = this.get(id)
-            return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.params(ctx)), ...props }) : Empty
+            return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.getParams(ctx)), ...props }) : Empty
         }
 
         get list() {
@@ -116,4 +106,5 @@ export const BuiltInColorThemes = {
 }
 export type BuiltInColorThemeName = keyof typeof BuiltInColorThemes
 export const BuiltInColorThemeNames = Object.keys(BuiltInColorThemes)
-export const BuiltInColorThemeOptions = BuiltInColorThemeNames.map(n => [n, n] as [BuiltInColorThemeName, string])
\ No newline at end of file
+export const BuiltInColorThemeOptions = BuiltInColorThemeNames.map(n => [n, n] as [BuiltInColorThemeName, string])
+export const getBuiltInColorThemeParams = (name: string, ctx: ThemeDataContext = {}) => PD.Group((BuiltInColorThemes as { [k: string]: ColorTheme.Provider<any> })[name].getParams(ctx))
\ No newline at end of file
diff --git a/src/mol-theme/color/carbohydrate-symbol.ts b/src/mol-theme/color/carbohydrate-symbol.ts
index ecb4b26c4..5063ca0c7 100644
--- a/src/mol-theme/color/carbohydrate-symbol.ts
+++ b/src/mol-theme/color/carbohydrate-symbol.ts
@@ -8,10 +8,11 @@ import { StructureElement, Link, ElementIndex, Unit } from 'mol-model/structure'
 
 import { SaccharideColors, MonosaccharidesColorTable } from 'mol-model/structure/structure/carbohydrates/constants';
 import { Location } from 'mol-model/location';
-import { ColorTheme, LocationColor, TableLegend } from '../color';
+import { ColorTheme, LocationColor } from '../color';
 import { Color } from 'mol-util/color';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext } from '../theme';
+import { TableLegend } from 'mol-util/color/tables';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Assigns colors according to the Symbol Nomenclature for Glycans (SNFG).'
@@ -73,5 +74,7 @@ export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: Carbo
 }
 
 export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<typeof CarbohydrateSymbolColorThemeParams> = {
-    factory: CarbohydrateSymbolColorTheme, params: getCarbohydrateSymbolColorThemeParams
+    label: 'Carbohydrate Symbol',
+    factory: CarbohydrateSymbolColorTheme,
+    getParams: getCarbohydrateSymbolColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/chain-id.ts b/src/mol-theme/color/chain-id.ts
index 139da7c8a..2983ce357 100644
--- a/src/mol-theme/color/chain-id.ts
+++ b/src/mol-theme/color/chain-id.ts
@@ -80,5 +80,7 @@ export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThem
 }
 
 export const ChainIdColorThemeProvider: ColorTheme.Provider<typeof ChainIdColorThemeParams> = {
-    factory: ChainIdColorTheme, params: getChainIdColorThemeParams
+    label: 'Chain Id',
+    factory: ChainIdColorTheme,
+    getParams: getChainIdColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/cross-link.ts b/src/mol-theme/color/cross-link.ts
index 1f6ff23b4..74b36b07e 100644
--- a/src/mol-theme/color/cross-link.ts
+++ b/src/mol-theme/color/cross-link.ts
@@ -68,5 +68,7 @@ export function CrossLinkColorTheme(ctx: ThemeDataContext, props: CrossLinkColor
 }
 
 export const CrossLinkColorThemeProvider: ColorTheme.Provider<typeof CrossLinkColorThemeParams> = {
-    factory: CrossLinkColorTheme, params: getCrossLinkColorThemeParams
+    label: 'Cross Link',
+    factory: CrossLinkColorTheme,
+    getParams: getCrossLinkColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/element-index.ts b/src/mol-theme/color/element-index.ts
index d0eb06e61..2307fd4d7 100644
--- a/src/mol-theme/color/element-index.ts
+++ b/src/mol-theme/color/element-index.ts
@@ -68,5 +68,7 @@ export function ElementIndexColorTheme(ctx: ThemeDataContext, props: ElementInde
 }
 
 export const ElementIndexColorThemeProvider: ColorTheme.Provider<typeof ElementIndexColorThemeParams> = {
-    factory: ElementIndexColorTheme, params: getElementIndexColorThemeParams
+    label: 'Element Index',
+    factory: ElementIndexColorTheme,
+    getParams: getElementIndexColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/element-symbol.ts b/src/mol-theme/color/element-symbol.ts
index 33bc576fb..2e7650413 100644
--- a/src/mol-theme/color/element-symbol.ts
+++ b/src/mol-theme/color/element-symbol.ts
@@ -8,9 +8,10 @@ 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 { ColorTheme, TableLegend } from '../color';
+import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext } from '../theme';
+import { TableLegend } from 'mol-util/color/tables';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF)
 export const ElementSymbolColors = ColorMap({
@@ -59,5 +60,7 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: ElementSym
 }
 
 export const ElementSymbolColorThemeProvider: ColorTheme.Provider<typeof ElementSymbolColorThemeParams> = {
-    factory: ElementSymbolColorTheme, params: getElementSymbolColorThemeParams
+    label: 'Element Symbol',
+    factory: ElementSymbolColorTheme,
+    getParams: getElementSymbolColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/molecule-type.ts b/src/mol-theme/color/molecule-type.ts
index d0581ac4b..11c87cbfb 100644
--- a/src/mol-theme/color/molecule-type.ts
+++ b/src/mol-theme/color/molecule-type.ts
@@ -7,11 +7,12 @@
 import { Color, ColorMap } from 'mol-util/color';
 import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
-import { ColorTheme, TableLegend } from '../color';
+import { ColorTheme } from '../color';
 import { MoleculeType } from 'mol-model/structure/model/types';
 import { getElementMoleculeType } from 'mol-model/structure/util';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext } from '../theme';
+import { TableLegend } from 'mol-util/color/tables';
 
 const MoleculeTypeColors = ColorMap({
     water: 0x386cb0,
@@ -68,5 +69,7 @@ export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: MoleculeTyp
 }
 
 export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<typeof MoleculeTypeColorThemeParams> = {
-    factory: MoleculeTypeColorTheme, params: getMoleculeTypeColorThemeParams
+    label: 'Molecule Type',
+    factory: MoleculeTypeColorTheme,
+    getParams: getMoleculeTypeColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/polymer-index.ts b/src/mol-theme/color/polymer-index.ts
index f34dc35a7..ed5cc40d6 100644
--- a/src/mol-theme/color/polymer-index.ts
+++ b/src/mol-theme/color/polymer-index.ts
@@ -65,5 +65,7 @@ export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PolymerInde
 }
 
 export const PolymerIndexColorThemeProvider: ColorTheme.Provider<typeof PolymerIndexColorThemeParams> = {
-    factory: PolymerIndexColorTheme, params: getPolymerIndexColorThemeParams
+    label: 'Polymer Index',
+    factory: PolymerIndexColorTheme,
+    getParams: getPolymerIndexColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/residue-name.ts b/src/mol-theme/color/residue-name.ts
index d529c1452..178d49634 100644
--- a/src/mol-theme/color/residue-name.ts
+++ b/src/mol-theme/color/residue-name.ts
@@ -7,9 +7,10 @@
 import { Color, ColorMap } from 'mol-util/color';
 import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
-import { ColorTheme, TableLegend } from '../color';
+import { ColorTheme } from '../color';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext } from '../theme';
+import { TableLegend } from 'mol-util/color/tables';
 
 // protein colors from Jmol http://jmol.sourceforge.net/jscolors/
 const ResidueNameColors = ColorMap({
@@ -124,5 +125,7 @@ export function ResidueNameColorTheme(ctx: ThemeDataContext, props: ResidueNameC
 }
 
 export const ResidueNameColorThemeProvider: ColorTheme.Provider<typeof ResidueNameColorThemeParams> = {
-    factory: ResidueNameColorTheme, params: getResidueNameColorThemeParams
+    label: 'Residue Name',
+    factory: ResidueNameColorTheme,
+    getParams: getResidueNameColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/secondary-structure.ts b/src/mol-theme/color/secondary-structure.ts
index a11eff2a7..955846c5f 100644
--- a/src/mol-theme/color/secondary-structure.ts
+++ b/src/mol-theme/color/secondary-structure.ts
@@ -7,11 +7,12 @@
 import { Color, ColorMap } from 'mol-util/color';
 import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
-import { ColorTheme, TableLegend } from '../color';
+import { ColorTheme } from '../color';
 import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types';
 import { getElementMoleculeType } from 'mol-model/structure/util';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext } from '../theme';
+import { TableLegend } from 'mol-util/color/tables';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (shapely)
 const SecondaryStructureColors = ColorMap({
@@ -91,5 +92,7 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: Secon
 }
 
 export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<typeof SecondaryStructureColorThemeParams> = {
-    factory: SecondaryStructureColorTheme, params: getSecondaryStructureColorThemeParams
+    label: 'Secondary Structure',
+    factory: SecondaryStructureColorTheme,
+    getParams: getSecondaryStructureColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/sequence-id.ts b/src/mol-theme/color/sequence-id.ts
index 43023587e..4fbfafa63 100644
--- a/src/mol-theme/color/sequence-id.ts
+++ b/src/mol-theme/color/sequence-id.ts
@@ -99,5 +99,7 @@ export function SequenceIdColorTheme(ctx: ThemeDataContext, props: SequenceIdCol
 }
 
 export const SequenceIdColorThemeProvider: ColorTheme.Provider<typeof SequenceIdColorThemeParams> = {
-    factory: SequenceIdColorTheme, params: getSequenceIdColorThemeParams
+    label: 'Sequence Id',
+    factory: SequenceIdColorTheme,
+    getParams: getSequenceIdColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/shape-group.ts b/src/mol-theme/color/shape-group.ts
index acbb30625..4d30c299b 100644
--- a/src/mol-theme/color/shape-group.ts
+++ b/src/mol-theme/color/shape-group.ts
@@ -35,5 +35,7 @@ export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: ShapeGroupCol
 }
 
 export const ShapeGroupColorThemeProvider: ColorTheme.Provider<typeof ShapeGroupColorThemeParams> = {
-    factory: ShapeGroupColorTheme, params: getShapeGroupColorThemeParams
+    label: 'Shape Group',
+    factory: ShapeGroupColorTheme,
+    getParams: getShapeGroupColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/uniform.ts b/src/mol-theme/color/uniform.ts
index 905deb9b3..0716bb4ed 100644
--- a/src/mol-theme/color/uniform.ts
+++ b/src/mol-theme/color/uniform.ts
@@ -4,10 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ColorTheme, TableLegend } from '../color';
+import { ColorTheme } from '../color';
 import { Color } from 'mol-util/color';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
-import { ThemeDataContext } from 'mol-theme/theme';
+import { ThemeDataContext } from '../theme';
+import { TableLegend } from 'mol-util/color/tables';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives everything the same, uniform color.'
@@ -33,5 +34,7 @@ export function UniformColorTheme(ctx: ThemeDataContext, props: UniformColorThem
 }
 
 export const UniformColorThemeProvider: ColorTheme.Provider<typeof UniformColorThemeParams> = {
-    factory: UniformColorTheme, params: getUniformColorThemeParams
+    label: 'Uniform',
+    factory: UniformColorTheme,
+    getParams: getUniformColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/color/unit-index.ts b/src/mol-theme/color/unit-index.ts
index 0a5079615..15494fc85 100644
--- a/src/mol-theme/color/unit-index.ts
+++ b/src/mol-theme/color/unit-index.ts
@@ -57,5 +57,7 @@ export function UnitIndexColorTheme(ctx: ThemeDataContext, props: UnitIndexColor
 }
 
 export const UnitIndexColorThemeProvider: ColorTheme.Provider<typeof UnitIndexColorThemeParams> = {
-    factory: UnitIndexColorTheme, params: getUnitIndexColorThemeParams
+    label: 'Unit Index',
+    factory: UnitIndexColorTheme,
+    getParams: getUnitIndexColorThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/size.ts b/src/mol-theme/size.ts
index 4676d896e..22042a455 100644
--- a/src/mol-theme/size.ts
+++ b/src/mol-theme/size.ts
@@ -27,23 +27,28 @@ namespace SizeTheme {
     }
 
     export interface Provider<P extends PD.Params> {
+        readonly label: string
         readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => SizeTheme<PD.Values<P>>
-        readonly params: (ctx: ThemeDataContext) => P
+        readonly getParams: (ctx: ThemeDataContext) => P
     }
 
     export class Registry {
         private _list: { name: string, provider: Provider<any> }[] = []
         private _map = new Map<string, Provider<any>>()
 
+        get default() { return this._list[0]; }
+        get types(): [string, string][] {
+            return this._list.map(e => [e.name, e.provider.label] as [string, string]);
+        }
+
         constructor() {
             Object.keys(BuiltInSizeThemes).forEach(name => {
                 const p = (BuiltInSizeThemes as { [k: string]: Provider<any> })[name]
-                this.add(name, p.factory, p.params)
+                this.add(name, p)
             })
         }
 
-        add<P extends PD.Params>(name: string, factory: Provider<P>['factory'], params: Provider<P>['params']) {
-            const provider = { factory, params } as Provider<P>
+        add<P extends PD.Params>(name: string, provider: Provider<P>) {
             this._list.push({ name, provider })
             this._map.set(name, provider)
         }
@@ -54,7 +59,7 @@ namespace SizeTheme {
 
         create(id: string, ctx: ThemeDataContext, props = {}) {
             const provider = this.get(id)
-            return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.params(ctx)), ...props }) : Empty
+            return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.getParams(ctx)), ...props }) : Empty
         }
 
         get list() {
@@ -69,4 +74,5 @@ export const BuiltInSizeThemes = {
 }
 export type BuiltInSizeThemeName = keyof typeof BuiltInSizeThemes
 export const BuiltInSizeThemeNames = Object.keys(BuiltInSizeThemes)
-export const BuiltInSizeThemeOptions = BuiltInSizeThemeNames.map(n => [n, n] as [BuiltInSizeThemeName, string])
\ No newline at end of file
+export const BuiltInSizeThemeOptions = BuiltInSizeThemeNames.map(n => [n, n] as [BuiltInSizeThemeName, string])
+export const getBuiltInSizeThemeParams = (name: string, ctx: ThemeDataContext = {}) => PD.Group((BuiltInSizeThemes as { [k: string]: SizeTheme.Provider<any> })[name].getParams(ctx))
\ No newline at end of file
diff --git a/src/mol-theme/size/physical.ts b/src/mol-theme/size/physical.ts
index 65017be88..c7a62dcdb 100644
--- a/src/mol-theme/size/physical.ts
+++ b/src/mol-theme/size/physical.ts
@@ -56,5 +56,7 @@ export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PhysicalSizeThem
 }
 
 export const PhysicalSizeThemeProvider: SizeTheme.Provider<typeof PhysicalSizeThemeParams> = {
-    factory: PhysicalSizeTheme, params: getPhysicalSizeThemeParams
+    label: 'Physical',
+    factory: PhysicalSizeTheme,
+    getParams: getPhysicalSizeThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/size/uniform.ts b/src/mol-theme/size/uniform.ts
index 8d12a1600..5040b9675 100644
--- a/src/mol-theme/size/uniform.ts
+++ b/src/mol-theme/size/uniform.ts
@@ -30,5 +30,7 @@ export function UniformSizeTheme(ctx: ThemeDataContext, props: UniformSizeThemeP
 }
 
 export const UniformSizeThemeProvider: SizeTheme.Provider<typeof UniformSizeThemeParams> = {
-    factory: UniformSizeTheme, params: getUniformSizeThemeParams
+    label: 'Uniform',
+    factory: UniformSizeTheme,
+    getParams: getUniformSizeThemeParams
 }
\ No newline at end of file
diff --git a/src/mol-theme/theme.ts b/src/mol-theme/theme.ts
index a5f1a3e80..f52ad6fd7 100644
--- a/src/mol-theme/theme.ts
+++ b/src/mol-theme/theme.ts
@@ -8,6 +8,7 @@ import { ColorTheme } from './color';
 import { SizeTheme } from './size';
 import { Structure } from 'mol-model/structure';
 import { VolumeData } from 'mol-model/volume';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
 
 export interface ThemeRegistryContext {
     colorThemeRegistry: ColorTheme.Registry
@@ -20,8 +21,6 @@ export interface ThemeDataContext {
     volume?: VolumeData
 }
 
-export interface ThemeProps { color?: {}, size?: {} }
-
 export interface Theme {
     color: ColorTheme
     size: SizeTheme
@@ -30,17 +29,17 @@ export interface Theme {
 
 type Props = { [k: string]: any }
 
-export function createTheme(ctx: ThemeRegistryContext, data: ThemeDataContext, props: Props, themeProps: ThemeProps = {}, theme?: Theme) {
+export function createTheme(ctx: ThemeRegistryContext, data: ThemeDataContext, props: Props, theme?: Theme) {
     theme = theme || {
         color: ColorTheme.Empty,
         size: SizeTheme.Empty
     }
-    // TODO check if props have changed
-    if (typeof props.colorTheme === 'string') {
-        theme.color = ctx.colorThemeRegistry.create(props.colorTheme, data, themeProps.color)
-    }
-    if (typeof props.sizeTheme === 'string') {
-        theme.size = ctx.sizeThemeRegistry.create(props.sizeTheme, data, themeProps.size)
-    }
+
+    const colorProps = props.colorTheme as PD.NamedParams
+    const sizeProps = props.sizeTheme as PD.NamedParams
+
+    theme.color = ctx.colorThemeRegistry.create(colorProps.name, data, colorProps.params)
+    theme.size = ctx.sizeThemeRegistry.create(sizeProps.name, data, sizeProps.params)
+
     return theme
 }
\ No newline at end of file
diff --git a/src/mol-util/color/scale.ts b/src/mol-util/color/scale.ts
index 7ed018496..328a79b7b 100644
--- a/src/mol-util/color/scale.ts
+++ b/src/mol-util/color/scale.ts
@@ -6,7 +6,6 @@
 
 import { Color } from './color'
 import { ColorBrewer, ColorMatplotlib, ColorOther } from './tables'
-import { ScaleLegend } from 'mol-theme/color';
 import { defaults } from 'mol-util';
 
 export type ColorListName = (
@@ -31,6 +30,16 @@ export function getColorListFromName(name: ColorListName) {
 
 //
 
+export interface ScaleLegend {
+    kind: 'scale-legend'
+    minLabel: string,
+    maxLabel: string,
+    colors: Color[]
+}
+export function ScaleLegend(minLabel: string, maxLabel: string, colors: Color[]): ScaleLegend {
+    return { kind: 'scale-legend', minLabel, maxLabel, colors }
+}
+
 export interface ColorScale {
     /** Returns hex color for given value */
     color: (value: number) => Color
diff --git a/src/mol-util/color/tables.ts b/src/mol-util/color/tables.ts
index 6ee4aa931..758fe1535 100644
--- a/src/mol-util/color/tables.ts
+++ b/src/mol-util/color/tables.ts
@@ -4,7 +4,15 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ColorMap, ColorTable } from './color';
+import { ColorMap, ColorTable, Color } from './color';
+
+export interface TableLegend {
+    kind: 'table-legend'
+    table: [ string, Color ][]
+}
+export function TableLegend(table: [ string, Color ][]): TableLegend {
+    return { kind: 'table-legend', table }
+}
 
 /**
  * Brewer Color Lists
-- 
GitLab