From 6359f3a87938c6d1b0e66d336e2151f20faa84f0 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Wed, 21 Nov 2018 15:13:29 -0800
Subject: [PATCH] wip, repr.setTheme

---
 src/mol-geo/geometry/geometry.ts              |  5 ----
 src/mol-plugin/state/actions/basic.ts         | 21 +++++++++++--
 .../state/transforms/representation.ts        | 30 +++++++++++++++----
 src/mol-repr/representation.ts                | 14 +++++++--
 src/mol-repr/shape/representation.ts          | 10 +++++--
 .../structure/complex-representation.ts       | 11 +++++--
 .../representation/ball-and-stick.ts          |  2 --
 .../structure/representation/carbohydrate.ts  |  3 --
 .../structure/representation/cartoon.ts       |  3 --
 .../representation/molecular-surface.ts       |  3 --
 .../structure/units-representation.ts         | 11 +++++--
 .../structure/visual/element-sphere.ts        |  2 --
 src/mol-repr/volume/representation.ts         | 11 +++++--
 src/mol-theme/color.ts                        |  6 +---
 src/mol-theme/size.ts                         | 11 +++----
 src/mol-theme/theme.ts                        |  2 +-
 16 files changed, 92 insertions(+), 53 deletions(-)

diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts
index eee463dc1..335d2349a 100644
--- a/src/mol-geo/geometry/geometry.ts
+++ b/src/mol-geo/geometry/geometry.ts
@@ -15,8 +15,6 @@ 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, 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';
 
@@ -67,9 +65,6 @@ export namespace Geometry {
         selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
 
         quality: PD.Select<VisualQuality>('auto', VisualQualityOptions),
-
-        colorTheme: PD.Mapped('uniform', BuiltInColorThemeOptions, getBuiltInColorThemeParams),
-        sizeTheme: PD.Mapped('uniform', BuiltInSizeThemeOptions, getBuiltInSizeThemeParams),
     }
     export type Params = typeof Params
 
diff --git a/src/mol-plugin/state/actions/basic.ts b/src/mol-plugin/state/actions/basic.ts
index 95e00158c..190dc4fd5 100644
--- a/src/mol-plugin/state/actions/basic.ts
+++ b/src/mol-plugin/state/actions/basic.ts
@@ -14,6 +14,9 @@ import { BallAndStickParams } from 'mol-repr/structure/representation/ball-and-s
 import { Download } from '../transforms/data';
 import { StateTree } from 'mol-state';
 import { StateTreeBuilder } from 'mol-state/tree/builder';
+import { PolymerIdColorThemeParams } from 'mol-theme/color/polymer-id';
+import { UniformSizeThemeParams } from 'mol-theme/size/uniform';
+import { ElementSymbolColorThemeParams } from 'mol-theme/color/element-symbol';
 
 // TODO: "structure parser provider"
 
@@ -106,11 +109,23 @@ function createStructureTree(b: StateTreeBuilder.To<PluginStateObject.Data.Binar
 
 function complexRepresentation(root: StateTreeBuilder.To<PluginStateObject.Molecule.Structure>) {
     root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
-        .apply(StateTransforms.Representation.StructureRepresentation3D, { type: { name: 'cartoon', params: PD.getDefaultValues(CartoonParams) } });
+        .apply(StateTransforms.Representation.StructureRepresentation3D, {
+            type: { name: 'cartoon', params: PD.getDefaultValues(CartoonParams) },
+            colorTheme: { name: 'polymer-id', params: PD.getDefaultValues(PolymerIdColorThemeParams) },
+            sizeTheme: { name: 'uniform', params: PD.getDefaultValues(UniformSizeThemeParams) },
+        });
     root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
-        .apply(StateTransforms.Representation.StructureRepresentation3D, { type: { name: 'ball-and-stick', params: PD.getDefaultValues(BallAndStickParams) } });
+        .apply(StateTransforms.Representation.StructureRepresentation3D, {
+            type: { name: 'ball-and-stick', params: PD.getDefaultValues(BallAndStickParams) },
+            colorTheme: { name: 'element-symbol', params: PD.getDefaultValues(ElementSymbolColorThemeParams) },
+            sizeTheme: { name: 'uniform', params: PD.getDefaultValues(UniformSizeThemeParams) },
+        });
     root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
-        .apply(StateTransforms.Representation.StructureRepresentation3D, { type: { name: 'ball-and-stick', params: { ...PD.getDefaultValues(BallAndStickParams), alpha: 0.51 } } })
+        .apply(StateTransforms.Representation.StructureRepresentation3D, {
+            type: { name: 'ball-and-stick', params: { ...PD.getDefaultValues(BallAndStickParams), alpha: 0.51 } },
+            colorTheme: { name: 'element-symbol', params: PD.getDefaultValues(ElementSymbolColorThemeParams) },
+            sizeTheme: { name: 'uniform', params: PD.getDefaultValues(UniformSizeThemeParams) },
+        })
     root.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' });
         // TODO: create spheres visual
 }
diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts
index d6c7f50ab..168481fc2 100644
--- a/src/mol-plugin/state/transforms/representation.ts
+++ b/src/mol-plugin/state/transforms/representation.ts
@@ -2,6 +2,7 @@
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { Transformer } from 'mol-state';
@@ -10,11 +11,14 @@ import { PluginStateTransform } from '../objects';
 import { PluginStateObject as SO } from '../objects';
 import { PluginContext } from 'mol-plugin/context';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { createTheme } from 'mol-theme/theme';
 
 export { StructureRepresentation3D }
 namespace StructureRepresentation3D {
     export interface Params {
-        type: { name: string, params: any /** todo is there "common type" */ },
+        type: { name: string, params: any /** TODO is there "common type" */ },
+        colorTheme: { name: string, params: any /** TODO is there "common type" */ },
+        sizeTheme: { name: string, params: any /** TODO is there "common type" */ },
     }
 }
 const StructureRepresentation3D = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, StructureRepresentation3D.Params>({
@@ -26,21 +30,37 @@ const StructureRepresentation3D = PluginStateTransform.Create<SO.Molecule.Struct
         type: PD.Mapped(
             ctx.structureRepresentation.registry.default.name,
             ctx.structureRepresentation.registry.types,
-            name => PD.Group<any>(ctx.structureRepresentation.registry.get(name).getParams(ctx.structureRepresentation.themeCtx, a.data)))
+            name => PD.Group<any>(ctx.structureRepresentation.registry.get(name).getParams(ctx.structureRepresentation.themeCtx, a.data))),
+        colorTheme: PD.Mapped(
+            // TODO how to get a default color theme dependent on the repr type?
+            ctx.structureRepresentation.themeCtx.colorThemeRegistry.default.name,
+            ctx.structureRepresentation.themeCtx.colorThemeRegistry.types,
+            name => PD.Group<any>(ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(name).getParams({ structure: a.data }))
+        ),
+        sizeTheme: PD.Mapped(
+            // TODO how to get a default size theme dependent on the repr type?
+            ctx.structureRepresentation.themeCtx.sizeThemeRegistry.default.name,
+            ctx.structureRepresentation.themeCtx.sizeThemeRegistry.types,
+            name => PD.Group<any>(ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(name).getParams({ structure: a.data }))
+        ),
     }),
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
             const provider = plugin.structureRepresentation.registry.get(params.type.name)
+            const props = params.type.params || {}
             const repr = provider.factory({ webgl: plugin.canvas3d.webgl, ...plugin.structureRepresentation.themeCtx }, provider.getParams)
-            await repr.createOrUpdate(params.type.params || {}, a.data).runInContext(ctx);
+            repr.setTheme(createTheme(plugin.structureRepresentation.themeCtx, { structure: a.data }, params))
+            // TODO set initial state, repr.setState({})
+            await repr.createOrUpdate(props, a.data).runInContext(ctx);
             return new SO.Molecule.Representation3D(repr, { label: provider.label });
         });
     },
     update({ a, b, oldParams, newParams }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
             if (newParams.type.name !== oldParams.type.name) return Transformer.UpdateResult.Recreate;
-
-            await b.data.createOrUpdate({ ...b.data.props, ...newParams.type.params }, a.data).runInContext(ctx);
+            const props = { ...b.data.props, ...newParams.type.params }
+            b.data.setTheme(createTheme(plugin.structureRepresentation.themeCtx, { structure: a.data }, newParams))
+            await b.data.createOrUpdate(props, a.data).runInContext(ctx);
             return Transformer.UpdateResult.Updated;
         });
     }
diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts
index bff58847c..b990ba87a 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 { Theme, ThemeRegistryContext } from 'mol-theme/theme';
+import { Theme, ThemeRegistryContext, createEmptyTheme } from 'mol-theme/theme';
 import { Subject } from 'rxjs';
 import { Mat4 } from 'mol-math/linear-algebra';
 
@@ -89,8 +89,10 @@ interface Representation<D, P extends PD.Params = {}> {
     readonly props: Readonly<PD.Values<P>>
     readonly params: Readonly<P>
     readonly state: Readonly<Representation.State>
+    readonly theme: Readonly<Theme>
     createOrUpdate: (props?: Partial<PD.Values<P>>, data?: D) => Task<void>
     setState: (state: Partial<Representation.State>) => void
+    setTheme: (theme: Theme) => void
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     destroy: () => void
@@ -114,9 +116,10 @@ namespace Representation {
 
     export type Any = Representation<any>
     export const Empty: Any = {
-        label: '', groupCount: 0, renderObjects: [], props: {}, params: {}, updated: new Subject(), state: createState(),
+        label: '', groupCount: 0, renderObjects: [], props: {}, params: {}, updated: new Subject(), state: createState(), theme: createEmptyTheme(),
         createOrUpdate: () => Task.constant('', undefined),
         setState: () => {},
+        setTheme: () => {},
         getLoci: () => EmptyLoci,
         mark: () => false,
         destroy: () => {}
@@ -128,6 +131,7 @@ namespace Representation {
         let version = 0
         const updated = new Subject<number>()
         const currentState = Representation.createState()
+        let currentTheme = createEmptyTheme()
 
         let currentParams: P
         let currentProps: PD.Values<P>
@@ -192,6 +196,7 @@ namespace Representation {
                 })
             },
             get state() { return currentState },
+            get theme() { return currentTheme },
             getLoci: (pickingId: PickingId) => {
                 for (let i = 0, il = reprList.length; i < il; ++i) {
                     const loci = reprList[i].getLoci(pickingId)
@@ -212,6 +217,11 @@ namespace Representation {
                 }
                 Representation.updateState(currentState, state)
             },
+            setTheme: (theme: Theme) => {
+                for (let i = 0, il = reprList.length; i < il; ++i) {
+                    reprList[i].setTheme(theme)
+                }
+            },
             destroy() {
                 for (let i = 0, il = reprList.length; i < il; ++i) {
                     reprList[i].destroy()
diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts
index a6a04a224..ef2a5076c 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 { createTheme } from 'mol-theme/theme';
+import { createEmptyTheme, Theme } from 'mol-theme/theme';
 import { Subject } from 'rxjs';
 
 export interface ShapeRepresentation<P extends ShapeParams> extends Representation<Shape, P> { }
@@ -37,6 +37,7 @@ export function ShapeRepresentation<P extends ShapeParams>(ctx: RepresentationCo
     const renderObjects: RenderObject[] = []
     let _renderObject: MeshRenderObject | undefined
     let _shape: Shape
+    let _theme = createEmptyTheme()
     let currentProps: PD.Values<P> = PD.getDefaultValues(ShapeParams) as PD.Values<P>
     let currentParams: P
     let locationIt: LocationIterator
@@ -52,10 +53,9 @@ export function ShapeRepresentation<P extends ShapeParams>(ctx: RepresentationCo
 
             const mesh = _shape.mesh
             locationIt = ShapeGroupIterator.fromShape(_shape)
-            const theme = createTheme(ctx, currentProps, {})
             const transform = createIdentityTransform()
 
-            const values = await Mesh.createValues(runtime, mesh, transform, locationIt, theme, currentProps)
+            const values = await Mesh.createValues(runtime, mesh, transform, locationIt, _theme, currentProps)
             const state = createRenderableState(currentProps)
 
             _renderObject = createMeshRenderObject(values, state)
@@ -71,6 +71,7 @@ export function ShapeRepresentation<P extends ShapeParams>(ctx: RepresentationCo
         get props () { return currentProps },
         get params () { return currentParams },
         get state() { return _state },
+        get theme() { return _theme },
         updated,
         createOrUpdate,
         getLoci(pickingId: PickingId) {
@@ -111,6 +112,9 @@ export function ShapeRepresentation<P extends ShapeParams>(ctx: RepresentationCo
 
             Representation.updateState(_state, state)
         },
+        setTheme(theme: Theme) {
+            _theme = theme
+        },
         destroy() {
             // TODO
             renderObjects.length = 0
diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts
index b069bb13f..2bbfa2955 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, Representation } from 'mol-repr/representation';
-import { Theme, createTheme } from 'mol-theme/theme';
+import { Theme, createEmptyTheme } from 'mol-theme/theme';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Subject } from 'rxjs';
 
@@ -26,7 +26,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
     let _structure: Structure
     let _params: P
     let _props: PD.Values<P>
-    let _theme: Theme
+    let _theme = createEmptyTheme()
 
     function createOrUpdate(props: Partial<PD.Values<P>> = {}, structure?: Structure) {
         if (structure && structure !== _structure) {
@@ -35,7 +35,6 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
             if (!_props) _props = PD.getDefaultValues(_params)
         }
         _props = Object.assign({}, _props, props)
-        _theme = createTheme(ctx, { structure: _structure }, props, _theme)
 
         return Task.create('Creating or updating ComplexRepresentation', async runtime => {
             if (!visual) visual = visualCtor()
@@ -59,6 +58,10 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
         Representation.updateState(_state, state)
     }
 
+    function setTheme(theme: Theme) {
+        _theme = theme
+    }
+
     function destroy() {
         if (visual) visual.destroy()
     }
@@ -74,9 +77,11 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
         get props() { return _props },
         get params() { return _params },
         get state() { return _state },
+        get theme() { return _theme },
         updated,
         createOrUpdate,
         setState,
+        setTheme,
         getLoci,
         mark,
         destroy
diff --git a/src/mol-repr/structure/representation/ball-and-stick.ts b/src/mol-repr/structure/representation/ball-and-stick.ts
index 490ae6ae5..3fcb18507 100644
--- a/src/mol-repr/structure/representation/ball-and-stick.ts
+++ b/src/mol-repr/structure/representation/ball-and-stick.ts
@@ -14,7 +14,6 @@ import { StructureRepresentation, StructureRepresentationProvider } from '../rep
 import { Representation, RepresentationParamsGetter, RepresentationContext } from 'mol-repr/representation';
 import { ThemeRegistryContext } from 'mol-theme/theme';
 import { Structure } from 'mol-model/structure';
-import { BuiltInColorThemeOptions, BuiltInColorThemes, ColorTheme } from 'mol-theme/color';
 import { UnitKind, UnitKindOptions } from '../visual/util/common';
 
 const BallAndStickVisuals = {
@@ -32,7 +31,6 @@ export const BallAndStickParams = {
     unitKinds: PD.MultiSelect<UnitKind>(['atomic'], UnitKindOptions),
     sizeFactor: PD.Numeric(0.3, { min: 0.01, max: 10, step: 0.01 }),
     sizeAspectRatio: PD.Numeric(2/3, { min: 0.01, max: 3, step: 0.01 }),
-    colorTheme: PD.Mapped('element-symbol', 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/carbohydrate.ts b/src/mol-repr/structure/representation/carbohydrate.ts
index 2d56dcc9a..2aa068163 100644
--- a/src/mol-repr/structure/representation/carbohydrate.ts
+++ b/src/mol-repr/structure/representation/carbohydrate.ts
@@ -13,7 +13,6 @@ import { StructureRepresentation, StructureRepresentationProvider } from '../rep
 import { Representation, RepresentationParamsGetter, RepresentationContext } from 'mol-repr/representation';
 import { ThemeRegistryContext } from 'mol-theme/theme';
 import { Structure } from 'mol-model/structure';
-import { BuiltInColorThemeOptions, getBuiltInColorThemeParams } from 'mol-theme/color';
 
 const CarbohydrateVisuals = {
     'carbohydrate-symbol': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, CarbohydrateSymbolParams>) => ComplexRepresentation('Carbohydrate symbol mesh', ctx, getParams, CarbohydrateSymbolVisual),
@@ -27,10 +26,8 @@ export const CarbohydrateParams = {
     ...CarbohydrateSymbolParams,
     ...CarbohydrateLinkParams,
     ...CarbohydrateTerminalLinkParams,
-    colorTheme: PD.Mapped('carbohydrate-symbol', BuiltInColorThemeOptions, getBuiltInColorThemeParams),
     visuals: PD.MultiSelect<CarbohydrateVisualName>(['carbohydrate-symbol', 'carbohydrate-link', 'carbohydrate-terminal-link'], CarbohydrateVisualOptions),
 }
-PD.getDefaultValues(CarbohydrateParams).colorTheme.name
 export type CarbohydrateParams = typeof CarbohydrateParams
 export function getCarbohydrateParams(ctx: ThemeRegistryContext, structure: Structure) {
     return PD.clone(CarbohydrateParams)
diff --git a/src/mol-repr/structure/representation/cartoon.ts b/src/mol-repr/structure/representation/cartoon.ts
index e04ce5b60..f6fbff786 100644
--- a/src/mol-repr/structure/representation/cartoon.ts
+++ b/src/mol-repr/structure/representation/cartoon.ts
@@ -14,7 +14,6 @@ import { Representation, RepresentationParamsGetter, RepresentationContext } fro
 import { PolymerDirectionVisual, PolymerDirectionParams } from '../visual/polymer-direction-wedge';
 import { Structure } from 'mol-model/structure';
 import { ThemeRegistryContext } from 'mol-theme/theme';
-import { BuiltInColorThemeOptions, getBuiltInColorThemeParams } from 'mol-theme/color';
 
 const CartoonVisuals = {
     'polymer-trace': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerTraceParams>) => UnitsRepresentation('Polymer trace mesh', ctx, getParams, PolymerTraceVisual),
@@ -31,10 +30,8 @@ export const CartoonParams = {
     ...NucleotideBlockParams,
     ...PolymerDirectionParams,
     sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
-    colorTheme: PD.Mapped('polymer-id', 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/representation/molecular-surface.ts b/src/mol-repr/structure/representation/molecular-surface.ts
index d7056a42c..4181217d3 100644
--- a/src/mol-repr/structure/representation/molecular-surface.ts
+++ b/src/mol-repr/structure/representation/molecular-surface.ts
@@ -13,7 +13,6 @@ import { StructureRepresentation, StructureRepresentationProvider } from '../rep
 import { Representation, RepresentationParamsGetter, RepresentationContext } from 'mol-repr/representation';
 import { ThemeRegistryContext } from 'mol-theme/theme';
 import { Structure } from 'mol-model/structure';
-import { BuiltInColorThemeOptions, getBuiltInColorThemeParams } from 'mol-theme/color';
 
 const MolecularSurfaceVisuals = {
     'gaussian-surface': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceParams>) => UnitsRepresentation('Gaussian surface', ctx, getParams, GaussianSurfaceVisual),
@@ -27,10 +26,8 @@ export const MolecularSurfaceParams = {
     ...GaussianSurfaceParams,
     ...GaussianWireframeParams,
     ...GaussianDensityVolumeParams,
-    colorTheme: PD.Mapped('polymer-id', BuiltInColorThemeOptions, getBuiltInColorThemeParams),
     visuals: PD.MultiSelect<MolecularSurfaceVisualName>(['gaussian-surface'], MolecularSurfaceVisualOptions),
 }
-PD.getDefaultValues(MolecularSurfaceParams).colorTheme.name
 export type MolecularSurfaceParams = typeof MolecularSurfaceParams
 export function getMolecularSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) {
     return PD.clone(MolecularSurfaceParams)
diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts
index 706af09d5..b796a22dc 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, createTheme } from 'mol-theme/theme';
+import { Theme, createEmptyTheme } from 'mol-theme/theme';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { UnitKind, UnitKindOptions } from './visual/util/common';
 import { Subject } from 'rxjs';
@@ -37,7 +37,7 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
     let _groups: ReadonlyArray<Unit.SymmetryGroup>
     let _params: P
     let _props: PD.Values<P>
-    let _theme: Theme
+    let _theme = createEmptyTheme()
 
     function createOrUpdate(props: Partial<PD.Values<P>> = {}, structure?: Structure) {
         if (structure && structure !== _structure) {
@@ -45,7 +45,6 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
             if (!_props) _props = PD.getDefaultValues(_params)
         }
         _props = Object.assign({}, _props, props)
-        _theme = createTheme(ctx, { structure: structure || _structure }, props, _theme)
 
         return Task.create('Creating or updating UnitsRepresentation', async runtime => {
             if (!_structure && !structure) {
@@ -159,6 +158,10 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
         Representation.updateState(_state, state)
     }
 
+    function setTheme(theme: Theme) {
+        _theme = theme
+    }
+
     function destroy() {
         visuals.forEach(({ visual }) => visual.destroy())
         visuals.clear()
@@ -183,9 +186,11 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
         get props() { return _props },
         get params() { return _params },
         get state() { return _state },
+        get theme() { return _theme },
         updated,
         createOrUpdate,
         setState,
+        setTheme,
         getLoci,
         mark,
         destroy
diff --git a/src/mol-repr/structure/visual/element-sphere.ts b/src/mol-repr/structure/visual/element-sphere.ts
index 50c2abc39..2b2f09c0f 100644
--- a/src/mol-repr/structure/visual/element-sphere.ts
+++ b/src/mol-repr/structure/visual/element-sphere.ts
@@ -10,11 +10,9 @@ 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 { BuiltInSizeThemeOptions, getBuiltInSizeThemeParams } from 'mol-theme/size';
 
 export const ElementSphereParams = {
     ...UnitsMeshParams,
-    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 4b33b0b3b..3e240f660 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 { Theme, createTheme } from 'mol-theme/theme';
+import { Theme, createEmptyTheme } from 'mol-theme/theme';
 import { Subject } from 'rxjs';
 
 export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
@@ -152,7 +152,7 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
     let _volume: VolumeData
     let _props: PD.Values<P>
     let _params: P
-    let _theme: Theme
+    let _theme = createEmptyTheme()
     let busy = false
 
     function createOrUpdate(props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
@@ -162,7 +162,6 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
             if (!_props) _props = PD.getDefaultValues(_params)
         }
         _props = Object.assign({}, _props, props)
-        _theme = createTheme(ctx, _props, {}, _theme)
 
         return Task.create('VolumeRepresentation.create', async runtime => {
             // TODO queue it somehow
@@ -199,6 +198,10 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
         Representation.updateState(_state, state)
     }
 
+    function setTheme(theme: Theme) {
+        _theme = theme
+    }
+
     function destroy() {
         if (visual) visual.destroy()
     }
@@ -214,9 +217,11 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
         get props () { return _props },
         get params() { return _params },
         get state() { return _state },
+        get theme() { return _theme },
         updated,
         createOrUpdate,
         setState,
+        setTheme,
         getLoci,
         mark,
         destroy
diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts
index 52d9608cf..5c34904e9 100644
--- a/src/mol-theme/color.ts
+++ b/src/mol-theme/color.ts
@@ -105,8 +105,4 @@ export const BuiltInColorThemes = {
     'shape-group': ShapeGroupColorThemeProvider,
     'unit-index': UnitIndexColorThemeProvider,
     'uniform': UniformColorThemeProvider,
-}
-export type BuiltInColorThemeName = keyof typeof BuiltInColorThemes
-export const BuiltInColorThemeNames = Object.keys(BuiltInColorThemes)
-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
+}
\ No newline at end of file
diff --git a/src/mol-theme/size.ts b/src/mol-theme/size.ts
index 22042a455..67d7e6c17 100644
--- a/src/mol-theme/size.ts
+++ b/src/mol-theme/size.ts
@@ -31,6 +31,7 @@ namespace SizeTheme {
         readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => SizeTheme<PD.Values<P>>
         readonly getParams: (ctx: ThemeDataContext) => P
     }
+    export const EmptyProvider: Provider<{}> = { label: '', factory: () => Empty, getParams: () => ({}) }
 
     export class Registry {
         private _list: { name: string, provider: Provider<any> }[] = []
@@ -53,8 +54,8 @@ namespace SizeTheme {
             this._map.set(name, provider)
         }
 
-        get(id: string) {
-            return this._map.get(id)
+        get<P extends PD.Params>(id: string) {
+            return this._map.get(id) || EmptyProvider as unknown as Provider<P>
         }
 
         create(id: string, ctx: ThemeDataContext, props = {}) {
@@ -71,8 +72,4 @@ namespace SizeTheme {
 export const BuiltInSizeThemes = {
     'physical': PhysicalSizeThemeProvider,
     'uniform': UniformSizeThemeProvider
-}
-export type BuiltInSizeThemeName = keyof typeof BuiltInSizeThemes
-export const BuiltInSizeThemeNames = Object.keys(BuiltInSizeThemes)
-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
+}
\ No newline at end of file
diff --git a/src/mol-theme/theme.ts b/src/mol-theme/theme.ts
index 4bd6aa1b0..ad2222c38 100644
--- a/src/mol-theme/theme.ts
+++ b/src/mol-theme/theme.ts
@@ -41,6 +41,6 @@ export function createTheme(ctx: ThemeRegistryContext, data: ThemeDataContext, p
     return theme
 }
 
-export function createEmptyTheme() {
+export function createEmptyTheme(): Theme {
     return { color: ColorTheme.Empty, size: SizeTheme.Empty }
 }
\ No newline at end of file
-- 
GitLab