瀏覽代碼

wip, repr/theme registry and providers

Alexander Rose 6 年之前
父節點
當前提交
b636bbc9a4
共有 85 個文件被更改,包括 1187 次插入833 次删除
  1. 8 6
      package-lock.json
  2. 1 0
      package.json
  3. 17 0
      src/apps/canvas/app.ts
  4. 4 4
      src/apps/canvas/assembly-symmetry.ts
  5. 15 18
      src/apps/canvas/component/representation.tsx
  6. 7 3
      src/apps/canvas/component/structure-view.tsx
  7. 7 3
      src/apps/canvas/component/volume-view.tsx
  8. 26 30
      src/apps/canvas/structure-view.ts
  9. 2 2
      src/apps/canvas/volume-view.ts
  10. 1 1
      src/mol-canvas3d/canvas3d.ts
  11. 2 34
      src/mol-geo/geometry/color-data.ts
  12. 2 1
      src/mol-geo/geometry/direct-volume/direct-volume.ts
  13. 1 1
      src/mol-geo/geometry/direct-volume/transfer-function.ts
  14. 7 24
      src/mol-geo/geometry/geometry.ts
  15. 2 1
      src/mol-geo/geometry/lines/lines.ts
  16. 3 2
      src/mol-geo/geometry/mesh/mesh.ts
  17. 3 2
      src/mol-geo/geometry/points/points.ts
  18. 1 18
      src/mol-geo/geometry/size-data.ts
  19. 4 1
      src/mol-gl/render-object.ts
  20. 1 5
      src/mol-gl/renderable.ts
  21. 2 2
      src/mol-model/structure/structure/unit/gaussian-density.ts
  22. 1 1
      src/mol-plugin/state/objects.ts
  23. 13 5
      src/mol-plugin/state/transforms/visuals.ts
  24. 63 28
      src/mol-repr/representation.ts
  25. 7 7
      src/mol-repr/shape/representation.ts
  26. 9 9
      src/mol-repr/structure/complex-representation.ts
  27. 16 10
      src/mol-repr/structure/complex-visual.ts
  28. 26 0
      src/mol-repr/structure/registry.ts
  29. 5 6
      src/mol-repr/structure/representation.ts
  30. 25 21
      src/mol-repr/structure/representation/backbone.ts
  31. 34 34
      src/mol-repr/structure/representation/ball-and-stick.ts
  32. 28 28
      src/mol-repr/structure/representation/carbohydrate.ts
  33. 32 18
      src/mol-repr/structure/representation/cartoon.ts
  34. 25 24
      src/mol-repr/structure/representation/distance-restraint.ts
  35. 31 29
      src/mol-repr/structure/representation/molecular-surface.ts
  36. 25 21
      src/mol-repr/structure/representation/point.ts
  37. 25 21
      src/mol-repr/structure/representation/spacefill.ts
  38. 9 10
      src/mol-repr/structure/units-representation.ts
  39. 25 19
      src/mol-repr/structure/units-visual.ts
  40. 5 5
      src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
  41. 6 6
      src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
  42. 6 6
      src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
  43. 6 6
      src/mol-repr/structure/visual/element-point.ts
  44. 5 5
      src/mol-repr/structure/visual/element-sphere.ts
  45. 6 6
      src/mol-repr/structure/visual/gaussian-density-point.ts
  46. 3 3
      src/mol-repr/structure/visual/gaussian-density-volume.ts
  47. 3 3
      src/mol-repr/structure/visual/gaussian-surface-mesh.ts
  48. 6 6
      src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
  49. 3 3
      src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
  50. 7 7
      src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
  51. 16 8
      src/mol-repr/structure/visual/nucleotide-block-mesh.ts
  52. 3 3
      src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
  53. 7 7
      src/mol-repr/structure/visual/polymer-direction-wedge.ts
  54. 11 10
      src/mol-repr/structure/visual/polymer-gap-cylinder.ts
  55. 6 5
      src/mol-repr/structure/visual/polymer-trace-mesh.ts
  56. 4 3
      src/mol-repr/structure/visual/util/common.ts
  57. 2 2
      src/mol-repr/structure/visual/util/element.ts
  58. 1 1
      src/mol-repr/structure/visual/util/link.ts
  59. 0 20
      src/mol-repr/util.ts
  60. 6 6
      src/mol-repr/volume/direct-volume.ts
  61. 6 6
      src/mol-repr/volume/isosurface-mesh.ts
  62. 25 0
      src/mol-repr/volume/registry.ts
  63. 9 5
      src/mol-repr/volume/representation.ts
  64. 82 107
      src/mol-theme/color.ts
  65. 26 5
      src/mol-theme/color/carbohydrate-symbol.ts
  66. 21 10
      src/mol-theme/color/chain-id.ts
  67. 23 9
      src/mol-theme/color/cross-link.ts
  68. 0 22
      src/mol-theme/color/custom.ts
  69. 21 6
      src/mol-theme/color/element-index.ts
  70. 15 3
      src/mol-theme/color/element-symbol.ts
  71. 15 3
      src/mol-theme/color/molecule-type.ts
  72. 21 6
      src/mol-theme/color/polymer-index.ts
  73. 15 3
      src/mol-theme/color/residue-name.ts
  74. 15 3
      src/mol-theme/color/secondary-structure.ts
  75. 21 12
      src/mol-theme/color/sequence-id.ts
  76. 17 5
      src/mol-theme/color/shape-group.ts
  77. 17 3
      src/mol-theme/color/uniform.ts
  78. 21 6
      src/mol-theme/color/unit-index.ts
  79. 57 23
      src/mol-theme/size.ts
  80. 20 8
      src/mol-theme/size/physical.ts
  81. 21 8
      src/mol-theme/size/uniform.ts
  82. 46 0
      src/mol-theme/theme.ts
  83. 29 7
      src/mol-util/color/scale.ts
  84. 10 2
      src/mol-util/param-definition.ts
  85. 7 1
      webpack.config.js

+ 8 - 6
package-lock.json

@@ -1721,6 +1721,12 @@
         "safe-buffer": "^5.0.1"
       }
     },
+    "circular-dependency-plugin": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz",
+      "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==",
+      "dev": true
+    },
     "class-utils": {
       "version": "0.3.6",
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -3390,14 +3396,12 @@
         "balanced-match": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "brace-expansion": {
           "version": "1.1.11",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -3417,8 +3421,7 @@
         "concat-map": {
           "version": "0.0.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "console-control-strings": {
           "version": "1.1.0",
@@ -3566,7 +3569,6 @@
           "version": "3.0.4",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }

+ 1 - 0
package.json

@@ -88,6 +88,7 @@
     "@types/react-dom": "^16.0.9",
     "@types/webgl2": "0.0.4",
     "benchmark": "^2.1.4",
+    "circular-dependency-plugin": "^5.0.2",
     "cpx": "^1.5.0",
     "css-loader": "^1.0.1",
     "extra-watch-webpack-plugin": "^1.0.3",

+ 17 - 0
src/apps/canvas/app.ts

@@ -12,6 +12,10 @@ import { CifBlock } from 'mol-io/reader/cif';
 import { VolumeView } from './volume-view';
 import { Ccp4File } from 'mol-io/reader/ccp4/schema';
 import { Progress } from 'mol-task';
+import { ColorTheme } from 'mol-theme/color';
+import { SizeTheme } from 'mol-theme/size';
+import { StructureRepresentationRegistry } from 'mol-repr/structure/registry';
+import { VolumeRepresentationRegistry } from 'mol-repr/volume/registry';
 
 export class App {
     canvas3d: Canvas3D
@@ -23,6 +27,11 @@ export class App {
     structureLoaded: BehaviorSubject<StructureView | null> = new BehaviorSubject<StructureView | null>(null)
     volumeLoaded: BehaviorSubject<VolumeView | null> = new BehaviorSubject<VolumeView | null>(null)
 
+    colorThemeRegistry = new ColorTheme.Registry()
+    sizeThemeRegistry = new SizeTheme.Registry()
+    structureRepresentationRegistry = new StructureRepresentationRegistry()
+    volumeRepresentationRegistry = new VolumeRepresentationRegistry()
+
     initViewer(_canvas: HTMLCanvasElement, _container: HTMLDivElement) {
         this.canvas = _canvas
         this.container = _container
@@ -64,6 +73,14 @@ export class App {
         console.log(Progress.format(progress))
     }
 
+    get reprCtx () {
+        return {
+            webgl: this.canvas3d.webgl,
+            colorThemeRegistry: this.colorThemeRegistry,
+            sizeThemeRegistry: this.sizeThemeRegistry
+        }
+    }
+
     //
 
     async loadMmcif(cif: CifBlock, assemblyId?: string) {

+ 4 - 4
src/apps/canvas/assembly-symmetry.ts

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

+ 15 - 18
src/apps/canvas/component/representation.tsx

@@ -8,16 +8,14 @@ import * as React from 'react'
 import Canvas3D from 'mol-canvas3d/canvas3d';
 import { App } from '../app';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { Representation } from 'mol-repr';
+import { Representation } from 'mol-repr/representation';
 import { ParametersComponent } from 'mol-app/component/parameters';
-import { ColorTheme } from 'mol-theme/color';
-import { getColorThemeProps } from 'mol-geo/geometry/color-data';
-import { ColorThemeComponent } from 'mol-app/component/color-theme';
 
-export interface RepresentationComponentProps {
+export interface RepresentationComponentProps<P extends PD.Params> {
     app: App
     canvas3d: Canvas3D
-    repr: Representation<PD.Params>
+    repr: Representation<P>
+    params: P
 }
 
 export interface RepresentationComponentState {
@@ -26,13 +24,13 @@ export interface RepresentationComponentState {
     reprProps: Readonly<{}>
 }
 
-export class RepresentationComponent extends React.Component<RepresentationComponentProps, RepresentationComponentState> {
+export class RepresentationComponent<P extends PD.Params> extends React.Component<RepresentationComponentProps<P>, RepresentationComponentState> {
 
-    private stateFromRepr(repr: Representation<PD.Params>) {
+    private stateFromRepr(repr: Representation<P>) {
         return {
-            label: this.props.repr.label,
-            reprParams: this.props.repr.params,
-            reprProps: this.props.repr.props
+            label: repr.label,
+            reprParams: this.props.params,
+            reprProps: repr.props
         }
     }
 
@@ -41,8 +39,7 @@ export class RepresentationComponent extends React.Component<RepresentationCompo
     }
 
     async onChange(k: string, v: any) {
-        const ctx = { webgl: this.props.canvas3d.webgl }
-        await this.props.app.runTask(this.props.repr.createOrUpdate(ctx, { [k]: v }).run(
+        await this.props.app.runTask(this.props.repr.createOrUpdate(this.props.app.reprCtx, { [k]: v }).run(
             progress => this.props.app.log(progress)
         ), 'Representation Update')
         this.props.canvas3d.add(this.props.repr)
@@ -52,10 +49,10 @@ export class RepresentationComponent extends React.Component<RepresentationCompo
 
     render() {
         const { label, reprParams, reprProps } = this.state
-        let colorTheme: ColorTheme | undefined = undefined
-        if ('colorTheme' in reprProps) {
-            colorTheme = ColorTheme(getColorThemeProps(reprProps))
-        }
+        // let colorTheme: ColorTheme | undefined = undefined
+        // if ('colorTheme' in reprProps) {
+        //     colorTheme = ColorTheme(getColorThemeProps(reprProps))
+        // }
 
         return <div>
             <div>
@@ -68,7 +65,7 @@ export class RepresentationComponent extends React.Component<RepresentationCompo
                     onChange={(k, v) => this.onChange(k as string, v)}
                 />
             </div>
-            { colorTheme !== undefined ? <ColorThemeComponent colorTheme={colorTheme} /> : '' }
+            {/* { colorTheme !== undefined ? <ColorThemeComponent colorTheme={colorTheme} /> : '' } */}
         </div>;
     }
 }

+ 7 - 3
src/apps/canvas/component/structure-view.tsx

@@ -7,8 +7,8 @@
 import * as React from 'react'
 import { StructureView } from '../structure-view';
 import { RepresentationComponent } from './representation';
-import { Representation } from 'mol-repr';
-import { StructureRepresentation } from 'mol-repr/structure/index';
+import { Representation } from 'mol-repr/representation';
+import { StructureRepresentation } from 'mol-repr/structure/representation';
 
 export interface StructureViewComponentProps {
     structureView: StructureView
@@ -37,6 +37,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
             structureView: sv,
 
             label: sv.label,
+            structure: sv.structure,
             modelId: sv.modelId,
             modelIds: sv.getModelIds(),
             assemblyId: sv.assemblyId,
@@ -84,7 +85,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
     }
 
     render() {
-        const { structureView, label, modelIds, assemblyIds, symmetryFeatureIds, active, structureRepresentations } = this.state
+        const { structureView, label, structure, modelIds, assemblyIds, symmetryFeatureIds, active, structureRepresentations } = this.state
 
         const modelIdOptions = modelIds.map(m => {
             return <option key={m.id} value={m.id}>{m.label}</option>
@@ -174,6 +175,9 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
                             return <div key={i}>
                                 <RepresentationComponent
                                     repr={structureRepresentations[k] as Representation<any>}
+                                    params={
+                                        structureView.app.structureRepresentationRegistry.get(k)!.params(structureView.app.reprCtx, structure!)
+                                    }
                                     canvas3d={structureView.canvas3d}
                                     app={structureView.app}
                                 />

+ 7 - 3
src/apps/canvas/component/volume-view.tsx

@@ -6,9 +6,9 @@
 
 import * as React from 'react'
 import { RepresentationComponent } from './representation';
-import { Representation } from 'mol-repr';
+import { Representation } from 'mol-repr/representation';
 import { VolumeView } from '../volume-view';
-import { VolumeRepresentation } from 'mol-repr/volume/index';
+import { VolumeRepresentation } from 'mol-repr/volume/representation';
 
 export interface VolumeViewComponentProps {
     volumeView: VolumeView
@@ -28,6 +28,7 @@ export class VolumeViewComponent extends React.Component<VolumeViewComponentProp
         return {
             volumeView: vv,
             label: vv.label,
+            volume: vv.volume,
             active: vv.active,
             volumeRepresentations: vv.volumeRepresentations
         }
@@ -61,7 +62,7 @@ export class VolumeViewComponent extends React.Component<VolumeViewComponentProp
     // }
 
     render() {
-        const { volumeView, label, active, volumeRepresentations } = this.state
+        const { volumeView, label, volume, active, volumeRepresentations } = this.state
 
         return <div>
             <div>
@@ -89,6 +90,9 @@ export class VolumeViewComponent extends React.Component<VolumeViewComponentProp
                             return <div key={i}>
                                 <RepresentationComponent
                                     repr={volumeRepresentations[k] as Representation<any>}
+                                    params={
+                                        volumeView.app.volumeRepresentationRegistry.get(k)!.params(volumeView.app.reprCtx, volume!)
+                                    }
                                     canvas3d={volumeView.viewer}
                                     app={volumeView.app}
                                 />

+ 26 - 30
src/apps/canvas/structure-view.ts

@@ -17,15 +17,8 @@ import Canvas3D from 'mol-canvas3d/canvas3d';
 // import { addBoundingBox } from 'mol-geo/mesh/builder/bounding-box';
 import { BehaviorSubject } from 'rxjs';
 import { App } from './app';
-import { StructureRepresentation } from 'mol-repr/structure/index';
-import { ShapeRepresentation, ShapeProps } from 'mol-repr/shape/index';
-import { CartoonRepresentation } from 'mol-repr/structure/representation/cartoon';
-import { MolecularSurfaceRepresentation } from 'mol-repr/structure/representation/molecular-surface';
-import { PointRepresentation } from 'mol-repr/structure/representation/point';
-import { BallAndStickRepresentation } from 'mol-repr/structure/representation/ball-and-stick';
-import { CarbohydrateRepresentation } from 'mol-repr/structure/representation/carbohydrate';
-import { SpacefillRepresentation } from 'mol-repr/structure/representation/spacefill';
-import { DistanceRestraintRepresentation } from 'mol-repr/structure/representation/distance-restraint';
+import { StructureRepresentation } from 'mol-repr/structure/representation';
+import { ShapeRepresentation, ShapeProps } from 'mol-repr/shape/representation';
 
 export interface StructureView {
     readonly app: App
@@ -66,25 +59,17 @@ interface StructureViewProps {
 export async function StructureView(app: App, canvas3d: Canvas3D, models: ReadonlyArray<Model>, props: StructureViewProps = {}): Promise<StructureView> {
     const active: { [k: string]: boolean } = {
         cartoon: true,
-        point: false,
-        surface: false,
-        ballAndStick: false,
-        carbohydrate: false,
-        spacefill: false,
-        distanceRestraint: false,
-        symmetryAxes: true,
+        // point: false,
+        // surface: false,
+        // ballAndStick: false,
+        // carbohydrate: false,
+        // spacefill: false,
+        // distanceRestraint: false,
+        // symmetryAxes: true,
         // polymerSphere: false,
     }
 
-    const structureRepresentations: { [k: string]: StructureRepresentation<any> } = {
-        cartoon: CartoonRepresentation(),
-        surface: MolecularSurfaceRepresentation(),
-        point: PointRepresentation(),
-        ballAndStick: BallAndStickRepresentation(),
-        carbohydrate: CarbohydrateRepresentation(),
-        spacefill: SpacefillRepresentation(),
-        distanceRestraint: DistanceRestraintRepresentation(),
-    }
+    const structureRepresentations: { [k: string]: StructureRepresentation<any> } = {}
 
     const symmetryAxes = ShapeRepresentation()
     const polymerSphere = ShapeRepresentation()
@@ -206,14 +191,25 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon
     async function createStructureRepr() {
         if (structure) {
             console.log('createStructureRepr')
-            for (const k in structureRepresentations) {
+            for (const k in active) {
                 if (active[k]) {
-                    await app.runTask(structureRepresentations[k].createOrUpdate({ webgl: canvas3d.webgl }, {}, structure).run(
+                    let repr: StructureRepresentation
+                    if (structureRepresentations[k]) {
+                        repr = structureRepresentations[k]
+                    } else {
+                        repr = app.structureRepresentationRegistry.create('cartoon', app.reprCtx, structure)
+                        structureRepresentations[k] = repr
+                    }
+                    await app.runTask(repr.createOrUpdate(app.reprCtx, {}, {}, structure).run(
                         progress => app.log(progress)
                     ), 'Create/update representation')
-                    canvas3d.add(structureRepresentations[k])
+                    canvas3d.add(repr)
                 } else {
-                    canvas3d.remove(structureRepresentations[k])
+                    if (structureRepresentations[k]) {
+                        canvas3d.remove(structureRepresentations[k])
+                        structureRepresentations[k].destroy()
+                        delete structureRepresentations[k]
+                    }
                 }
             }
 
@@ -264,7 +260,7 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon
                     //     colorFunction: colorTheme.color,
                     //     colorGranularity: colorTheme.granularity,
                     // }).run()
-                    await symmetryAxes.createOrUpdate({ webgl: canvas3d.webgl }, {}, axesShape).run()
+                    await symmetryAxes.createOrUpdate(app.reprCtx, {}, {}, axesShape).run()
                     canvas3d.add(symmetryAxes)
                 } else {
                     canvas3d.remove(symmetryAxes)

+ 2 - 2
src/apps/canvas/volume-view.ts

@@ -8,7 +8,7 @@ import Canvas3D from 'mol-canvas3d/canvas3d';
 import { BehaviorSubject } from 'rxjs';
 import { App } from './app';
 import { VolumeData } from 'mol-model/volume';
-import { VolumeRepresentation } from 'mol-repr/volume/index';
+import { VolumeRepresentation } from 'mol-repr/volume/representation';
 import { IsosurfaceRepresentation } from 'mol-repr/volume/isosurface-mesh';
 import { DirectVolumeRepresentation } from 'mol-repr/volume/direct-volume';
 
@@ -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({ webgl: viewer.webgl }, {}, volume).run(
+                await app.runTask(volumeRepresentations[k].createOrUpdate(app.reprCtx, {}, {}, volume).run(
                     progress => app.log(progress)
                 ), 'Create/update representation')
                 viewer.add(volumeRepresentations[k])

+ 1 - 1
src/mol-canvas3d/canvas3d.ts

@@ -16,7 +16,7 @@ import TrackballControls from './controls/trackball'
 import { Viewport } from './camera/util'
 import { resizeCanvas } from './util';
 import { createContext, getGLContext, WebGLContext } from 'mol-gl/webgl/context';
-import { Representation } from 'mol-repr';
+import { Representation } from 'mol-repr/representation';
 import { createRenderTarget } from 'mol-gl/webgl/render-target';
 import Scene from 'mol-gl/scene';
 import { RenderVariant } from 'mol-gl/webgl/render-item';

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

@@ -6,14 +6,13 @@
 
 import { ValueCell } from 'mol-util';
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
-import { Color, ColorMap } from 'mol-util/color';
+import { Color } from 'mol-util/color';
 import { Vec2, Vec3 } from 'mol-math/linear-algebra';
 import { LocationIterator } from '../util/location-iterator';
 import { NullLocation } from 'mol-model/location';
-import { LocationColor, ColorThemeProps, ColorTheme, ColorThemeName, ScaleLegend, TableLegend, ColorScaleName, getColorScaleFromName } from 'mol-theme/color';
+import { LocationColor, ColorTheme } from 'mol-theme/color';
 import { RuntimeContext } from 'mol-task';
 import { getGranularity } from './geometry';
-import { Structure } from 'mol-model/structure';
 
 export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 
@@ -25,37 +24,6 @@ export type ColorData = {
     dColorType: ValueCell<string>,
 }
 
-export interface ColorProps {
-    colorTheme: ColorThemeName
-    colorList?: Color[] | ColorScaleName
-    colorMap?: ColorMap<any>
-    colorDomain?: [number, number]
-    colorValue?: Color
-    colorFunction?: LocationColor,
-    colorGranularity?: ColorType,
-    colorDescription?: string,
-    colorLegend?: ScaleLegend | TableLegend
-    structure?: Structure
-}
-
-export function getColorThemeProps(props: ColorProps): ColorThemeProps {
-    const p: ColorThemeProps = {
-        name: props.colorTheme
-    }
-    if (props.colorDomain !== undefined) p.domain = props.colorDomain
-    if (props.colorList !== undefined) {
-        p.list = typeof props.colorList === 'string' ? getColorScaleFromName(props.colorList) : props.colorList
-    }
-    if (props.colorMap !== undefined) p.map = props.colorMap
-    if (props.colorValue !== undefined) p.value = props.colorValue
-    if (props.structure !== undefined) p.structure = props.structure
-    if (props.colorFunction !== undefined) p.color = props.colorFunction
-    if (props.colorGranularity !== undefined) p.granularity = props.colorGranularity
-    if (props.colorDescription !== undefined) p.description = props.colorDescription
-    if (props.colorLegend !== undefined) p.legend = props.colorLegend
-    return p
-}
-
 export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, colorTheme: ColorTheme, colorData?: ColorData): Promise<ColorData> {
     switch (getGranularity(locationIt, colorTheme.granularity)) {
         case 'uniform': return createUniformColor(ctx, locationIt, colorTheme.color, colorData)

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

@@ -17,9 +17,10 @@ import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { TransformData } from '../transform-data';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
-import { Geometry, Theme } from '../geometry';
+import { Geometry } from '../geometry';
 import { transformPositionArray } from 'mol-geo/util';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
+import { Theme } from 'mol-theme/theme';
 
 const VolumeBox = Box()
 const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]

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

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

+ 7 - 24
src/mol-geo/geometry/geometry.ts

@@ -9,15 +9,14 @@ import { Points } from './points/points';
 import { RenderableState } from 'mol-gl/renderable';
 import { ValueCell } from 'mol-util';
 import { BaseValues } from 'mol-gl/renderable/schema';
-import { Color } from 'mol-util/color';
-import { ColorThemeOptions, ColorThemeName, ColorScaleOptions, ColorScaleName, ColorTheme } from 'mol-theme/color';
 import { LocationIterator } from '../util/location-iterator';
-import { ColorType, getColorThemeProps } from './color-data';
-import { SizeType, getSizeThemeProps } from './size-data';
+import { ColorType } from './color-data';
+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 { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
+import { BuiltInSizeThemeOptions, BuiltInSizeThemeName } from 'mol-theme/size';
+import { BuiltInColorThemeName, BuiltInColorThemeOptions } from 'mol-theme/color';
 
 //
 
@@ -36,18 +35,6 @@ export type VisualQuality = keyof typeof VisualQualityInfo
 export const VisualQualityNames = Object.keys(VisualQualityInfo)
 export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [VisualQuality, string])
 
-export interface Theme {
-    color: ColorTheme
-    size: SizeTheme
-}
-
-export function createTheme(props: Geometry.Props) {
-    return {
-        color: ColorTheme(getColorThemeProps(props)),
-        size: SizeTheme(getSizeThemeProps(props))
-    }
-}
-
 //
 
 export type GeometryKindType = {
@@ -76,15 +63,11 @@ export namespace Geometry {
         visible: PD.Boolean('Visible', '', true),
         depthMask: PD.Boolean('Depth Mask', '', true),
         useFog: PD.Boolean('Use Fog', '', false),
-        quality: PD.Select<VisualQuality>('Quality', '', 'auto', VisualQualityOptions),
 
-        colorTheme: PD.Select<ColorThemeName>('Color Name', '', 'uniform', ColorThemeOptions),
-        colorList: PD.Select<ColorScaleName>('Color Scale', '', 'default', ColorScaleOptions),
-        colorValue: PD.Color('Color Value', '', Color(0xCCCCCC)),
+        quality: PD.Select<VisualQuality>('Quality', '', 'auto', VisualQualityOptions),
 
-        sizeTheme: PD.Select<SizeThemeName>('Size Name', '', 'uniform', SizeThemeOptions),
-        sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
-        sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1),
+        colorTheme: PD.Select<BuiltInColorThemeName>('Color Name', '', 'uniform', BuiltInColorThemeOptions),
+        sizeTheme: PD.Select<BuiltInSizeThemeName>('Size Name', '', 'uniform', BuiltInSizeThemeOptions),
     }
     export const DefaultProps = PD.getDefaultValues(Params)
     export type Props = typeof DefaultProps

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

@@ -7,7 +7,7 @@
 import { ValueCell } from 'mol-util'
 import { Mat4 } from 'mol-math/linear-algebra'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
-import { Geometry, Theme } from '../geometry';
+import { Geometry } from '../geometry';
 import { RuntimeContext } from 'mol-task';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
@@ -20,6 +20,7 @@ import { LinesBuilder } from './lines-builder';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
 import { Sphere3D } from 'mol-math/geometry';
+import { Theme } from 'mol-theme/theme';
 
 /** Wide line */
 export interface Lines {

+ 3 - 2
src/mol-geo/geometry/mesh/mesh.ts

@@ -9,8 +9,7 @@ import { ValueCell } from 'mol-util'
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { Sphere3D } from 'mol-math/geometry'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
-import { MeshValues } from 'mol-gl/renderable';
-import { Geometry, Theme } from '../geometry';
+import { Geometry } from '../geometry';
 import { createMarkers } from '../marker-data';
 import { TransformData } from '../transform-data';
 import { LocationIterator } from '../../util/location-iterator';
@@ -18,6 +17,8 @@ import { createColors } from '../color-data';
 import { ChunkedArray } from 'mol-data/util';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
+import { Theme } from 'mol-theme/theme';
+import { MeshValues } from 'mol-gl/renderable/mesh';
 
 export interface Mesh {
     readonly kind: 'mesh',

+ 3 - 2
src/mol-geo/geometry/points/points.ts

@@ -7,8 +7,7 @@
 import { ValueCell } from 'mol-util'
 import { Mat4 } from 'mol-math/linear-algebra'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
-import { Geometry, Theme } from '../geometry';
-import { PointsValues } from 'mol-gl/renderable';
+import { Geometry } from '../geometry';
 import { RuntimeContext } from 'mol-task';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
@@ -18,6 +17,8 @@ import { LocationIterator } from '../../util/location-iterator';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
 import { Sphere3D } from 'mol-math/geometry';
+import { Theme } from 'mol-theme/theme';
+import { PointsValues } from 'mol-gl/renderable/points';
 
 /** Point cloud */
 export interface Points {

+ 1 - 18
src/mol-geo/geometry/size-data.ts

@@ -10,9 +10,8 @@ import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 import { LocationIterator } from '../util/location-iterator';
 import { Location, NullLocation } from 'mol-model/location';
 import { RuntimeContext } from 'mol-task';
-import { SizeThemeProps, SizeTheme, SizeThemeName } from 'mol-theme/size';
+import { SizeTheme } from 'mol-theme/size';
 import { getGranularity } from './geometry';
-import { Structure } from 'mol-model/structure';
 
 export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 
@@ -24,22 +23,6 @@ export type SizeData = {
     dSizeType: ValueCell<string>,
 }
 
-export interface SizeProps {
-    sizeTheme: SizeThemeName
-    sizeValue?: number
-    sizeFactor?: number
-    structure?: Structure
-}
-
-export function getSizeThemeProps(props: SizeProps): SizeThemeProps {
-    return {
-        name: props.sizeTheme,
-        value: props.sizeValue,
-        factor: props.sizeFactor,
-        structure: props.structure,
-    }
-}
-
 export async function createSizes(ctx: RuntimeContext, locationIt: LocationIterator, sizeTheme: SizeTheme, sizeData?: SizeData): Promise<SizeData> {
     switch (getGranularity(locationIt, sizeTheme.granularity)) {
         case 'uniform': return createUniformSize(ctx, locationIt, sizeTheme.size, sizeData)

+ 4 - 1
src/mol-gl/render-object.ts

@@ -4,12 +4,15 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { PointsRenderable, MeshRenderable, RenderableState, MeshValues, PointsValues, LinesValues, LinesRenderable, Renderable } from './renderable'
+import { RenderableState, Renderable } from './renderable'
 import { RenderableValues } from './renderable/schema';
 import { idFactory } from 'mol-util/id-factory';
 import { WebGLContext } from './webgl/context';
 import { GaussianDensityValues, GaussianDensityRenderable } from './renderable/gaussian-density';
 import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume';
+import { MeshValues, MeshRenderable } from './renderable/mesh';
+import { PointsValues, PointsRenderable } from './renderable/points';
+import { LinesValues, LinesRenderable } from './renderable/lines';
 
 const getNextId = idFactory(0, 0x7FFFFFFF)
 

+ 1 - 5
src/mol-gl/renderable.ts

@@ -46,8 +46,4 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
         update: () => renderItem.update(),
         dispose: () => renderItem.destroy()
     }
-}
-
-export { MeshRenderable, MeshSchema, MeshValues } from './renderable/mesh'
-export { PointsRenderable, PointsSchema, PointsValues } from './renderable/points'
-export { LinesRenderable, LinesSchema, LinesValues } from './renderable/lines'
+}

+ 2 - 2
src/mol-model/structure/structure/unit/gaussian-density.ts

@@ -5,7 +5,6 @@
  */
 
 import { Unit, StructureElement, ElementIndex } from 'mol-model/structure';
-import { SizeTheme } from 'mol-theme/size';
 import { GaussianDensity } from 'mol-math/geometry/gaussian-density';
 import { Task, RuntimeContext } from 'mol-task';
 import { DensityData } from 'mol-math/geometry';
@@ -13,6 +12,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { GaussianDensityTexture } from 'mol-math/geometry/gaussian-density/gpu';
 import { Texture } from 'mol-gl/webgl/texture';
 import { WebGLContext } from 'mol-gl/webgl/context';
+import { PhysicalSizeTheme } from 'mol-theme/size/physical';
 
 export const GaussianDensityParams = {
     resolution: PD.Numeric('Resolution', '', 1, 0.1, 10, 0.1),
@@ -43,7 +43,7 @@ function getConformationAndRadius(unit: Unit) {
     }
 
     const l = StructureElement.create(unit)
-    const sizeTheme = SizeTheme({ name: 'physical' })
+    const sizeTheme = PhysicalSizeTheme({}, {})
     const radius = (index: number) => {
         l.element = index as ElementIndex
         return sizeTheme.size(l)

+ 1 - 1
src/mol-plugin/state/objects.ts

@@ -7,7 +7,7 @@
 import { PluginStateObject } from './base';
 import { CifFile } from 'mol-io/reader/cif';
 import { Model as _Model, Structure as _Structure } from 'mol-model/structure'
-import { StructureRepresentation } from 'mol-repr/structure/index';
+import { StructureRepresentation } from 'mol-repr/structure/representation';
 
 const _create = PluginStateObject.Create
 

+ 13 - 5
src/mol-plugin/state/transforms/visuals.ts

@@ -8,9 +8,16 @@ import { Transformer } from 'mol-state';
 import { Task } from 'mol-task';
 import { PluginStateTransform } from '../base';
 import { PluginStateObjects as SO } from '../objects';
-//import { CartoonRepresentation, DefaultCartoonProps } from 'mol-repr/structure/representation/cartoon';
-import { BallAndStickRepresentation, DefaultBallAndStickProps } from 'mol-repr/structure/representation/ball-and-stick';
+// import { CartoonRepresentation, DefaultCartoonProps } from 'mol-repr/structure/representation/cartoon';
+// import { BallAndStickRepresentation } from 'mol-repr/structure/representation/ball-and-stick';
 import { PluginContext } from 'mol-plugin/context';
+import { ColorTheme } from 'mol-theme/color';
+import { SizeTheme } from 'mol-theme/size';
+import { RepresentationRegistry } from 'mol-repr/representation';
+
+const colorThemeRegistry = new ColorTheme.Registry()
+const sizeThemeRegistry = new SizeTheme.Registry()
+const representationRegistry = new RepresentationRegistry()
 
 export { CreateStructureRepresentation }
 namespace CreateStructureRepresentation { export interface Params { } }
@@ -21,14 +28,15 @@ const CreateStructureRepresentation = PluginStateTransform.Create<SO.Structure,
     to: [SO.StructureRepresentation3D],
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
-            const repr = BallAndStickRepresentation(); // CartoonRepresentation();
-            await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl }, DefaultBallAndStickProps, a.data).runInContext(ctx);
+            const repr = representationRegistry.create('cartoon', { colorThemeRegistry, sizeThemeRegistry }, a.data)
+            // const repr = BallAndStickRepresentation(); // CartoonRepresentation();
+            await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl, colorThemeRegistry, sizeThemeRegistry }, {}, {}, a.data).runInContext(ctx);
             return new SO.StructureRepresentation3D({ label: 'Visual Repr.' }, repr);
         });
     },
     update({ a, b }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
-            await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl }, b.data.props, a.data).runInContext(ctx);
+            await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl, colorThemeRegistry, sizeThemeRegistry }, b.data.props, {}, a.data).runInContext(ctx);
             return Transformer.UpdateResult.Updated;
         });
     }

+ 63 - 28
src/mol-repr/index.ts → src/mol-repr/representation.ts

@@ -12,61 +12,95 @@ import { MarkerAction } from '../mol-geo/geometry/marker-data';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { WebGLContext } from 'mol-gl/webgl/context';
 import { getQualityProps } from './util';
-import { Theme } from 'mol-geo/geometry/geometry';
-// import { ColorTheme } from 'mol-theme/color';
+import { ColorTheme } from 'mol-theme/color';
+import { SizeTheme } from 'mol-theme/size';
+import { ThemeProps, Theme, ThemeRegistryContext } from 'mol-theme/theme';
 
 // export interface RepresentationProps {
 //     visuals?: string[]
 // }
 export type RepresentationProps = { [k: string]: any }
 
+//
+
+export interface RepresentationProvider<D, P extends PD.Params> {
+    readonly factory: (defaultProps: PD.DefaultValues<P>) => Representation<D, PD.DefaultValues<P>>
+    readonly params: (ctx: ThemeRegistryContext, data: D) => P
+}
+
+export class RepresentationRegistry<D> {
+    private _list: { name: string, provider: RepresentationProvider<D, any> }[] = []
+    private _map = new Map<string, RepresentationProvider<D, any>>()
+
+    constructor() {};
+
+    add<P extends PD.Params>(name: string, factory: RepresentationProvider<D, P>['factory'], params: RepresentationProvider<D, P>['params']) {
+        const provider = { factory, params } as RepresentationProvider<D, P>
+        this._list.push({ name, provider })
+        this._map.set(name, provider)
+    }
+
+    get(id: string) {
+        return this._map.get(id)
+    }
+
+    create(id: string, ctx: ThemeRegistryContext, data: D, props = {}): Representation<D, any> {
+        const provider = this.get(id)
+        return provider ? provider.factory({ ...PD.getDefaultValues(provider.params(ctx, data)), ...props }) : Representation.Empty
+    }
+
+    get list() {
+        return this._list
+    }
+}
+
+//
+
 export interface RepresentationContext {
     webgl?: WebGLContext
+    colorThemeRegistry: ColorTheme.Registry
+    sizeThemeRegistry: SizeTheme.Registry
 }
 
-export interface Representation<D, P extends RepresentationProps = {}> {
+export { Representation }
+interface Representation<D, P extends RepresentationProps = {}> {
     readonly label: string
-    readonly params: PD.Params
     readonly renderObjects: ReadonlyArray<RenderObject>
     readonly props: Readonly<P>
-    createOrUpdate: (ctx: RepresentationContext, props?: Partial<P>, data?: D) => Task<void>
+    createOrUpdate: (ctx: RepresentationContext, props?: Partial<P>, themeProps?: ThemeProps, data?: D) => Task<void>
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     destroy: () => void
 }
-
-export namespace Representation {
+namespace Representation {
     export type Any = Representation<any>
-    export const Empty: Representation<undefined> = {
-        label: '', params: {}, renderObjects: [], props: {},
+    export const Empty: Representation<any> = {
+        label: '', renderObjects: [], props: {},
         createOrUpdate: () => Task.constant('', undefined),
         getLoci: () => EmptyLoci,
         mark: () => false,
         destroy: () => {}
     }
 
-    export function createMulti<D, P extends RepresentationProps = {}>(label: string, params: PD.Params, defaultProps: P, reprList: Representation<D, P>[]): Representation<D, P> {
-        let currentProps: P
+    export type Def<P extends RepresentationProps = {}> = { [k: string]: (defaultProps: P) => Representation<any, P> }
+
+    export function createMulti<D, P extends RepresentationProps = {}>(label: string, defaultProps: P, reprDefs: Def<P>): Representation<D, P> {
+        let currentProps: P = Object.assign({}, defaultProps)
         let currentData: D
 
-        const visualsOptions: [string, string][] = []
-        for (let i = 0, il = reprList.length; i < il; ++i) {
-            visualsOptions.push([ i.toString(), reprList[i].label ])
-        }
-        params['visuals'] = PD.MultiSelect<string>('Visuals', '', ['surface'], visualsOptions)
-
-        if (!defaultProps.visuals) {
-            defaultProps.visuals = reprList.map((r, i) => i.toString())
-        }
+        const reprMap: { [k: number]: string } = {}
+        const reprList: Representation<D, P>[] = Object.keys(reprDefs).map((name, i) => {
+            reprMap[i] = name
+            return reprDefs[name](defaultProps)
+        })
 
         return {
             label,
-            params,
             get renderObjects() {
                 const { visuals } = currentProps
                 const renderObjects: RenderObject[] = []
                 for (let i = 0, il = reprList.length; i < il; ++i) {
-                    if (!visuals || visuals.includes(i.toString())) {
+                    if (!visuals || visuals.includes(reprMap[i])) {
                         renderObjects.push(...reprList[i].renderObjects)
                     }
                 }
@@ -77,16 +111,16 @@ export namespace Representation {
                 reprList.forEach(r => Object.assign(props, r.props))
                 return props as P
             },
-            createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, data?: D) => {
+            createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, data?: D) => {
                 if (data) currentData = data
-                const qualityProps = getQualityProps(Object.assign({}, currentProps, props), data)
-                currentProps = Object.assign({}, defaultProps, currentProps, props, qualityProps)
+                const qualityProps = getQualityProps(Object.assign({}, currentProps, props), currentData)
+                Object.assign(currentProps, props, qualityProps)
 
                 const { visuals } = currentProps
                 return Task.create(`Creating '${label}' representation`, async runtime => {
                     for (let i = 0, il = reprList.length; i < il; ++i) {
-                        if (!visuals || visuals.includes(i.toString())) {
-                            await reprList[i].createOrUpdate(ctx, currentProps, currentData).runInContext(runtime)
+                        if (!visuals || visuals.includes(reprMap[i])) {
+                            await reprList[i].createOrUpdate(ctx, currentProps, themeProps, currentData).runInContext(runtime)
                         }
                     }
                 })
@@ -116,7 +150,8 @@ export namespace Representation {
 
 //
 
-export interface VisualContext extends RepresentationContext {
+export interface VisualContext {
+    webgl?: WebGLContext
     runtime: RuntimeContext,
 }
 

+ 7 - 7
src/mol-repr/shape/index.ts → src/mol-repr/shape/representation.ts

@@ -6,25 +6,26 @@
 
 import { Task } from 'mol-task'
 import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
-import { RepresentationProps, Representation, RepresentationContext } from '..';
+import { RepresentationProps, Representation, RepresentationContext } from '../representation';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
 import { ValueCell } from 'mol-util';
-import { ColorThemeName, ColorThemeOptions } from 'mol-theme/color';
 import { Shape } from 'mol-model/shape';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { createRenderableState, createTheme } from 'mol-geo/geometry/geometry';
+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';
 
 export interface ShapeRepresentation<P extends RepresentationProps = {}> extends Representation<Shape, P> { }
 
 export const ShapeParams = {
     ...Mesh.Params,
-    colorTheme: PD.Select<ColorThemeName>('Color Theme', '', 'shape-group', ColorThemeOptions)
+    // TODO
+    // colorTheme: PD.Select<ColorThemeName>('Color Theme', '', 'shape-group', ColorThemeOptions)
 }
 export const DefaultShapeProps = PD.getDefaultValues(ShapeParams)
 export type ShapeProps = typeof DefaultShapeProps
@@ -38,7 +39,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
     let _shape: Shape
     let currentProps: P
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, shape?: Shape) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, shape?: Shape) {
         currentProps = Object.assign({}, DefaultShapeProps, currentProps, props)
         if (shape) _shape = shape
 
@@ -49,7 +50,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
 
             const mesh = _shape.mesh
             const locationIt = ShapeGroupIterator.fromShape(_shape)
-            const theme = createTheme(currentProps)
+            const theme = createTheme(ctx, currentProps, themeProps, {})
             const transform = createIdentityTransform()
 
             const values = await Mesh.createValues(runtime, mesh, transform, locationIt, theme, currentProps)
@@ -62,7 +63,6 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
 
     return {
         label: 'Shape mesh',
-        params: ShapeParams,
         get renderObjects () { return renderObjects },
         get props () { return currentProps },
         createOrUpdate,

+ 9 - 9
src/mol-repr/structure/complex-representation.ts

@@ -8,23 +8,24 @@
 import { Structure } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { StructureProps, StructureRepresentation, StructureParams } from './index';
+import { StructureProps, StructureRepresentation } from './representation';
 import { ComplexVisual } from './complex-visual';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
-import { RepresentationContext } from 'mol-repr';
-import { createTheme, Theme } from 'mol-geo/geometry/geometry';
+import { RepresentationContext } from 'mol-repr/representation';
+import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
 
-export function ComplexRepresentation<P extends StructureProps>(label: string, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> {
+export function ComplexRepresentation<P extends StructureProps>(label: string, defaultProps: P, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> {
     let visual: ComplexVisual<P> | undefined
+
     let _structure: Structure
-    let _props: P
+    let _props: P = Object.assign({}, defaultProps)
     let _theme: Theme
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, structure?: Structure) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
         if (structure) _structure = structure
-        _props = Object.assign({}, _props, props, { structure: _structure })
-        _theme = createTheme(_props)
+        _props = Object.assign({}, _props, props)
+        _theme = createTheme(ctx, { structure: _structure }, props, themeProps, _theme)
 
         return Task.create('Creating or updating ComplexRepresentation', async runtime => {
             if (!visual) visual = visualCtor()
@@ -46,7 +47,6 @@ export function ComplexRepresentation<P extends StructureProps>(label: string, v
 
     return {
         label,
-        params: StructureParams, // TODO
         get renderObjects() {
             return visual && visual.renderObject ? [ visual.renderObject ] : []
         },

+ 16 - 10
src/mol-repr/structure/complex-visual.ts

@@ -5,23 +5,26 @@
  */
 
 import { Structure } from 'mol-model/structure';
-import { Visual, VisualContext } from '..';
+import { Visual, VisualContext } from '../representation';
 import { MeshRenderObject, LinesRenderObject, PointsRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
 import { createComplexMeshRenderObject, UnitKind, UnitKindOptions } from './visual/util/common';
-import { StructureProps, StructureMeshParams, StructureParams } from './index';
+import { StructureProps, StructureMeshParams, StructureParams } from './representation';
 import { deepEqual, ValueCell } from 'mol-util';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { Interval } from 'mol-data/int';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { RenderableValues } from 'mol-gl/renderable/schema';
 import { createSizes } from 'mol-geo/geometry/size-data';
-import { Geometry, updateRenderableState, Theme } from 'mol-geo/geometry/geometry';
+import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { createColors } from 'mol-geo/geometry/color-data';
 import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
-import { VisualUpdateState, colorChanged, sizeChanged } from 'mol-repr/util';
+import { VisualUpdateState } from 'mol-repr/util';
+import { Theme } from 'mol-theme/theme';
+import { ColorTheme } from 'mol-theme/color';
+import { SizeTheme } from 'mol-theme/size';
 
 export interface  ComplexVisual<P extends StructureProps> extends Visual<Structure, P> { }
 
@@ -40,7 +43,7 @@ interface ComplexVisualBuilder<P extends ComplexProps, G extends Geometry> {
     createLocationIterator(structure: Structure): LocationIterator
     getLoci(pickingId: PickingId, structure: Structure, id: number): Loci
     mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean,
-    setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void
+    setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P, newTheme: Theme, currentTheme: Theme): void
 }
 
 interface ComplexVisualGeometryBuilder<P extends ComplexProps, G extends Geometry> extends ComplexVisualBuilder<P, G> {
@@ -56,6 +59,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
 
     let renderObject: ComplexRenderObject | undefined
     let currentProps: P
+    let currentTheme: Theme
     let geometry: Geometry
     let currentStructure: Structure
     let locationIt: LocationIterator
@@ -63,6 +67,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
 
     async function create(ctx: VisualContext, structure: Structure, theme: Theme, props: Partial<P> = {}) {
         currentProps = Object.assign({}, defaultProps, props)
+        currentTheme = theme
         currentStructure = structure
 
         conformationHash = Structure.conformationHash(currentStructure)
@@ -79,7 +84,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
 
         locationIt.reset()
         VisualUpdateState.reset(updateState)
-        setUpdateState(updateState, newProps, currentProps)
+        setUpdateState(updateState, newProps, currentProps, theme, currentTheme)
 
         const newConformationHash = Structure.conformationHash(currentStructure)
         if (newConformationHash !== conformationHash) {
@@ -87,7 +92,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
             updateState.createGeometry = true
         }
 
-        if (colorChanged(currentProps, newProps)) updateState.updateColor = true
+        if (ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true
         if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
 
         //
@@ -113,6 +118,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
         updateRenderableState(renderObject.state, newProps)
 
         currentProps = newProps
+        currentTheme = theme
         return true
     }
 
@@ -178,9 +184,9 @@ export interface ComplexMeshVisualBuilder<P extends ComplexMeshProps> extends Co
 export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> {
     return ComplexVisual({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
-            builder.setUpdateState(state, newProps, currentProps)
-            if (sizeChanged(currentProps, newProps)) state.createGeometry = true
+        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P, newTheme: Theme, currentTheme: Theme) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+            if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
         createEmptyGeometry: Mesh.createEmpty,
         createRenderObject: createComplexMeshRenderObject,

+ 26 - 0
src/mol-repr/structure/registry.ts

@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Structure } from 'mol-model/structure';
+import { RepresentationProvider, RepresentationRegistry } from '../representation';
+import { CartoonRepresentationProvider } from './representation/cartoon';
+
+export class StructureRepresentationRegistry extends RepresentationRegistry<Structure> {
+    constructor() {
+        super()
+        Object.keys(BuiltInStructureRepresentations).forEach(name => {
+            const p = (BuiltInStructureRepresentations as { [k: string]: RepresentationProvider<Structure, any> })[name]
+            this.add(name, p.factory, p.params)
+        })
+    }
+}
+
+export const BuiltInStructureRepresentations = {
+    'cartoon': CartoonRepresentationProvider,
+}
+export type BuiltInStructureRepresentationsName = keyof typeof BuiltInStructureRepresentations
+export const BuiltInStructureRepresentationsNames = Object.keys(BuiltInStructureRepresentations)
+export const BuiltInStructureRepresentationsOptions = BuiltInStructureRepresentationsNames.map(n => [n, n] as [BuiltInStructureRepresentationsName, string])

+ 5 - 6
src/mol-repr/structure/index.ts → src/mol-repr/structure/representation.ts

@@ -6,9 +6,7 @@
  */
 
 import { Structure } from 'mol-model/structure';
-import { ColorThemeName, ColorThemeOptions } from 'mol-theme/color';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { Representation, RepresentationProps } from '..';
+import { Representation, RepresentationProps, RepresentationProvider } from '../representation';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Geometry } from 'mol-geo/geometry/geometry';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
@@ -17,12 +15,13 @@ import { Lines } from 'mol-geo/geometry/lines/lines';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
 
 export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { }
-// export interface  StructureVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { }
+
+export type StructureRepresentationProvider<P extends PD.Params> = RepresentationProvider<Structure, P>
+
+//
 
 export const StructureParams = {
     ...Geometry.Params,
-    colorTheme: PD.Select<ColorThemeName>('Color Theme', '', 'polymer-index', ColorThemeOptions),
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
 }
 export const DefaultStructureProps = PD.getDefaultValues(StructureParams)
 export type StructureProps = typeof DefaultStructureProps

+ 25 - 21
src/mol-repr/structure/representation/backbone.ts

@@ -1,25 +1,29 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
 
-import { PolymerBackboneVisual, PolymerBackboneParams } from '../visual/polymer-backbone-cylinder';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { UnitsRepresentation } from '../units-representation';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
+// import { PolymerBackboneVisual, PolymerBackboneParams } from '../visual/polymer-backbone-cylinder';
+// import { ParamDefinition as PD } from 'mol-util/param-definition';
+// import { UnitsRepresentation } from '../units-representation';
+// import { StructureRepresentation } from '../representation';
+// import { Representation } from 'mol-repr/representation';
+// import { ThemeRegistryContext } from 'mol-theme/theme';
+// import { Structure } from 'mol-model/structure';
 
-export const BackboneParams = {
-    ...PolymerBackboneParams
-}
-export const DefaultBackboneProps = PD.getDefaultValues(BackboneParams)
-export type BackboneProps = typeof DefaultBackboneProps
+// export const BackboneParams = {
+//     ...PolymerBackboneParams,
+// }
+// export function getBackboneParams(ctx: ThemeRegistryContext, structure: Structure) {
+//     return BackboneParams // TODO return copy
+// }
+// export type BackboneProps = PD.DefaultValues<typeof BackboneParams>
 
-export type BackboneRepresentation = StructureRepresentation<BackboneProps>
+// export type BackboneRepresentation = StructureRepresentation<BackboneProps>
 
-export function BackboneRepresentation(): BackboneRepresentation {
-    return Representation.createMulti('Backbone', BackboneParams, DefaultBackboneProps, [
-        UnitsRepresentation('Polymer backbone cylinder', PolymerBackboneVisual)
-    ] as StructureRepresentation<BackboneProps>[])
-}
+// export function BackboneRepresentation(defaultProps: BackboneProps): BackboneRepresentation {
+//     return Representation.createMulti('Backbone', defaultProps, [
+//         UnitsRepresentation('Polymer backbone cylinder', defaultProps, PolymerBackboneVisual)
+//     ])
+// }

+ 34 - 34
src/mol-repr/structure/representation/ball-and-stick.ts

@@ -1,38 +1,38 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
 
-import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere';
-import { IntraUnitLinkVisual, IntraUnitLinkParams } from '../visual/intra-unit-link-cylinder';
-import { InterUnitLinkVisual, InterUnitLinkParams } from '../visual/inter-unit-link-cylinder';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { UnitKind, UnitKindOptions } from '../visual/util/common';
-import { UnitsRepresentation } from '../units-representation';
-import { ComplexRepresentation } from '../complex-representation';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
+// import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere';
+// import { IntraUnitLinkVisual, IntraUnitLinkParams } from '../visual/intra-unit-link-cylinder';
+// import { InterUnitLinkVisual, InterUnitLinkParams } from '../visual/inter-unit-link-cylinder';
+// import { ParamDefinition as PD } from 'mol-util/param-definition';
+// import { UnitsRepresentation } from '../units-representation';
+// import { ComplexRepresentation } from '../complex-representation';
+// import { StructureRepresentation } from '../representation';
+// import { Representation } from 'mol-repr/representation';
+// import { ThemeRegistryContext } from 'mol-theme/theme';
+// import { Structure } from 'mol-model/structure';
 
-export const BallAndStickParams = {
-    ...ElementSphereParams,
-    ...IntraUnitLinkParams,
-    ...InterUnitLinkParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1),
-    sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1),
-    unitKinds: PD.MultiSelect<UnitKind>('Unit Kind', '', ['atomic'], UnitKindOptions),
-}
-export const DefaultBallAndStickProps = PD.getDefaultValues(BallAndStickParams)
-export type BallAndStickProps = typeof DefaultBallAndStickProps
+// export const BallAndStickParams = {
+//     ...ElementSphereParams,
+//     ...IntraUnitLinkParams,
+//     ...InterUnitLinkParams,
+//     // TODO
+//     // unitKinds: PD.MultiSelect<UnitKind>('Unit Kind', '', ['atomic'], UnitKindOptions),
+// }
+// export function getBallAndStickParams(ctx: ThemeRegistryContext, structure: Structure) {
+//     return BallAndStickParams // TODO return copy
+// }
+// export type BallAndStickProps = PD.DefaultValues<typeof BallAndStickParams>
 
-export type BallAndStickRepresentation = StructureRepresentation<BallAndStickProps>
+// export type BallAndStickRepresentation = StructureRepresentation<BallAndStickProps>
 
-export function BallAndStickRepresentation(): BallAndStickRepresentation {
-    return Representation.createMulti('Ball & Stick', BallAndStickParams, DefaultBallAndStickProps, [
-        UnitsRepresentation('Element sphere mesh', ElementSphereVisual),
-        UnitsRepresentation('Intra-unit link cylinder', IntraUnitLinkVisual),
-        ComplexRepresentation('Inter-unit link cylinder', InterUnitLinkVisual)
-    ] as unknown as StructureRepresentation<BallAndStickProps>[]) // TODO avoid cast to unknown
-}
+// export function BallAndStickRepresentation(defaultProps: BallAndStickProps): BallAndStickRepresentation {
+//     return Representation.createMulti('Ball & Stick', defaultProps, [
+//         UnitsRepresentation('Element sphere mesh', defaultProps, ElementSphereVisual),
+//         UnitsRepresentation('Intra-unit link cylinder', defaultProps, IntraUnitLinkVisual),
+//         ComplexRepresentation('Inter-unit link cylinder', defaultProps, InterUnitLinkVisual)
+//     ])
+// }

+ 28 - 28
src/mol-repr/structure/representation/carbohydrate.ts

@@ -1,32 +1,32 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
 
-import { CarbohydrateSymbolVisual, CarbohydrateSymbolParams } from '../visual/carbohydrate-symbol-mesh';
-import { CarbohydrateLinkVisual, CarbohydrateLinkParams } from '../visual/carbohydrate-link-cylinder';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { ComplexRepresentation } from '../complex-representation';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
+// import { CarbohydrateSymbolVisual, CarbohydrateSymbolParams } from '../visual/carbohydrate-symbol-mesh';
+// import { CarbohydrateLinkVisual, CarbohydrateLinkParams } from '../visual/carbohydrate-link-cylinder';
+// import { ParamDefinition as PD } from 'mol-util/param-definition';
+// import { ComplexRepresentation } from '../complex-representation';
+// import { StructureRepresentation } from '../representation';
+// import { Representation } from 'mol-repr/representation';
+// import { ThemeRegistryContext } from 'mol-theme/theme';
+// import { Structure } from 'mol-model/structure';
 
-export const CarbohydrateParams = {
-    ...CarbohydrateSymbolParams,
-    ...CarbohydrateLinkParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 1, 0, 0.1, 20),
-    sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1),
-}
-export const DefaultCarbohydrateProps = PD.getDefaultValues(CarbohydrateParams)
-export type CarbohydrateProps = typeof DefaultCarbohydrateProps
+// export const CarbohydrateParams = {
+//     ...CarbohydrateSymbolParams,
+//     ...CarbohydrateLinkParams,
+// }
+// export function getCarbohydrateParams(ctx: ThemeRegistryContext, structure: Structure) {
+//     return CarbohydrateParams // TODO return copy
+// }
+// export type CarbohydrateProps = PD.DefaultValues<typeof CarbohydrateParams>
 
-export type CarbohydrateRepresentation = StructureRepresentation<CarbohydrateProps>
+// export type CarbohydrateRepresentation = StructureRepresentation<CarbohydrateProps>
 
-export function CarbohydrateRepresentation(): CarbohydrateRepresentation {
-    return Representation.createMulti('Carbohydrate', CarbohydrateParams, DefaultCarbohydrateProps, [
-        ComplexRepresentation('Carbohydrate symbol mesh', CarbohydrateSymbolVisual),
-        ComplexRepresentation('Carbohydrate link cylinder', CarbohydrateLinkVisual)
-    ] as StructureRepresentation<CarbohydrateProps>[])
-}
+// export function CarbohydrateRepresentation(defaultProps: CarbohydrateProps): CarbohydrateRepresentation {
+//     return Representation.createMulti('Carbohydrate', defaultProps, [
+//         ComplexRepresentation('Carbohydrate symbol mesh', defaultProps, CarbohydrateSymbolVisual),
+//         ComplexRepresentation('Carbohydrate link cylinder', defaultProps, CarbohydrateLinkVisual)
+//     ])
+// }

+ 32 - 18
src/mol-repr/structure/representation/cartoon.ts

@@ -4,34 +4,48 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { PolymerTraceVisual,  PolymerTraceParams } from '../visual/polymer-trace-mesh';
-import { PolymerGapVisual, PolymerGapParams } from '../visual/polymer-gap-cylinder';
-import { NucleotideBlockVisual, NucleotideBlockParams } from '../visual/nucleotide-block-mesh';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
+import { PolymerTraceVisual,  PolymerTraceParams, PolymerTraceProps } from '../visual/polymer-trace-mesh';
+import { PolymerGapVisual, PolymerGapParams, PolymerGapProps } from '../visual/polymer-gap-cylinder';
+import { NucleotideBlockVisual, NucleotideBlockParams, NucleotideBlockProps } from '../visual/nucleotide-block-mesh';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { UnitsRepresentation } from '../units-representation';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
-import { PolymerDirectionVisual, PolymerDirectionParams } from '../visual/polymer-direction-wedge';
+import { StructureRepresentation, StructureRepresentationProvider } from '../representation';
+import { Representation } from 'mol-repr/representation';
+import { PolymerDirectionVisual, PolymerDirectionParams, PolymerDirectionProps } 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';
+
+const CartoonVisuals = {
+    'polymer-trace': (defaultProps: PolymerTraceProps) => UnitsRepresentation('Polymer trace mesh', defaultProps, PolymerTraceVisual),
+    'polymer-gap': (defaultProps: PolymerGapProps) => UnitsRepresentation('Polymer gap cylinder', defaultProps, PolymerGapVisual),
+    'nucleotide-block': (defaultProps: NucleotideBlockProps) => UnitsRepresentation('Nucleotide block mesh', defaultProps, NucleotideBlockVisual),
+    'direction-wedge': (defaultProps: PolymerDirectionProps) => UnitsRepresentation('Polymer direction wedge', defaultProps, PolymerDirectionVisual)
+}
+type CartoonVisualName = keyof typeof CartoonVisuals
+const CartoonVisualOptions = Object.keys(CartoonVisuals).map(name => [name, name] as [CartoonVisualName, string])
 
 export const CartoonParams = {
     ...PolymerTraceParams,
     ...PolymerGapParams,
     ...NucleotideBlockParams,
     ...PolymerDirectionParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1),
+    sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01),
+    sizeTheme: PD.Select<BuiltInSizeThemeName>('Size Theme', '', 'uniform', BuiltInSizeThemeOptions),
+    colorTheme: PD.Select<BuiltInColorThemeName>('Color Theme', '', 'polymer-index', BuiltInColorThemeOptions),
+    visuals: PD.MultiSelect<CartoonVisualName>('Visuals', '', ['polymer-trace', 'polymer-gap', 'nucleotide-block'], CartoonVisualOptions),
 }
-export const DefaultCartoonProps = { ...PD.getDefaultValues(CartoonParams), visuals: [ '0', '1', '2' ] }
-export type CartoonProps = typeof DefaultCartoonProps
+export function getCartoonParams(ctx: ThemeRegistryContext, structure: Structure) {
+    return CartoonParams // TODO return copy
+}
+export type CartoonProps = PD.DefaultValues<typeof CartoonParams>
 
 export type CartoonRepresentation = StructureRepresentation<CartoonProps>
+export function CartoonRepresentation(defaultProps: CartoonProps): CartoonRepresentation {
+    return Representation.createMulti('Cartoon', defaultProps, CartoonVisuals as unknown as Representation.Def<CartoonProps>)
+}
 
-export function CartoonRepresentation(): CartoonRepresentation {
-    return Representation.createMulti('Cartoon', CartoonParams, DefaultCartoonProps, [
-        UnitsRepresentation('Polymer trace mesh', PolymerTraceVisual),
-        UnitsRepresentation('Polymer gap cylinder', PolymerGapVisual),
-        UnitsRepresentation('Nucleotide block mesh', NucleotideBlockVisual),
-        UnitsRepresentation('Polymer direction wedge', PolymerDirectionVisual)
-    ] as unknown as StructureRepresentation<CartoonProps>[]) // TODO avoid cast to unknown
+export const CartoonRepresentationProvider: StructureRepresentationProvider<typeof CartoonParams> = {
+    factory: CartoonRepresentation, params: getCartoonParams
 }

+ 25 - 24
src/mol-repr/structure/representation/distance-restraint.ts

@@ -1,28 +1,29 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
 
-import { CrossLinkRestraintVisual, CrossLinkRestraintParams } from '../visual/cross-link-restraint-cylinder';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { ComplexRepresentation } from '../complex-representation';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
+// import { CrossLinkRestraintVisual, CrossLinkRestraintParams } from '../visual/cross-link-restraint-cylinder';
+// import { ParamDefinition as PD } from 'mol-util/param-definition';
+// import { ComplexRepresentation } from '../complex-representation';
+// import { StructureRepresentation } from '../representation';
+// import { Representation } from 'mol-repr/representation';
+// import { ThemeRegistryContext } from 'mol-theme/theme';
+// import { Structure } from 'mol-model/structure';
 
-export const DistanceRestraintParams = {
-    ...CrossLinkRestraintParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 0.25, 0, 0.05, 20),
-}
-export const DefaultDistanceRestraintProps = PD.getDefaultValues(DistanceRestraintParams)
-export type DistanceRestraintProps = typeof DefaultDistanceRestraintProps
+// export const DistanceRestraintParams = {
+//     ...CrossLinkRestraintParams,
+// }
+// export function getDistanceRestraintParams(ctx: ThemeRegistryContext, structure: Structure) {
+//     return DistanceRestraintParams // TODO return copy
+// }
+// export type DistanceRestraintProps = PD.DefaultValues<typeof DistanceRestraintParams>
 
-export type DistanceRestraintRepresentation = StructureRepresentation<DistanceRestraintProps>
+// export type DistanceRestraintRepresentation = StructureRepresentation<DistanceRestraintProps>
 
-export function DistanceRestraintRepresentation(): DistanceRestraintRepresentation {
-    return Representation.createMulti('Distance restraint', DistanceRestraintParams, DefaultDistanceRestraintProps, [
-        ComplexRepresentation('Cross-link restraint', CrossLinkRestraintVisual)
-    ] as StructureRepresentation<DistanceRestraintProps>[])
-}
+// export function DistanceRestraintRepresentation(defaultProps: DistanceRestraintProps): DistanceRestraintRepresentation {
+//     return Representation.createMulti('Distance restraint', defaultProps, [
+//         ComplexRepresentation('Cross-link restraint', defaultProps, CrossLinkRestraintVisual)
+//     ])
+// }

+ 31 - 29
src/mol-repr/structure/representation/molecular-surface.ts

@@ -1,33 +1,35 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
 
-import { GaussianSurfaceVisual, GaussianSurfaceParams } from '../visual/gaussian-surface-mesh';
-import { UnitsRepresentation } from '../units-representation';
-import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
+// import { GaussianSurfaceVisual, GaussianSurfaceParams } from '../visual/gaussian-surface-mesh';
+// import { UnitsRepresentation } from '../units-representation';
+// import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe';
+// import { ParamDefinition as PD } from 'mol-util/param-definition';
+// import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume';
+// import { StructureRepresentation } from '../representation';
+// import { Representation } from 'mol-repr/representation';
+// import { ThemeRegistryContext } from 'mol-theme/theme';
+// import { Structure } from 'mol-model/structure';
 
-export const MolecularSurfaceParams = {
-    ...GaussianSurfaceParams,
-    ...GaussianWireframeParams,
-    ...GaussianDensityVolumeParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-}
-export const DefaultMolecularSurfaceProps = { ...PD.getDefaultValues(MolecularSurfaceParams), visuals: [ '0' ] }
-export type MolecularSurfaceProps = typeof DefaultMolecularSurfaceProps
+// export const MolecularSurfaceParams = {
+//     ...GaussianSurfaceParams,
+//     ...GaussianWireframeParams,
+//     ...GaussianDensityVolumeParams,
+// }
+// export function getMolecularSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) {
+//     return MolecularSurfaceParams // TODO return copy
+// }
+// export type MolecularSurfaceProps = PD.DefaultValues<typeof MolecularSurfaceParams>
 
-export type MolecularSurfaceRepresentation = StructureRepresentation<MolecularSurfaceProps>
+// export type MolecularSurfaceRepresentation = StructureRepresentation<MolecularSurfaceProps>
 
-export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation {
-    return Representation.createMulti('Molecular Surface', MolecularSurfaceParams, DefaultMolecularSurfaceProps, [
-        UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual),
-        UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual),
-        UnitsRepresentation('Gaussian volume', GaussianDensityVolumeVisual)
-    ] as unknown as StructureRepresentation<MolecularSurfaceProps>[]) // TODO avoid cast to unknown
-}
+// export function MolecularSurfaceRepresentation(defaultProps: MolecularSurfaceProps): MolecularSurfaceRepresentation {
+//     return Representation.createMulti('Molecular Surface', defaultProps, [
+//         UnitsRepresentation('Gaussian surface', defaultProps, GaussianSurfaceVisual),
+//         UnitsRepresentation('Gaussian wireframe', defaultProps, GaussianWireframeVisual),
+//         UnitsRepresentation('Gaussian volume', defaultProps, GaussianDensityVolumeVisual)
+//     ])
+// }

+ 25 - 21
src/mol-repr/structure/representation/point.ts

@@ -1,25 +1,29 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
 
-import { ElementPointVisual, ElementPointParams } from '../visual/element-point';
-import { UnitsRepresentation } from '../units-representation';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
+// import { ElementPointVisual, ElementPointParams } from '../visual/element-point';
+// import { UnitsRepresentation } from '../units-representation';
+// import { ParamDefinition as PD } from 'mol-util/param-definition';
+// import { StructureRepresentation } from '../representation';
+// import { Representation } from 'mol-repr/representation';
+// import { ThemeRegistryContext } from 'mol-theme/theme';
+// import { Structure } from 'mol-model/structure';
 
-export const PointParams = {
-    ...ElementPointParams,
-}
-export const DefaultPointProps = PD.getDefaultValues(PointParams)
-export type PointProps = typeof DefaultPointProps
+// export const PointParams = {
+//     ...ElementPointParams,
+// }
+// export function getPointParams(ctx: ThemeRegistryContext, structure: Structure) {
+//     return PointParams // TODO return copy
+// }
+// export type PointProps = PD.DefaultValues<typeof PointParams>
 
-export type PointRepresentation = StructureRepresentation<PointProps>
+// export type PointRepresentation = StructureRepresentation<PointProps>
 
-export function PointRepresentation(): PointRepresentation {
-    return Representation.createMulti('Point', PointParams, DefaultPointProps, [
-        UnitsRepresentation('Point', ElementPointVisual)
-    ] as StructureRepresentation<PointProps>[])
-}
+// export function PointRepresentation(defaultProps: PointProps): PointRepresentation {
+//     return Representation.createMulti('Point', defaultProps, [
+//         UnitsRepresentation('Point', defaultProps, ElementPointVisual)
+//     ])
+// }

+ 25 - 21
src/mol-repr/structure/representation/spacefill.ts

@@ -1,25 +1,29 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
 
-import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere';
-import { UnitsRepresentation } from '../units-representation';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { StructureRepresentation } from '../index';
-import { Representation } from 'mol-repr';
+// import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere';
+// import { UnitsRepresentation } from '../units-representation';
+// import { ParamDefinition as PD } from 'mol-util/param-definition';
+// import { StructureRepresentation } from '../representation';
+// import { Representation } from 'mol-repr/representation';
+// import { ThemeRegistryContext } from 'mol-theme/theme';
+// import { Structure } from 'mol-model/structure';
 
-export const SpacefillParams = {
-    ...ElementSphereParams
-}
-export const DefaultSpacefillProps = PD.getDefaultValues(SpacefillParams)
-export type SpacefillProps = typeof DefaultSpacefillProps
+// export const SpacefillParams = {
+//     ...ElementSphereParams,
+// }
+// export function getSpacefillParams(ctx: ThemeRegistryContext, structure: Structure) {
+//     return SpacefillParams // TODO return copy
+// }
+// export type SpacefillProps = PD.DefaultValues<typeof SpacefillParams>
 
-export type SpacefillRepresentation = StructureRepresentation<SpacefillProps>
+// export type SpacefillRepresentation = StructureRepresentation<SpacefillProps>
 
-export function SpacefillRepresentation(): SpacefillRepresentation {
-    return Representation.createMulti('Spacefill', SpacefillParams, DefaultSpacefillProps, [
-        UnitsRepresentation('Sphere mesh', ElementSphereVisual)
-    ] as StructureRepresentation<SpacefillProps>[])
-}
+// export function SpacefillRepresentation(defaultProps: SpacefillProps): SpacefillRepresentation {
+//     return Representation.createMulti('Spacefill', defaultProps, [
+//         UnitsRepresentation('Sphere mesh', defaultProps, ElementSphereVisual)
+//     ])
+// }

+ 9 - 10
src/mol-repr/structure/units-representation.ts

@@ -8,27 +8,27 @@
 import { Structure, Unit } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { RenderObject } from 'mol-gl/render-object';
-import { RepresentationProps, Visual, RepresentationContext } from '..';
+import { RepresentationProps, Visual, RepresentationContext } from '../representation';
 import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
 import { StructureGroup } from './units-visual';
-import { StructureProps, StructureParams, StructureRepresentation } from './index';
+import { StructureProps, StructureRepresentation } from './representation';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
-import { Theme, createTheme } from 'mol-geo/geometry/geometry';
+import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
 
 export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { }
 
-export function UnitsRepresentation<P extends StructureProps>(label: string, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> {
+export function UnitsRepresentation<P extends StructureProps>(label: string, defaultProps: P, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> {
     let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>()
 
-    let _props: P
-    let _theme: Theme
     let _structure: Structure
     let _groups: ReadonlyArray<Unit.SymmetryGroup>
+    let _props: P = Object.assign({}, defaultProps)
+    let _theme: Theme
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, structure?: Structure) {
-        _props = Object.assign({}, _props, props, { structure: structure || _structure })
-        _theme = createTheme(_props)
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
+        _props = Object.assign({}, _props, props)
+        _theme = createTheme(ctx, { structure: structure || _structure }, props, themeProps, _theme)
 
         return Task.create('Creating or updating UnitsRepresentation', async runtime => {
             if (!_structure && !structure) {
@@ -132,7 +132,6 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
 
     return {
         label,
-        params: StructureParams, // TODO
         get renderObjects() {
             const renderObjects: RenderObject[] = []
             visuals.forEach(({ visual }) => {

+ 25 - 19
src/mol-repr/structure/units-visual.ts

@@ -5,8 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { RepresentationProps, Visual, VisualContext } from '../';
-import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureParams } from './index';
+import { RepresentationProps, Visual, VisualContext } from '../representation';
+import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureParams } from './representation';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
 import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject, UnitKind, UnitKindOptions, includesUnitKind } from './visual/util/common';
@@ -14,7 +14,7 @@ import { deepEqual, ValueCell, UUID } from 'mol-util';
 import { Interval } from 'mol-data/int';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { RenderableValues } from 'mol-gl/renderable/schema';
-import { Geometry, updateRenderableState, Theme } from 'mol-geo/geometry/geometry';
+import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { createMarkers, MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
@@ -24,7 +24,10 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { Points } from 'mol-geo/geometry/points/points';
 import { Lines } from 'mol-geo/geometry/lines/lines';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
-import { VisualUpdateState, colorChanged, sizeChanged } from 'mol-repr/util';
+import { VisualUpdateState } from 'mol-repr/util';
+import { Theme } from 'mol-theme/theme';
+import { ColorTheme } from 'mol-theme/color';
+import { SizeTheme } from 'mol-theme/size';
 
 export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup }
 
@@ -52,7 +55,7 @@ interface UnitsVisualBuilder<P extends UnitsProps, G extends Geometry> {
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number): Loci
     mark(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean): boolean
-    setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void
+    setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P, newTheme: Theme, currentTheme: Theme): void
 }
 
 interface UnitsVisualGeometryBuilder<P extends UnitsProps, G extends Geometry> extends UnitsVisualBuilder<P, G> {
@@ -68,6 +71,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
 
     let renderObject: UnitsRenderObject | undefined
     let currentProps: P
+    let currentTheme: Theme
     let geometry: Geometry
     let currentGroup: Unit.SymmetryGroup
     let currentStructure: Structure
@@ -76,6 +80,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
 
     async function create(ctx: VisualContext, group: Unit.SymmetryGroup, theme: Theme, props: Partial<P> = {}) {
         currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure })
+        currentTheme = theme
         currentGroup = group
 
         const unit = group.units[0]
@@ -97,7 +102,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
 
         locationIt.reset()
         VisualUpdateState.reset(updateState)
-        setUpdateState(updateState, newProps, currentProps)
+        setUpdateState(updateState, newProps, currentProps, theme, currentTheme)
 
         const newConformationId = Unit.conformationId(unit)
         if (newConformationId !== currentConformationId) {
@@ -107,7 +112,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
 
         if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true
 
-        if (colorChanged(currentProps, newProps)) updateState.updateColor = true
+        if (ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true
         if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
 
         //
@@ -143,6 +148,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
         updateRenderableState(renderObject.state, newProps)
 
         currentProps = newProps
+        currentTheme = theme
     }
 
     return {
@@ -212,9 +218,9 @@ export interface UnitsMeshVisualBuilder<P extends UnitsMeshProps> extends UnitsV
 export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
     return UnitsVisual({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
-            builder.setUpdateState(state, newProps, currentProps)
-            if (sizeChanged(currentProps, newProps)) state.createGeometry = true
+        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P, newTheme: Theme, currentTheme: Theme) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+            if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
         createEmptyGeometry: Mesh.createEmpty,
         createRenderObject: createUnitsMeshRenderObject,
@@ -237,9 +243,9 @@ export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPoin
         ...builder,
         createEmptyGeometry: Points.createEmpty,
         createRenderObject: createUnitsPointsRenderObject,
-        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
-            builder.setUpdateState(state, newProps, currentProps)
-            if (sizeChanged(currentProps, newProps)) state.updateSize = true
+        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P, newTheme: Theme, currentTheme: Theme) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+            if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
         updateValues: Points.updateValues
     })
@@ -260,9 +266,9 @@ export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesV
         ...builder,
         createEmptyGeometry: Lines.createEmpty,
         createRenderObject: createUnitsLinesRenderObject,
-        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
-            builder.setUpdateState(state, newProps, currentProps)
-            if (sizeChanged(currentProps, newProps)) state.updateSize = true
+        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P, newTheme: Theme, currentTheme: Theme) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+            if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
         updateValues: Lines.updateValues
     })
@@ -283,9 +289,9 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde
         ...builder,
         createEmptyGeometry: DirectVolume.createEmpty,
         createRenderObject: createUnitsDirectVolumeRenderObject,
-        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P) => {
-            builder.setUpdateState(state, newProps, currentProps)
-            if (sizeChanged(currentProps, newProps)) state.createGeometry = true
+        setUpdateState: (state: VisualUpdateState, newProps: P, currentProps: P, newTheme: Theme, currentTheme: Theme) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+            if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
         updateValues: DirectVolume.updateValues
     })

+ 5 - 5
src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts

@@ -10,7 +10,6 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { createLinkCylinderMesh, LinkCylinderProps, LinkCylinderParams } from './util/link';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { ComplexMeshVisual, ComplexVisual } from '../complex-visual';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { LinkType } from 'mol-model/structure/model/types';
 import { BitFlags } from 'mol-util';
 import { UnitsMeshParams } from '../units-visual';
@@ -19,8 +18,8 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualUpdateState } from '../../util';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 // TODO create seperate visual
 // for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) {
@@ -64,8 +63,9 @@ async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure:
 export const CarbohydrateLinkParams = {
     ...UnitsMeshParams,
     ...LinkCylinderParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
     detail: PD.Numeric('Sphere Detail', '', 0, 0, 3, 1),
 }
 export const DefaultCarbohydrateLinkProps = PD.getDefaultValues(CarbohydrateLinkParams)

+ 6 - 6
src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts

@@ -13,19 +13,18 @@ import { DiamondPrism, PentagonalPrism, HexagonalPrism } from 'mol-geo/primitive
 import { Structure, StructureElement } from 'mol-model/structure';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { ComplexMeshParams, ComplexMeshVisual } from '../complex-visual';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { ComplexVisual } from '../index';
+import { ComplexVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { EmptyLoci, Loci } from 'mol-model/loci';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -148,8 +147,9 @@ async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Struc
 
 export const CarbohydrateSymbolParams = {
     ...ComplexMeshParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 1, 0, 10, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 1, 0, 10, 0.1),
     detail: PD.Numeric('Sphere Detail', '', 0, 0, 3, 1),
 }
 export const DefaultCarbohydrateSymbolProps = PD.getDefaultValues(CarbohydrateSymbolParams)

+ 6 - 6
src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts

@@ -5,22 +5,21 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { ComplexVisual } from '../index';
+import { ComplexVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { LinkCylinderProps, createLinkCylinderMesh, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual';
 import { Interval } from 'mol-data/int';
-import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
 import { BitFlags } from 'mol-util';
 import { LinkType } from 'mol-model/structure/model/types';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
 
@@ -54,8 +53,9 @@ async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structur
 export const CrossLinkRestraintParams = {
     ...ComplexMeshParams,
     ...LinkCylinderParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
 }
 export const DefaultCrossLinkRestraintProps = PD.getDefaultValues(CrossLinkRestraintParams)
 export type CrossLinkRestraintProps = typeof DefaultCrossLinkRestraintProps

+ 6 - 6
src/mol-repr/structure/visual/element-point.ts

@@ -5,22 +5,22 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { getElementLoci, StructureElementIterator, markElement } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
 import { UnitsPointsVisual, UnitsPointsParams } from '../units-visual';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Points } from 'mol-geo/geometry/points/points';
 import { PointsBuilder } from 'mol-geo/geometry/points/points-builder';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 export const ElementPointParams = {
     ...UnitsPointsParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 3, 0, 20, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 3, 0, 20, 0.1),
     pointSizeAttenuation: PD.Boolean('Point Size Attenuation', '', false),
 }
 export const DefaultElementPointProps = PD.getDefaultValues(ElementPointParams)

+ 5 - 5
src/mol-repr/structure/visual/element-sphere.ts

@@ -5,18 +5,18 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 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 { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 
 export const ElementSphereParams = {
     ...UnitsMeshParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1),
-    sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1),
+    // sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1),
     detail: PD.Numeric('Sphere Detail', '', 0, 0, 3, 1),
 }
 export const DefaultElementSphereProps = PD.getDefaultValues(ElementSphereParams)

+ 6 - 6
src/mol-repr/structure/visual/gaussian-density-point.ts

@@ -5,25 +5,25 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { StructureElementIterator } from './util/element';
 import { EmptyLoci } from 'mol-model/loci';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { UnitsPointsVisual, UnitsPointsParams } from '../units-visual';
-import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
 import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Points } from 'mol-geo/geometry/points/points';
 import { PointsBuilder } from 'mol-geo/geometry/points/points-builder';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 export const GaussianDensityPointParams = {
     ...UnitsPointsParams,
     ...GaussianDensityParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
     pointSizeAttenuation: PD.Boolean('Point Size Attenuation', '', false),
 }
 export const DefaultGaussianDensityPointProps = PD.getDefaultValues(GaussianDensityPointParams)

+ 3 - 3
src/mol-repr/structure/visual/gaussian-density-volume.ts

@@ -5,15 +5,15 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { UnitsDirectVolumeVisual, UnitsDirectVolumeParams } from '../units-visual';
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
 import { GaussianDensityProps, GaussianDensityParams, computeUnitGaussianDensityTexture } from 'mol-model/structure/structure/unit/gaussian-density';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 async function createGaussianDensityVolume(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> {
     const { runtime, webgl } = ctx

+ 3 - 3
src/mol-repr/structure/visual/gaussian-surface-mesh.ts

@@ -5,7 +5,7 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
@@ -13,8 +13,8 @@ import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
     const { smoothness } = props

+ 6 - 6
src/mol-repr/structure/visual/gaussian-surface-wireframe.ts

@@ -5,17 +5,16 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { UnitsLinesVisual, UnitsLinesParams } from '../units-visual';
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
 import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { Lines } from 'mol-geo/geometry/lines/lines';
 import { computeMarchingCubesLines } from 'mol-geo/util/marching-cubes/algorithm';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, lines?: Lines): Promise<Lines> {
     const { smoothness } = props
@@ -36,8 +35,9 @@ async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure
 export const GaussianWireframeParams = {
     ...UnitsLinesParams,
     ...GaussianDensityParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 2, 0, 10, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 2, 0, 10, 0.1),
     lineSizeAttenuation: PD.Boolean('Line Size Attenuation', '', false),
 }
 export const DefaultGaussianWireframeProps = PD.getDefaultValues(GaussianWireframeParams)

+ 3 - 3
src/mol-repr/structure/visual/inter-unit-link-cylinder.ts

@@ -5,7 +5,7 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { ComplexVisual } from '../index';
+import { ComplexVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
@@ -16,8 +16,8 @@ import { BitFlags } from 'mol-util';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { PickingId } from 'mol-geo/geometry/picking';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
     const links = structure.links

+ 7 - 7
src/mol-repr/structure/visual/intra-unit-link-cylinder.ts

@@ -6,20 +6,19 @@
  */
 
 import { Unit, Link, StructureElement, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { UnitsMeshVisual, UnitsMeshParams, StructureGroup } from '../units-visual';
 import { Interval } from 'mol-data/int';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { BitFlags } from 'mol-util';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { PickingId } from 'mol-geo/geometry/picking';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
@@ -67,9 +66,10 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s
 export const IntraUnitLinkParams = {
     ...UnitsMeshParams,
     ...LinkCylinderParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1),
-    sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 0.2, 0, 10, 0.1),
+    // sizeFactor: PD.Numeric('Size Factor', '', 1, 0, 10, 0.1),
 }
 export const DefaultIntraUnitLinkProps = PD.getDefaultValues(IntraUnitLinkParams)
 export type IntraUnitLinkProps = typeof DefaultIntraUnitLinkProps

+ 16 - 8
src/mol-repr/structure/visual/nucleotide-block-mesh.ts

@@ -5,7 +5,7 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { Segmentation } from 'mol-data/int';
 import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
@@ -17,8 +17,8 @@ import { Box } from 'mol-geo/primitive/box';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 const p1 = Vec3.zero()
 const p2 = Vec3.zero()
@@ -34,10 +34,17 @@ const t = Mat4.identity()
 const sVec = Vec3.zero()
 const box = Box()
 
-// TODO define props, should be scalable
-async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: {}, mesh?: Mesh) {
+export const NucleotideBlockMeshParams = {
+    sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01),
+}
+export const DefaultNucleotideBlockMeshProps = PD.getDefaultValues(NucleotideBlockMeshParams)
+export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps
+
+async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: NucleotideBlockMeshProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
+    const { sizeFactor } = props
+
     // TODO better vertex count estimate
     const builder = MeshBuilder.create(256, 128, mesh)
 
@@ -64,7 +71,7 @@ async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structu
                 const parentId = modifiedResidues.parentId.get(compId)
                 if (parentId !== undefined) compId = parentId
                 let idx1 = -1, idx2 = -1, idx3 = -1, idx4 = -1, idx5 = -1, idx6 = -1
-                let width = 4.5, height = 4.5, depth = 0.5
+                let width = 4.5, height = 4.5, depth = 2.5 * sizeFactor
 
                 if (isPurinBase(compId)) {
                     height = 4.5
@@ -87,7 +94,7 @@ async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structu
                 if (idx5 !== -1 && idx6 !== -1) {
                     pos(idx5, p5); pos(idx6, p6)
                     builder.setGroup(i)
-                    addCylinder(builder, p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
+                    addCylinder(builder, p5, p6, 1, { radiusTop: 1 * sizeFactor, radiusBottom: 1 * sizeFactor })
                     if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1) {
                         pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4);
                         Vec3.normalize(v12, Vec3.sub(v12, p2, p1))
@@ -113,7 +120,8 @@ async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structu
 }
 
 export const NucleotideBlockParams = {
-    ...UnitsMeshParams
+    ...UnitsMeshParams,
+    ...NucleotideBlockMeshParams
 }
 export const DefaultNucleotideBlockProps = PD.getDefaultValues(NucleotideBlockParams)
 export type NucleotideBlockProps = typeof DefaultNucleotideBlockProps

+ 3 - 3
src/mol-repr/structure/visual/polymer-backbone-cylinder.ts

@@ -5,7 +5,7 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { PolymerBackboneIterator } from './util/polymer';
 import { getElementLoci, markElement, StructureElementIterator } from './util/element';
@@ -17,8 +17,8 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 export const PolymerBackboneCylinderParams = {
     radialSegments: PD.Numeric('Radial Segments', '', 16, 3, 56, 1),

+ 7 - 7
src/mol-repr/structure/visual/polymer-direction-wedge.ts

@@ -5,18 +5,17 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Wedge } from 'mol-geo/primitive/wedge';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -31,15 +30,16 @@ const heightFactor = 6
 const wedge = Wedge()
 
 export const PolymerDirectionWedgeParams = {
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
+    sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01),
 }
 export const DefaultPolymerDirectionWedgeProps = PD.getDefaultValues(PolymerDirectionWedgeParams)
 export type PolymerDirectionWedgeProps = typeof DefaultPolymerDirectionWedgeProps
 
 async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerDirectionWedgeProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
+
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
+    const { sizeFactor } = props
 
     const vertexCount = polymerElementCount * 24
     const builder = MeshBuilder.create(vertexCount, vertexCount / 10, mesh)
@@ -62,7 +62,7 @@ async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, s
         interpolateCurveSegment(state, v, tension, shift)
 
         if ((isSheet && !v.secStrucChange) || !isSheet) {
-            const size = theme.size.size(v.center)
+            const size = theme.size.size(v.center) * sizeFactor
             const depth = depthFactor * size
             const width = widthFactor * size
             const height = heightFactor * size

+ 11 - 10
src/mol-repr/structure/visual/polymer-gap-cylinder.ts

@@ -5,12 +5,11 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { PolymerGapIterator, PolymerGapLocationIterator, markPolymerGapElement, getPolymerGapElementLoci } from './util/polymer';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
-import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { LinkCylinderParams } from './util/link';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
@@ -18,12 +17,13 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { addFixedCountDashedCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 const segmentCount = 10
 
 export const PolymerGapCylinderParams = {
+    sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01),
     radialSegments: PD.Numeric('Radial Segments', '', 16, 3, 56, 1),
 }
 export const DefaultPolymerGapCylinderProps = PD.getDefaultValues(PolymerGapCylinderParams)
@@ -33,7 +33,7 @@ async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, stru
     const polymerGapCount = unit.gapElements.length
     if (!polymerGapCount) return Mesh.createEmpty(mesh)
 
-    const { radialSegments } = props
+    const { sizeFactor, radialSegments } = props
 
     const vertexCountEstimate = segmentCount * radialSegments * 2 * polymerGapCount * 2
     const builder = MeshBuilder.create(vertexCountEstimate, vertexCountEstimate / 10, mesh)
@@ -57,11 +57,11 @@ async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, stru
             pos(centerA.element, pA)
             pos(centerB.element, pB)
 
-            cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA)
+            cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA) * sizeFactor
             builder.setGroup(i)
             addFixedCountDashedCylinder(builder, pA, pB, 0.5, segmentCount, cylinderProps)
 
-            cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB)
+            cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB) * sizeFactor
             builder.setGroup(i + 1)
             addFixedCountDashedCylinder(builder, pB, pA, 0.5, segmentCount, cylinderProps)
         }
@@ -78,9 +78,10 @@ async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, stru
 export const InterUnitLinkParams = {
     ...UnitsMeshParams,
     ...LinkCylinderParams,
-    sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
-    sizeFactor: PD.Numeric('Size Factor', '', 0.3, 0, 10, 0.1),
+    // TODO
+    // sizeTheme: PD.Select<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
+    // sizeValue: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
+    // sizeFactor: PD.Numeric('Size Factor', '', 0.3, 0, 10, 0.1),
 }
 export const DefaultIntraUnitLinkProps = PD.getDefaultValues(InterUnitLinkParams)
 export type IntraUnitLinkProps = typeof DefaultIntraUnitLinkProps

+ 6 - 5
src/mol-repr/structure/visual/polymer-trace-mesh.ts

@@ -5,7 +5,7 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual } from '../index';
+import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
@@ -15,10 +15,11 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addSheet } from 'mol-geo/geometry/mesh/builder/sheet';
 import { addTube } from 'mol-geo/geometry/mesh/builder/tube';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 export const PolymerTraceMeshParams = {
+    sizeFactor: PD.Numeric('Size Factor', '', 0.2, 0, 10, 0.01),
     linearSegments: PD.Numeric('Linear Segments', '', 8, 1, 48, 1),
     radialSegments: PD.Numeric('Radial Segments', '', 16, 3, 56, 1),
     aspectRatio: PD.Numeric('Aspect Ratio', '', 5, 0.1, 5, 0.1),
@@ -33,7 +34,7 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure:
     const polymerElementCount = unit.polymerElements.length
 
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
-    const { linearSegments, radialSegments, aspectRatio, arrowFactor } = props
+    const { sizeFactor, linearSegments, radialSegments, aspectRatio, arrowFactor } = props
 
     const vertexCount = linearSegments * radialSegments * polymerElementCount + (radialSegments + 1) * polymerElementCount * 2
     const builder = MeshBuilder.create(vertexCount, vertexCount / 10, mesh)
@@ -56,7 +57,7 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure:
 
         interpolateCurveSegment(state, v, tension, shift)
 
-        let width = theme.size.size(v.center)
+        let width = theme.size.size(v.center) * sizeFactor
         if (isCoarse) width *= aspectRatio / 2
 
         if (isSheet) {

+ 4 - 3
src/mol-repr/structure/visual/util/common.ts

@@ -5,17 +5,18 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { StructureProps } from '../../index';
+import { StructureProps } from '../../representation';
 import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolumeRenderObject } from 'mol-gl/render-object';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { TransformData, createTransform, createIdentityTransform } from 'mol-geo/geometry/transform-data';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
-import { createRenderableState, Theme } from 'mol-geo/geometry/geometry';
+import { createRenderableState } from 'mol-geo/geometry/geometry';
 import { Points } from 'mol-geo/geometry/points/points';
 import { Lines } from 'mol-geo/geometry/lines/lines';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
-import { VisualContext } from 'mol-repr';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 
 export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) {
     const unitCount = units.length

+ 2 - 2
src/mol-repr/structure/visual/util/element.ts

@@ -14,8 +14,8 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
-import { VisualContext } from 'mol-repr';
-import { Theme } from 'mol-geo/geometry/geometry';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme } from 'mol-theme/theme';
 import { StructureGroup } from 'mol-repr/structure/units-visual';
 
 export interface ElementSphereMeshProps {

+ 1 - 1
src/mol-repr/structure/visual/util/link.ts

@@ -13,7 +13,7 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
-import { VisualContext } from 'mol-repr';
+import { VisualContext } from 'mol-repr/representation';
 
 export const LinkCylinderParams = {
     linkScale: PD.Range('Link Scale', '', 0.4, 0, 1, 0.1),

+ 0 - 20
src/mol-repr/util.ts

@@ -7,8 +7,6 @@
 import { defaults } from 'mol-util';
 import { Structure } from 'mol-model/structure';
 import { VisualQuality } from 'mol-geo/geometry/geometry';
-import { SizeProps } from 'mol-geo/geometry/size-data';
-import { ColorProps } from 'mol-geo/geometry/color-data';
 
 export interface VisualUpdateState {
     updateTransform: boolean
@@ -33,24 +31,6 @@ export namespace VisualUpdateState {
     }
 }
 
-export function sizeChanged(oldProps: SizeProps, newProps: SizeProps) {
-    return (
-        oldProps.sizeTheme !== newProps.sizeTheme ||
-        oldProps.sizeValue !== newProps.sizeValue ||
-        oldProps.sizeFactor !== newProps.sizeFactor
-    )
-}
-
-export function colorChanged(oldProps: ColorProps, newProps: ColorProps) {
-    return (
-        oldProps.colorTheme !== newProps.colorTheme ||
-        oldProps.colorValue !== newProps.colorValue ||
-        oldProps.colorDomain !== newProps.colorDomain ||
-        oldProps.colorList !== newProps.colorList ||
-        oldProps.colorMap !== newProps.colorMap
-    )
-}
-
 //
 
 export interface QualityProps {

+ 6 - 6
src/mol-repr/volume/direct-volume.ts

@@ -6,7 +6,7 @@
 
 import { VolumeData } from 'mol-model/volume'
 import { RuntimeContext } from 'mol-task'
-import { VolumeVisual, VolumeRepresentation } from './index';
+import { VolumeVisual, VolumeRepresentation } from './representation';
 import { createDirectVolumeRenderObject } from 'mol-gl/render-object';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
@@ -17,11 +17,12 @@ import { createTexture } from 'mol-gl/webgl/texture';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
-import { Geometry, createRenderableState, Theme } from 'mol-geo/geometry/geometry';
+import { Geometry, createRenderableState } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { VisualUpdateState } from 'mol-repr/util';
-import { VisualContext, RepresentationContext } from 'mol-repr';
+import { VisualContext, RepresentationContext } from 'mol-repr/representation';
+import { ThemeProps, Theme } from 'mol-theme/theme';
 
 function getBoundingBox(gridDimension: Vec3, transform: Mat4) {
     const bbox = Box3D.empty()
@@ -213,16 +214,15 @@ export function DirectVolumeRepresentation(): VolumeRepresentation<DirectVolumeP
     const volumeRepr = VolumeRepresentation(DirectVolumeVisual)
     return {
         label: 'Direct Volume',
-        params: DirectVolumeParams,
         get renderObjects() {
             return [ ...volumeRepr.renderObjects ]
         },
         get props() {
             return { ...volumeRepr.props }
         },
-        createOrUpdate: (ctx: RepresentationContext, props: Partial<DirectVolumeProps> = {}, volume?: VolumeData) => {
+        createOrUpdate: (ctx: RepresentationContext, props: Partial<DirectVolumeProps> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) => {
             currentProps = Object.assign({}, DefaultDirectVolumeProps, currentProps, props)
-            return volumeRepr.createOrUpdate(ctx, currentProps, volume)
+            return volumeRepr.createOrUpdate(ctx, currentProps, themeProps, volume)
         },
         getLoci: (pickingId: PickingId) => {
             return volumeRepr.getLoci(pickingId)

+ 6 - 6
src/mol-repr/volume/isosurface-mesh.ts

@@ -6,7 +6,7 @@
  */
 
 import { VolumeData } from 'mol-model/volume'
-import { VolumeVisual, VolumeRepresentation } from './index';
+import { VolumeVisual, VolumeRepresentation } from './representation';
 import { createMeshRenderObject } from 'mol-gl/render-object';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
@@ -14,11 +14,12 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { createRenderableState, Theme } from 'mol-geo/geometry/geometry';
+import { createRenderableState } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { VisualUpdateState } from 'mol-repr/util';
-import { RepresentationContext, VisualContext } from 'mol-repr';
+import { RepresentationContext, VisualContext } from 'mol-repr/representation';
+import { ThemeProps, Theme } from 'mol-theme/theme';
 
 interface VolumeIsosurfaceProps {
     isoValueAbsolute: number
@@ -72,16 +73,15 @@ export function IsosurfaceRepresentation(): VolumeRepresentation<IsosurfaceProps
     const volumeRepr = VolumeRepresentation(IsosurfaceVisual)
     return {
         label: 'Isosurface',
-        params: IsosurfaceParams,
         get renderObjects() {
             return [ ...volumeRepr.renderObjects ]
         },
         get props() {
             return { ...volumeRepr.props }
         },
-        createOrUpdate: (ctx: RepresentationContext, props: Partial<IsosurfaceProps> = {}, volume?: VolumeData) => {
+        createOrUpdate: (ctx: RepresentationContext, props: Partial<IsosurfaceProps> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) => {
             currentProps = Object.assign({}, DefaultIsosurfaceProps, currentProps, props)
-            return volumeRepr.createOrUpdate(ctx, currentProps, volume)
+            return volumeRepr.createOrUpdate(ctx, currentProps, themeProps, volume)
         },
         getLoci: (pickingId: PickingId) => {
             return volumeRepr.getLoci(pickingId)

+ 25 - 0
src/mol-repr/volume/registry.ts

@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { RepresentationProvider, RepresentationRegistry } from '../representation';
+import { VolumeData } from 'mol-model/volume';
+
+export class VolumeRepresentationRegistry extends RepresentationRegistry<VolumeData> {
+    constructor() {
+        super()
+        Object.keys(BuiltInVolumeRepresentations).forEach(name => {
+            const p = (BuiltInVolumeRepresentations as { [k: string]: RepresentationProvider<VolumeData, any> })[name]
+            this.add(name, p.factory, p.params)
+        })
+    }
+}
+
+export const BuiltInVolumeRepresentations = {
+    // TODO
+}
+export type BuiltInVolumeRepresentationsName = keyof typeof BuiltInVolumeRepresentations
+export const BuiltInVolumeRepresentationsNames = Object.keys(BuiltInVolumeRepresentations)
+export const BuiltInVolumeRepresentationsOptions = BuiltInVolumeRepresentationsNames.map(n => [n, n] as [BuiltInVolumeRepresentationsName, string])

+ 9 - 5
src/mol-repr/volume/index.ts → src/mol-repr/volume/representation.ts

@@ -5,10 +5,10 @@
  */
 
 import { Task } from 'mol-task'
-import { RepresentationProps, Representation, Visual, RepresentationContext, VisualContext } from '..';
+import { RepresentationProps, Representation, Visual, RepresentationContext, VisualContext, RepresentationProvider } from '../representation';
 import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
-import { Geometry, updateRenderableState, Theme, createTheme } from 'mol-geo/geometry/geometry';
+import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
@@ -19,6 +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';
 
 export interface VolumeVisual<P extends RepresentationProps = {}> extends Visual<VolumeData, P> { }
 
@@ -134,6 +135,10 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr
 
 export interface VolumeRepresentation<P extends RepresentationProps = {}> extends Representation<VolumeData, P> { }
 
+export type VolumeRepresentationProvider<P extends PD.Params> = RepresentationProvider<VolumeData, P>
+
+//
+
 export const VolumeParams = {
     ...Geometry.Params,
     isoValueAbsolute: PD.Range('Iso Value Absolute', '', 0.22, -1, 1, 0.01),
@@ -148,9 +153,9 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
     let _theme: Theme
     let busy = false
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, volumeData?: VolumeData) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, volumeData?: VolumeData) {
         _props = Object.assign({}, DefaultVolumeProps, _props, props)
-        _theme = createTheme(_props)
+        _theme = createTheme(ctx, _props, themeProps, {}, _theme)
 
         return Task.create('VolumeRepresentation.create', async runtime => {
             // TODO queue it somehow
@@ -173,7 +178,6 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
 
     return {
         label: 'Volume',
-        params: VolumeParams,
         get renderObjects() {
             return visual && visual.renderObject ? [ visual.renderObject ] : []
         },

+ 82 - 107
src/mol-theme/color.ts

@@ -4,26 +4,25 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Color, ColorMap } from 'mol-util/color';
-import { Structure } from 'mol-model/structure';
+import { Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { ColorType } from 'mol-geo/geometry/color-data';
-
-import { ElementIndexColorTheme } from './color/element-index';
-import { CarbohydrateSymbolColorTheme } from './color/carbohydrate-symbol';
-import { ChainIdColorTheme } from './color/chain-id';
-import { ElementSymbolColorTheme } from './color/element-symbol';
-import { UnitIndexColorTheme } from './color/unit-index';
-import { UniformColorTheme } from './color/uniform';
-import { CrossLinkColorTheme } from './color/cross-link';
-import { ShapeGroupColorTheme } from './color/shape-group';
-import { CustomColorTheme } from './color/custom';
-import { ResidueNameColorTheme } from './color/residue-name';
-import { SequenceIdColorTheme } from './color/sequence-id';
-import { SecondaryStructureColorTheme } from './color/secondary-structure';
-import { MoleculeTypeColorTheme } from './color/molecule-type';
-import { PolymerIndexColorTheme } from './color/polymer-index';
-import { ColorMatplotlib, ColorBrewer, ColorOther } from 'mol-util/color/tables';
+import { CarbohydrateSymbolColorThemeProvider } from './color/carbohydrate-symbol';
+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 { ChainIdColorThemeProvider } from './color/chain-id';
+import { CrossLinkColorThemeProvider } from './color/cross-link';
+import { ElementIndexColorThemeProvider } from './color/element-index';
+import { ElementSymbolColorThemeProvider } from './color/element-symbol';
+import { MoleculeTypeColorThemeProvider } from './color/molecule-type';
+import { PolymerIndexColorThemeProvider } from './color/polymer-index';
+import { ResidueNameColorThemeProvider } from './color/residue-name';
+import { SecondaryStructureColorThemeProvider } from './color/secondary-structure';
+import { SequenceIdColorThemeProvider } from './color/sequence-id';
+import { ShapeGroupColorThemeProvider } from './color/shape-group';
+import { UnitIndexColorThemeProvider } from './color/unit-index';
 
 export type LocationColor = (location: Location, isSecondary: boolean) => Color
 
@@ -37,29 +36,6 @@ export function ScaleLegend(minLabel: string, maxLabel: string, colors: Color[])
     return { kind: 'scale-legend', minLabel, maxLabel, colors }
 }
 
-export type ColorScaleName = (
-    'default' |
-    keyof typeof ColorBrewer | keyof typeof ColorMatplotlib | keyof typeof ColorOther
-)
-export const ColorScaleNames = [
-    'default',
-    ...Object.keys(ColorBrewer), ...Object.keys(ColorMatplotlib), ...Object.keys(ColorOther)
-]
-export const ColorScaleOptions = ColorScaleNames.map(n => [n, n] as [ColorScaleName, string])
-
-export function getColorScaleFromName(name: string) {
-    if (name === 'default') {
-        return
-    } else if (name in ColorBrewer) {
-        return ColorBrewer[name as keyof typeof ColorBrewer]
-    } else if (name in ColorMatplotlib) {
-        return ColorMatplotlib[name as keyof typeof ColorMatplotlib]
-    } else if (name in ColorOther) {
-        return ColorOther[name as keyof typeof ColorOther]
-    }
-    console.warn(`unknwon color list named '${name}'`)
-}
-
 export interface TableLegend {
     kind: 'table-legend'
     table: [ string, Color ][]
@@ -68,77 +44,76 @@ export function TableLegend(table: [ string, Color ][]): TableLegend {
     return { kind: 'table-legend', table }
 }
 
-export interface ColorThemeFeatures {
-    /** Does allow providing a structure object */
-    structure?: boolean
-    /** Does allow providing a volume object */
-    volume?: boolean
-    /** Does allow providing a list of colors (for creating a scale) */
-    list?: boolean
-    /** Does allow providing a map of colors */
-    map?: boolean
-    /** Does allow providing the boundaries for the scale */
-    domain?: boolean
-    /** Does allow providing a single/special color value */
-    value?: boolean
-}
+export type ColorThemeProps = { [k: string]: any }
 
-export interface ColorTheme {
-    features: ColorThemeFeatures
-    granularity: ColorType
-    color: LocationColor
-    description?: string
-    legend?: ScaleLegend | TableLegend
+export { ColorTheme }
+interface ColorTheme<P extends ColorThemeProps = {}> {
+    readonly granularity: ColorType
+    readonly color: LocationColor
+    readonly props: Readonly<P>
+    readonly description?: string
+    readonly legend?: Readonly<ScaleLegend | TableLegend>
 }
+namespace ColorTheme {
+    export type Props = { [k: string]: any }
+    export const Empty = UniformColorTheme({}, { value: Color(0xCCCCCC) })
 
-export function ColorTheme(props: ColorThemeProps): ColorTheme {
-    switch (props.name) {
-        case 'carbohydrate-symbol': return CarbohydrateSymbolColorTheme(props)
-        case 'chain-id': return ChainIdColorTheme(props)
-        case 'cross-link': return CrossLinkColorTheme(props)
-        case 'custom': return CustomColorTheme(props)
-        case 'element-index': return ElementIndexColorTheme(props)
-        case 'element-symbol': return ElementSymbolColorTheme(props)
-        case 'molecule-type': return MoleculeTypeColorTheme(props)
-        case 'polymer-index': return PolymerIndexColorTheme(props)
-        case 'residue-name': return ResidueNameColorTheme(props)
-        case 'secondary-structure': return SecondaryStructureColorTheme(props)
-        case 'sequence-id': return SequenceIdColorTheme(props)
-        case 'shape-group': return ShapeGroupColorTheme(props)
-        case 'unit-index': return UnitIndexColorTheme(props)
-        case 'uniform': return UniformColorTheme(props)
+    export function areEqual(themeA: ColorTheme, themeB: ColorTheme) {
+        return themeA === themeB && deepEqual(themeA.props, themeB.props)
     }
-}
 
-export interface ColorThemeProps {
-    name: ColorThemeName
-    domain?: [number, number]
-    value?: Color
-    list?: Color[]
-    map?: ColorMap<any>
-    structure?: Structure
-    color?: LocationColor
-    granularity?: ColorType,
-    description?: string,
-    legend?: ScaleLegend | TableLegend
+    export interface Provider<P extends PD.Params> {
+        readonly factory: (ctx: ThemeDataContext, props: PD.DefaultValues<P>) => ColorTheme<PD.DefaultValues<P>>
+        readonly params: (ctx: ThemeDataContext) => P
+    }
+
+    export class Registry {
+        private _list: { name: string, provider: Provider<any> }[] = []
+        private _map = new Map<string, Provider<any>>()
+
+        constructor() {
+            Object.keys(BuiltInColorThemes).forEach(name => {
+                const p = (BuiltInColorThemes as { [k: string]: Provider<any> })[name]
+                this.add(name, p.factory, p.params)
+            })
+        }
+
+        add<P extends PD.Params>(name: string, factory: Provider<P>['factory'], params: Provider<P>['params']) {
+            const provider = { factory, params } as Provider<P>
+            this._list.push({ name, provider })
+            this._map.set(name, provider)
+        }
+
+        get(id: string) {
+            return this._map.get(id)
+        }
+
+        create(id: string, ctx: ThemeDataContext, props = {}) {
+            const provider = this.get(id)
+            return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.params(ctx)), ...props }) : Empty
+        }
+
+        get list() {
+            return this._list
+        }
+    }
 }
 
-export const ColorThemeInfo = {
-    'carbohydrate-symbol': {},
-    'chain-id': {},
-    'cross-link': {},
-    'custom': {},
-    'element-index': {},
-    'element-symbol': {},
-    'molecule-type': {},
-    'polymer-index': {},
-    'residue-name': {},
-    'secondary-structure': {},
-    'sequence-id': {},
-    'shape-group': {},
-    'unit-index': {},
-    'uniform': {},
+export const BuiltInColorThemes = {
+    'carbohydrate-symbol': CarbohydrateSymbolColorThemeProvider,
+    'chain-id': ChainIdColorThemeProvider,
+    'cross-link': CrossLinkColorThemeProvider,
+    'element-index': ElementIndexColorThemeProvider,
+    'element-symbol': ElementSymbolColorThemeProvider,
+    'molecule-type': MoleculeTypeColorThemeProvider,
+    'polymer-index': PolymerIndexColorThemeProvider,
+    'residue-name': ResidueNameColorThemeProvider,
+    'secondary-structure': SecondaryStructureColorThemeProvider,
+    'sequence-id': SequenceIdColorThemeProvider,
+    'shape-group': ShapeGroupColorThemeProvider,
+    'unit-index': UnitIndexColorThemeProvider,
+    'uniform': UniformColorThemeProvider,
 }
-export type ColorThemeName = keyof typeof ColorThemeInfo
-export const ColorThemeNames = Object.keys(ColorThemeInfo)
-export const ColorThemeOptions = ColorThemeNames.map(n => [n, n] as [ColorThemeName, string])
+export type BuiltInColorThemeName = keyof typeof BuiltInColorThemes
+export const BuiltInColorThemeNames = Object.keys(BuiltInColorThemes)
+export const BuiltInColorThemeOptions = BuiltInColorThemeNames.map(n => [n, n] as [BuiltInColorThemeName, string])

+ 26 - 5
src/mol-theme/color/carbohydrate-symbol.ts

@@ -8,17 +8,34 @@ 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 { ColorThemeProps, ColorTheme, LocationColor, TableLegend } from '../color';
+import { ColorTheme, LocationColor, TableLegend } from '../color';
 import { Color } from 'mol-util/color';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Assigns colors according to the Symbol Nomenclature for Glycans (SNFG).'
 
-export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme {
+// name: ColorThemeName
+// domain?: [number, number]
+// value?: Color
+// list?: Color[]
+// map?: ColorMap<any>
+
+export const CarbohydrateSymbolColorThemeParams = {
+    // domain: PD.Interval('Color Domain', '', [0, 1]),
+    // value: PD.Color('Color Value', '', DefaultColor),
+}
+export function getCarbohydrateSymbolColorThemeParams(ctx: ThemeDataContext) {
+    return CarbohydrateSymbolColorThemeParams // TODO return copy
+}
+export type CarbohydrateSymbolColorThemeProps = PD.DefaultValues<typeof CarbohydrateSymbolColorThemeParams>
+
+export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: CarbohydrateSymbolColorThemeProps): ColorTheme<CarbohydrateSymbolColorThemeProps> {
     let color: LocationColor
 
-    if (props.structure) {
-        const { elements, getElementIndex, getAnomericCarbon } = props.structure.carbohydrates
+    if (ctx.structure) {
+        const { elements, getElementIndex, getAnomericCarbon } = ctx.structure.carbohydrates
 
         const getColor = (unit: Unit, index: ElementIndex) => {
             const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index[index]
@@ -47,10 +64,14 @@ export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme
     }
 
     return {
-        features: {},
         granularity: 'group',
         color: color,
+        props: props,
         description: Description,
         legend: TableLegend(MonosaccharidesColorTable)
     }
+}
+
+export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<typeof CarbohydrateSymbolColorThemeParams> = {
+    factory: CarbohydrateSymbolColorTheme, params: getCarbohydrateSymbolColorThemeParams
 }

+ 21 - 10
src/mol-theme/color/chain-id.ts

@@ -8,11 +8,22 @@ import { Unit, StructureProperties, StructureElement, Link } from 'mol-model/str
 
 import { ColorScale, Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
-import { ColorThemeProps, ColorTheme, LocationColor } from '../color';
+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/scale';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every chain a color based on its `asym_id` value.'
 
+export const ChainIdColorThemeParams = {
+    list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions),
+}
+export function getChainIdColorThemeParams(ctx: ThemeDataContext) {
+    return ChainIdColorThemeParams // TODO return copy
+}
+export type ChainIdColorThemeProps = PD.DefaultValues<typeof ChainIdColorThemeParams>
+
 function getAsymId(unit: Unit): StructureElement.Property<string> {
     switch (unit.kind) {
         case Unit.Kind.Atomic:
@@ -23,14 +34,13 @@ function getAsymId(unit: Unit): StructureElement.Property<string> {
     }
 }
 
-export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
+export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThemeProps): ColorTheme<ChainIdColorThemeProps> {
     let color: LocationColor
-    const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
-    // const table: [string, Color][] = []
+    const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
-    if (props.structure) {
+    if (ctx.structure) {
         const l = StructureElement.create()
-        const { models } = props.structure
+        const { models } = ctx.structure
         const asymIdSerialMap = new Map<string, number>()
         let j = 0
         for (let i = 0, il = models.length; i <il; ++i) {
@@ -44,8 +54,6 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
         scale.setDomain(0, asymIdSerialMap.size - 1)
         const scaleColor = scale.color
 
-        // asymIdSerialMap.forEach((v, k) => table.push([k, scaleColor(v)]))
-
         color = (location: Location): Color => {
             if (StructureElement.isLocation(location)) {
                 const asym_id = getAsymId(location.unit)
@@ -63,11 +71,14 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { structure: true, list: true },
         granularity: 'group',
         color,
+        props,
         description: Description,
-        // legend: scale ? TableLegend(table) : undefined
         legend: scale ? scale.legend : undefined
     }
+}
+
+export const ChainIdColorThemeProvider: ColorTheme.Provider<typeof ChainIdColorThemeParams> = {
+    factory: ChainIdColorTheme, params: getChainIdColorThemeParams
 }

+ 23 - 9
src/mol-theme/color/cross-link.ts

@@ -8,14 +8,24 @@ import { Link } from 'mol-model/structure';
 
 import { Color, ColorScale } from 'mol-util/color';
 import { Location } from 'mol-model/location';
-import { ColorThemeProps, ColorTheme, LocationColor } from '../color';
+import { ColorTheme, LocationColor } from '../color';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { ColorBrewer } from 'mol-util/color/tables';
-import { defaults } from 'mol-util';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
+import { ColorListName, ColorListOptions } from 'mol-util/color/scale';
 
 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('Color Domain', '', [-10, 10]),
+    list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions),
+}
+export function getCrossLinkColorThemeParams(ctx: ThemeDataContext) {
+    return CrossLinkColorThemeParams // TODO return copy
+}
+export type CrossLinkColorThemeProps = PD.DefaultValues<typeof CrossLinkColorThemeParams>
+
 const distVecA = Vec3.zero(), distVecB = Vec3.zero()
 function linkDistance(link: Link.Location) {
     link.aUnit.conformation.position(link.aIndex, distVecA)
@@ -23,15 +33,15 @@ function linkDistance(link: Link.Location) {
     return Vec3.distance(distVecA, distVecB)
 }
 
-export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
+export function CrossLinkColorTheme(ctx: ThemeDataContext, props: CrossLinkColorThemeProps): ColorTheme<CrossLinkColorThemeProps> {
     let color: LocationColor
     let scale: ColorScale | undefined = undefined
 
-    if (props.structure) {
-        const crosslinks = props.structure.crossLinkRestraints
+    if (ctx.structure) {
+        const crosslinks = ctx.structure.crossLinkRestraints
         scale = ColorScale.create({
-            domain: defaults(props.domain, [ -10, 10 ]),
-            list: defaults(props.list, ColorBrewer.RdYlBu)
+            domain: props.domain,
+            listOrName: props.list
         })
         const scaleColor = scale.color
 
@@ -49,10 +59,14 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { list: true, domain: true, structure: true },
         granularity: 'group',
         color,
+        props,
         description: Description,
         legend: scale ? scale.legend : undefined
     }
+}
+
+export const CrossLinkColorThemeProvider: ColorTheme.Provider<typeof CrossLinkColorThemeParams> = {
+    factory: CrossLinkColorTheme, params: getCrossLinkColorThemeParams
 }

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

@@ -1,22 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { Color } from 'mol-util/color';
-import { ColorThemeProps, ColorTheme } from '../color';
-import { defaults } from 'mol-util';
-
-const DefaultColor = Color(0xCCCCCC)
-
-export function CustomColorTheme(props: ColorThemeProps): ColorTheme {
-    const value = defaults(props.value, DefaultColor)
-    return {
-        features: {},
-        granularity: defaults(props.granularity, 'uniform'),
-        color: defaults(props.color, () => value),
-        description: props.description,
-        legend: props.legend
-    }
-}

+ 21 - 6
src/mol-theme/color/element-index.ts

@@ -8,17 +8,28 @@ import { ColorScale, 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 { ColorThemeProps, ColorTheme, LocationColor } from '../color';
+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/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 function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
+export const ElementIndexColorThemeParams = {
+    list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions),
+}
+export function getElementIndexColorThemeParams(ctx: ThemeDataContext) {
+    return ElementIndexColorThemeParams // TODO return copy
+}
+export type ElementIndexColorThemeProps = PD.DefaultValues<typeof ElementIndexColorThemeParams>
+
+export function ElementIndexColorTheme(ctx: ThemeDataContext, props: ElementIndexColorThemeProps): ColorTheme<ElementIndexColorThemeProps> {
     let color: LocationColor
-    let scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
+    let scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
-    if (props.structure) {
-        const { units } = props.structure
+    if (ctx.structure) {
+        const { units } = ctx.structure
         const unitCount = units.length
         const cummulativeElementCount = new Map<number, number>()
         const unitIdIndex = new Map<number, number>()
@@ -48,10 +59,14 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { structure: true, list: true },
         granularity: 'groupInstance',
         color,
+        props,
         description: Description,
         legend: scale ? scale.legend : undefined
     }
+}
+
+export const ElementIndexColorThemeProvider: ColorTheme.Provider<typeof ElementIndexColorThemeParams> = {
+    factory: ElementIndexColorTheme, params: getElementIndexColorThemeParams
 }

+ 15 - 3
src/mol-theme/color/element-symbol.ts

@@ -8,7 +8,9 @@ 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 { ColorThemeProps, ColorTheme, TableLegend } from '../color';
+import { ColorTheme, TableLegend } from '../color';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF)
 export const ElementSymbolColors = ColorMap({
@@ -18,12 +20,18 @@ export const ElementSymbolColors = ColorMap({
 const DefaultElementSymbolColor = Color(0xFFFFFF)
 const Description = 'Assigns a color to every atom according to its chemical element.'
 
+export const ElementSymbolColorThemeParams = {}
+export function getElementSymbolColorThemeParams(ctx: ThemeDataContext) {
+    return ElementSymbolColorThemeParams // TODO return copy
+}
+export type ElementSymbolColorThemeProps = PD.DefaultValues<typeof ElementSymbolColorThemeParams>
+
 export function elementSymbolColor(element: ElementSymbol): Color {
     const c = (ElementSymbolColors as { [k: string]: Color })[element];
     return c === undefined ? DefaultElementSymbolColor : c
 }
 
-export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme {
+export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: ElementSymbolColorThemeProps): ColorTheme<ElementSymbolColorThemeProps> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             if (Unit.isAtomic(location.unit)) {
@@ -40,12 +48,16 @@ export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: {},
         granularity: 'group',
         color,
+        props,
         description: Description,
         legend: TableLegend(Object.keys(ElementSymbolColors).map(name => {
             return [name, (ElementSymbolColors as any)[name] as Color] as [string, Color]
         }))
     }
+}
+
+export const ElementSymbolColorThemeProvider: ColorTheme.Provider<typeof ElementSymbolColorThemeParams> = {
+    factory: ElementSymbolColorTheme, params: getElementSymbolColorThemeParams
 }

+ 15 - 3
src/mol-theme/color/molecule-type.ts

@@ -7,9 +7,11 @@
 import { Color, ColorMap } from 'mol-util/color';
 import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
-import { ColorThemeProps, ColorTheme, TableLegend } from '../color';
+import { ColorTheme, TableLegend } 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';
 
 const MoleculeTypeColors = ColorMap({
     water: 0x386cb0,
@@ -24,6 +26,12 @@ const MoleculeTypeColors = ColorMap({
 const DefaultMoleculeTypeColor = Color(0xffff99)
 const Description = 'Assigns a color based on the molecule type of a residue.'
 
+export const MoleculeTypeColorThemeParams = {}
+export function getMoleculeTypeColorThemeParams(ctx: ThemeDataContext) {
+    return MoleculeTypeColorThemeParams // TODO return copy
+}
+export type MoleculeTypeColorThemeProps = PD.DefaultValues<typeof MoleculeTypeColorThemeParams>
+
 export function moleculeTypeColor(unit: Unit, element: ElementIndex): Color {
     const moleculeType = getElementMoleculeType(unit, element)
     switch (moleculeType) {
@@ -38,7 +46,7 @@ export function moleculeTypeColor(unit: Unit, element: ElementIndex): Color {
     return DefaultMoleculeTypeColor
 }
 
-export function MoleculeTypeColorTheme(props: ColorThemeProps): ColorTheme {
+export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: MoleculeTypeColorThemeProps): ColorTheme<MoleculeTypeColorThemeProps> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             return moleculeTypeColor(location.unit, location.element)
@@ -49,12 +57,16 @@ export function MoleculeTypeColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: {},
         granularity: 'group',
         color,
+        props,
         description: Description,
         legend: TableLegend(Object.keys(MoleculeTypeColors).map(name => {
             return [name, (MoleculeTypeColors as any)[name] as Color] as [string, Color]
         }).concat([[ 'Other/unknown', DefaultMoleculeTypeColor ]]))
     }
+}
+
+export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<typeof MoleculeTypeColorThemeParams> = {
+    factory: MoleculeTypeColorTheme, params: getMoleculeTypeColorThemeParams
 }

+ 21 - 6
src/mol-theme/color/polymer-index.ts

@@ -7,17 +7,28 @@
 import { ColorScale, Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { StructureElement, Link } from 'mol-model/structure';
-import { ColorTheme, ColorThemeProps, LocationColor } from '../color';
+import { ColorTheme, LocationColor } from '../color';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
+import { ColorListName, ColorListOptions } from 'mol-util/color/scale';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every polymer a unique color based on the position (index) of the polymer in the list of polymers in the structure.'
 
-export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
+export const PolymerIndexColorThemeParams = {
+    list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions),
+}
+export function getPolymerIndexColorThemeParams(ctx: ThemeDataContext) {
+    return PolymerIndexColorThemeParams // TODO return copy
+}
+export type PolymerIndexColorThemeProps = PD.DefaultValues<typeof PolymerIndexColorThemeParams>
+
+export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PolymerIndexColorThemeProps): ColorTheme<PolymerIndexColorThemeProps> {
     let color: LocationColor
-    const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
+    const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
-    if (props.structure) {
-        const { units } = props.structure
+    if (ctx.structure) {
+        const { units } = ctx.structure
         let polymerCount = 0
         for (let i = 0, il = units.length; i <il; ++i) {
             if (units[i].polymerElements.length > 0) ++polymerCount
@@ -45,10 +56,14 @@ export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { structure: true, list: true },
         granularity: 'instance',
         color,
+        props,
         description: Description,
         legend: scale ? scale.legend : undefined
     }
+}
+
+export const PolymerIndexColorThemeProvider: ColorTheme.Provider<typeof PolymerIndexColorThemeParams> = {
+    factory: PolymerIndexColorTheme, params: getPolymerIndexColorThemeParams
 }

+ 15 - 3
src/mol-theme/color/residue-name.ts

@@ -7,7 +7,9 @@
 import { Color, ColorMap } from 'mol-util/color';
 import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
-import { ColorThemeProps, ColorTheme, TableLegend } from '../color';
+import { ColorTheme, TableLegend } from '../color';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
 
 // protein colors from Jmol http://jmol.sourceforge.net/jscolors/
 const ResidueNameColors = ColorMap({
@@ -59,6 +61,12 @@ const ResidueNameColors = ColorMap({
 const DefaultResidueNameColor = Color(0xFF00FF)
 const Description = 'Assigns a color to every residue according to its name.'
 
+export const ResidueNameColorThemeParams = {}
+export function getResidueNameColorThemeParams(ctx: ThemeDataContext) {
+    return ResidueNameColorThemeParams // TODO return copy
+}
+export type ResidueNameColorThemeProps = PD.DefaultValues<typeof ResidueNameColorThemeParams>
+
 export function residueNameColor(residueName: string): Color {
     const c = (ResidueNameColors as { [k: string]: Color })[residueName];
     return c === undefined ? DefaultResidueNameColor : c
@@ -84,7 +92,7 @@ function getCoarseCompId(unit: Unit.Spheres | Unit.Gaussians, element: ElementIn
     }
 }
 
-export function ResidueNameColorTheme(props: ColorThemeProps): ColorTheme {
+export function ResidueNameColorTheme(ctx: ThemeDataContext, props: ResidueNameColorThemeProps): ColorTheme<ResidueNameColorThemeProps> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             if (Unit.isAtomic(location.unit)) {
@@ -105,12 +113,16 @@ export function ResidueNameColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: {},
         granularity: 'group',
         color,
+        props,
         description: Description,
         legend: TableLegend(Object.keys(ResidueNameColors).map(name => {
             return [name, (ResidueNameColors as any)[name] as Color] as [string, Color]
         }).concat([[ 'Unknown', DefaultResidueNameColor ]]))
     }
+}
+
+export const ResidueNameColorThemeProvider: ColorTheme.Provider<typeof ResidueNameColorThemeParams> = {
+    factory: ResidueNameColorTheme, params: getResidueNameColorThemeParams
 }

+ 15 - 3
src/mol-theme/color/secondary-structure.ts

@@ -7,9 +7,11 @@
 import { Color, ColorMap } from 'mol-util/color';
 import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
-import { ColorThemeProps, ColorTheme, TableLegend } from '../color';
+import { ColorTheme, TableLegend } 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';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (shapely)
 const SecondaryStructureColors = ColorMap({
@@ -29,6 +31,12 @@ const SecondaryStructureColors = ColorMap({
 const DefaultSecondaryStructureColor = Color(0x808080)
 const Description = 'Assigns a color based on the type of secondary structure and basic molecule type.'
 
+export const SecondaryStructureColorThemeParams = {}
+export function getSecondaryStructureColorThemeParams(ctx: ThemeDataContext) {
+    return SecondaryStructureColorThemeParams // TODO return copy
+}
+export type SecondaryStructureColorThemeProps = PD.DefaultValues<typeof SecondaryStructureColorThemeParams>
+
 export function secondaryStructureColor(unit: Unit, element: ElementIndex): Color {
     let secStrucType = SecondaryStructureType.create(SecondaryStructureType.Flag.None)
     if (Unit.isAtomic(unit)) {
@@ -61,7 +69,7 @@ export function secondaryStructureColor(unit: Unit, element: ElementIndex): Colo
     return DefaultSecondaryStructureColor
 }
 
-export function SecondaryStructureColorTheme(props: ColorThemeProps): ColorTheme {
+export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: SecondaryStructureColorThemeProps): ColorTheme<SecondaryStructureColorThemeProps> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             return secondaryStructureColor(location.unit, location.element)
@@ -72,12 +80,16 @@ export function SecondaryStructureColorTheme(props: ColorThemeProps): ColorTheme
     }
 
     return {
-        features: {},
         granularity: 'group',
         color,
+        props,
         description: Description,
         legend: TableLegend(Object.keys(SecondaryStructureColors).map(name => {
             return [name, (SecondaryStructureColors as any)[name] as Color] as [string, Color]
         }).concat([[ 'Other', DefaultSecondaryStructureColor ]]))
     }
+}
+
+export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<typeof SecondaryStructureColorThemeParams> = {
+    factory: SecondaryStructureColorTheme, params: getSecondaryStructureColorThemeParams
 }

+ 21 - 12
src/mol-theme/color/sequence-id.ts

@@ -8,13 +8,22 @@ import { Unit, StructureElement, Link, ElementIndex } from 'mol-model/structure'
 
 import { ColorScale, Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
-import { ColorThemeProps, ColorTheme } from '../color';
-import { ColorOther } from 'mol-util/color/tables';
-import { defaults } from 'mol-util';
+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/scale';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every polymer residue a color based on its `seq_id` value.'
 
+export const SequenceIdColorThemeParams = {
+    list: PD.Select<ColorListName>('Color Scale', '', 'rainbow', ColorListOptions),
+}
+export function getSequenceIdColorThemeParams(ctx: ThemeDataContext) {
+    return SequenceIdColorThemeParams // TODO return copy
+}
+export type SequenceIdColorThemeProps = PD.DefaultValues<typeof SequenceIdColorThemeParams>
+
 function getSeqId(unit: Unit, element: ElementIndex): number {
     const { model } = unit
     switch (unit.kind) {
@@ -55,15 +64,12 @@ function getSequenceLength(unit: Unit, element: ElementIndex) {
     return model.sequence.byEntityKey[entityIndex].sequence.sequence.length
 }
 
-export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme {
-    const p = {
-        ...props,
-        list: defaults(props.list, ColorOther.rainbow),
+export function SequenceIdColorTheme(ctx: ThemeDataContext, props: SequenceIdColorThemeProps): ColorTheme<SequenceIdColorThemeProps> {
+    const scale = ColorScale.create({
+        listOrName: props.list,
         minLabel: 'Start',
         maxLabel: 'End',
-    }
-
-    const scale = ColorScale.create(p)
+    })
     const color = (location: Location): Color => {
         if (StructureElement.isLocation(location)) {
             const { unit, element } = location
@@ -84,11 +90,14 @@ export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { list: true },
         granularity: 'group',
         color,
+        props,
         description: Description,
-        // legend: scale ? TableLegend(table) : undefined
         legend: scale ? scale.legend : undefined
     }
+}
+
+export const SequenceIdColorThemeProvider: ColorTheme.Provider<typeof SequenceIdColorThemeParams> = {
+    factory: SequenceIdColorTheme, params: getSequenceIdColorThemeParams
 }

+ 17 - 5
src/mol-theme/color/shape-group.ts

@@ -4,16 +4,24 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ColorTheme, ColorThemeProps } from '../color';
+import { ColorTheme } from '../color';
 import { Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { Shape } from 'mol-model/shape';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
 
 const DefaultColor = Color(0xCCCCCC)
+const Description = 'Assigns colors as defined by the shape object.'
 
-export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme {
+export const ShapeGroupColorThemeParams = {}
+export function getShapeGroupColorThemeParams(ctx: ThemeDataContext) {
+    return ShapeGroupColorThemeParams // TODO return copy
+}
+export type ShapeGroupColorThemeProps = PD.DefaultValues<typeof ShapeGroupColorThemeParams>
+
+export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: ShapeGroupColorThemeProps): ColorTheme<ShapeGroupColorThemeProps> {
     return {
-        features: {},
         granularity: 'group',
         color: (location: Location): Color => {
             if (Shape.isLocation(location)) {
@@ -21,7 +29,11 @@ export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme {
             }
             return DefaultColor
         },
-        description: props.description,
-        legend: props.legend
+        props,
+        description: Description
     }
+}
+
+export const ShapeGroupColorThemeProvider: ColorTheme.Provider<typeof ShapeGroupColorThemeParams> = {
+    factory: ShapeGroupColorTheme, params: getShapeGroupColorThemeParams
 }

+ 17 - 3
src/mol-theme/color/uniform.ts

@@ -4,20 +4,34 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ColorTheme, ColorThemeProps, TableLegend } from '../color';
+import { ColorTheme, TableLegend } from '../color';
 import { Color } from 'mol-util/color';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives everything the same, uniform color.'
 
-export function UniformColorTheme(props: ColorThemeProps): ColorTheme {
+export const UniformColorThemeParams = {
+    value: PD.Color('Color Value', '', DefaultColor),
+}
+export function getUniformColorThemeParams(ctx: ThemeDataContext) {
+    return UniformColorThemeParams // TODO return copy
+}
+export type UniformColorThemeProps = PD.DefaultValues<typeof UniformColorThemeParams>
+
+export function UniformColorTheme(ctx: ThemeDataContext, props: UniformColorThemeProps): ColorTheme<UniformColorThemeProps> {
     const color = props.value || DefaultColor
 
     return {
-        features: {},
         granularity: 'uniform',
         color: () => color,
+        props: props,
         description: Description,
         legend: TableLegend([['uniform', color]])
     }
+}
+
+export const UniformColorThemeProvider: ColorTheme.Provider<typeof UniformColorThemeParams> = {
+    factory: UniformColorTheme, params: getUniformColorThemeParams
 }

+ 21 - 6
src/mol-theme/color/unit-index.ts

@@ -7,17 +7,28 @@
 import { ColorScale, Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { StructureElement, Link } from 'mol-model/structure';
-import { ColorTheme, ColorThemeProps, LocationColor } from '../color';
+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/scale';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every unit (single chain or collection of single elements) a unique color based on the position (index) of the unit in the list of units in the structure.'
 
-export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme {
+export const UnitIndexColorThemeParams = {
+    list: PD.Select<ColorListName>('Color Scale', '', 'RdYlBu', ColorListOptions),
+}
+export function getUnitIndexColorThemeParams(ctx: ThemeDataContext) {
+    return UnitIndexColorThemeParams // TODO return copy
+}
+export type UnitIndexColorThemeProps = PD.DefaultValues<typeof UnitIndexColorThemeParams>
+
+export function UnitIndexColorTheme(ctx: ThemeDataContext, props: UnitIndexColorThemeProps): ColorTheme<UnitIndexColorThemeProps> {
     let color: LocationColor
-    const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
+    const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
-    if (props.structure) {
-        const { units } = props.structure
+    if (ctx.structure) {
+        const { units } = ctx.structure
         scale.setDomain(0, units.length - 1)
         const unitIdColor = new Map<number, Color>()
         for (let i = 0, il = units.length; i <il; ++i) {
@@ -37,10 +48,14 @@ export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { structure: true, list: true },
         granularity: 'instance',
         color,
+        props,
         description: Description,
         legend: scale ? scale.legend : undefined
     }
+}
+
+export const UnitIndexColorThemeProvider: ColorTheme.Provider<typeof UnitIndexColorThemeParams> = {
+    factory: UnitIndexColorTheme, params: getUnitIndexColorThemeParams
 }

+ 57 - 23
src/mol-theme/size.ts

@@ -4,35 +4,69 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Structure } from 'mol-model/structure';
 import { SizeType, LocationSize } from 'mol-geo/geometry/size-data';
+import { UniformSizeTheme, UniformSizeThemeProvider } from './size/uniform';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { ThemeDataContext } from 'mol-theme/theme';
+import { PhysicalSizeThemeProvider } from './size/physical';
+import { deepEqual } from 'mol-util';
 
-import { PhysicalSizeTheme } from './size/physical';
-import { UniformSizeTheme } from './size/uniform';
-
-export interface SizeTheme {
-    granularity: SizeType
-    size: LocationSize
+export { SizeTheme }
+interface SizeTheme<P extends SizeTheme.Props = {}> {
+    readonly granularity: SizeType
+    readonly size: LocationSize
+    readonly props: Readonly<P>
+    readonly description?: string
 }
+namespace SizeTheme {
+    export type Props = { [k: string]: any }
+    export const Empty = UniformSizeTheme({}, { value: 1 })
 
-export function SizeTheme(props: SizeThemeProps): SizeTheme {
-    switch (props.name) {
-        case 'physical': return PhysicalSizeTheme(props)
-        case 'uniform': return UniformSizeTheme(props)
+    export function areEqual(themeA: SizeTheme, themeB: SizeTheme) {
+        return themeA === themeB && deepEqual(themeA.props, themeB.props)
     }
-}
 
-export interface SizeThemeProps {
-    name: 'physical' | 'uniform'
-    value?: number
-    factor?: number
-    structure?: Structure
+    export interface Provider<P extends PD.Params> {
+        readonly factory: (ctx: ThemeDataContext, props: PD.DefaultValues<P>) => SizeTheme<PD.DefaultValues<P>>
+        readonly params: (ctx: ThemeDataContext) => P
+    }
+
+    export class Registry {
+        private _list: { name: string, provider: Provider<any> }[] = []
+        private _map = new Map<string, Provider<any>>()
+
+        constructor() {
+            Object.keys(BuiltInSizeThemes).forEach(name => {
+                const p = (BuiltInSizeThemes as { [k: string]: Provider<any> })[name]
+                this.add(name, p.factory, p.params)
+            })
+        }
+
+        add<P extends PD.Params>(name: string, factory: Provider<P>['factory'], params: Provider<P>['params']) {
+            const provider = { factory, params } as Provider<P>
+            this._list.push({ name, provider })
+            this._map.set(name, provider)
+        }
+
+        get(id: string) {
+            return this._map.get(id)
+        }
+
+        create(id: string, ctx: ThemeDataContext, props = {}) {
+            const provider = this.get(id)
+            return provider ? provider.factory(ctx, { ...PD.getDefaultValues(provider.params(ctx)), ...props }) : Empty
+        }
+
+        get list() {
+            return this._list
+        }
+    }
 }
 
-export const SizeThemeInfo = {
-    'physical': {},
-    'uniform': {}
+export const BuiltInSizeThemes = {
+    'physical': PhysicalSizeThemeProvider,
+    'uniform': UniformSizeThemeProvider
 }
-export type SizeThemeName = keyof typeof SizeThemeInfo
-export const SizeThemeNames = Object.keys(SizeThemeInfo)
-export const SizeThemeOptions = SizeThemeNames.map(n => [n, n] as [SizeThemeName, string])
+export type BuiltInSizeThemeName = keyof typeof BuiltInSizeThemes
+export const BuiltInSizeThemeNames = Object.keys(BuiltInSizeThemes)
+export const BuiltInSizeThemeOptions = BuiltInSizeThemeNames.map(n => [n, n] as [BuiltInSizeThemeName, string])

+ 20 - 8
src/mol-theme/size/physical.ts

@@ -6,11 +6,19 @@
 
 import { StructureElement, Unit, Link, ElementIndex } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
-import { SizeThemeProps, SizeTheme } from '../size';
+import { SizeTheme } from '../size';
 import { VdwRadius } from 'mol-model/structure/model/properties/atomic';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
 
 const DefaultSize = 1
-const DefaultFactor = 1
+const Description = 'Assigns a physical size.'
+
+export const PhysicalSizeThemeParams = {}
+export function getPhysicalSizeThemeParams(ctx: ThemeDataContext) {
+    return PhysicalSizeThemeParams // TODO return copy
+}
+export type PhysicalSizeThemeProps = PD.DefaultValues<typeof PhysicalSizeThemeParams>
 
 export function getPhysicalRadius(unit: Unit, element: ElementIndex): number {
     if (Unit.isAtomic(unit)) {
@@ -26,10 +34,8 @@ export function getPhysicalRadius(unit: Unit, element: ElementIndex): number {
  * Create attribute data with the physical size of an element,
  * i.e. vdw for atoms and radius for coarse spheres
  */
-export function PhysicalSizeTheme(props: SizeThemeProps): SizeTheme {
-    const factor = props.factor || DefaultFactor
-
-    function sizeFn(location: Location): number {
+export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PhysicalSizeThemeProps): SizeTheme<PhysicalSizeThemeProps> {
+    function size(location: Location): number {
         let size: number
         if (StructureElement.isLocation(location)) {
             size = getPhysicalRadius(location.unit, location.element)
@@ -38,11 +44,17 @@ export function PhysicalSizeTheme(props: SizeThemeProps): SizeTheme {
         } else {
             size = DefaultSize
         }
-        return factor * size
+        return size
     }
 
     return {
         granularity: 'group',
-        size: sizeFn
+        size,
+        props,
+        description: Description
     }
+}
+
+export const PhysicalSizeThemeProvider: SizeTheme.Provider<typeof PhysicalSizeThemeParams> = {
+    factory: PhysicalSizeTheme, params: getPhysicalSizeThemeParams
 }

+ 21 - 8
src/mol-theme/size/uniform.ts

@@ -4,18 +4,31 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { SizeTheme, SizeThemeProps } from '../size';
+import { SizeTheme } from '../size';
+import { ParamDefinition as PD } from 'mol-util/param-definition'
+import { ThemeDataContext } from 'mol-theme/theme';
 
-const DefaultSize = 1
-const DefaultFactor = 1
+const Description = 'Gives everything the same, uniform size.'
 
-export function UniformSizeTheme(props: SizeThemeProps): SizeTheme {
-    const value = props.value || DefaultSize
-    const factor = props.factor || DefaultFactor
-    const size = value * factor
+export const UniformSizeThemeParams = {
+    value: PD.Numeric('Size Value', '', 1, 0, 20, 0.1),
+}
+export function getUniformSizeThemeParams(ctx: ThemeDataContext) {
+    return UniformSizeThemeParams // TODO return copy
+}
+export type UniformSizeThemeProps = PD.DefaultValues<typeof UniformSizeThemeParams>
+
+export function UniformSizeTheme(ctx: ThemeDataContext, props: UniformSizeThemeProps): SizeTheme<UniformSizeThemeProps> {
+    const size = props.value
 
     return {
         granularity: 'uniform',
-        size: () => size
+        size: () => size,
+        props,
+        description: Description
     }
+}
+
+export const UniformSizeThemeProvider: SizeTheme.Provider<typeof UniformSizeThemeParams> = {
+    factory: UniformSizeTheme, params: getUniformSizeThemeParams
 }

+ 46 - 0
src/mol-theme/theme.ts

@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ColorTheme } from './color';
+import { SizeTheme } from './size';
+import { Structure } from 'mol-model/structure';
+import { VolumeData } from 'mol-model/volume';
+
+export interface ThemeRegistryContext {
+    colorThemeRegistry: ColorTheme.Registry
+    sizeThemeRegistry: SizeTheme.Registry
+}
+
+export interface ThemeDataContext {
+    [k: string]: any
+    structure?: Structure
+    volume?: VolumeData
+}
+
+export interface ThemeProps { color?: {}, size?: {} }
+
+export interface Theme {
+    color: ColorTheme
+    size: SizeTheme
+    // label: LabelTheme // TODO
+}
+
+type Props = { [k: string]: any }
+
+export function createTheme(ctx: ThemeRegistryContext, data: ThemeDataContext, props: Props, themeProps: ThemeProps = {}, 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)
+    }
+    return theme
+}

+ 29 - 7
src/mol-util/color/scale.ts

@@ -5,10 +5,32 @@
  */
 
 import { Color } from './color'
-import { ColorBrewer } from './tables'
+import { ColorBrewer, ColorMatplotlib, ColorOther } from './tables'
 import { ScaleLegend } from 'mol-theme/color';
 import { defaults } from 'mol-util';
 
+export type ColorListName = (
+    keyof typeof ColorBrewer | keyof typeof ColorMatplotlib | keyof typeof ColorOther
+)
+export const ColorListNames = [
+    ...Object.keys(ColorBrewer), ...Object.keys(ColorMatplotlib), ...Object.keys(ColorOther)
+]
+export const ColorListOptions = ColorListNames.map(n => [n, n] as [ColorListName, string])
+
+export function getColorListFromName(name: ColorListName) {
+    if (name in ColorBrewer) {
+        return ColorBrewer[name as keyof typeof ColorBrewer]
+    } else if (name in ColorMatplotlib) {
+        return ColorMatplotlib[name as keyof typeof ColorMatplotlib]
+    } else if (name in ColorOther) {
+        return ColorOther[name as keyof typeof ColorOther]
+    }
+    console.warn(`unknown color list named '${name}'`)
+    return ColorBrewer.RdYlBu
+}
+
+//
+
 export interface ColorScale {
     /** Returns hex color for given value */
     color: (value: number) => Color
@@ -22,20 +44,20 @@ export interface ColorScale {
     readonly legend: ScaleLegend
 }
 
-export const DefaultColorScale = {
+export const DefaultColorScaleProps = {
     domain: [0, 1],
     reverse: false,
-    list: ColorBrewer.RdYlBu,
+    listOrName: ColorBrewer.RdYlBu as Color[] | ColorListName,
     minLabel: '' as string | undefined,
     maxLabel: '' as string | undefined,
 }
-export type ColorScaleProps = Partial<typeof DefaultColorScale>
+export type ColorScaleProps = Partial<typeof DefaultColorScaleProps>
 
 export namespace ColorScale {
     export function create(props: ColorScaleProps): ColorScale {
-        // ensure that no undefined .list property exists so that the default assignment works
-        if (props.list === undefined) delete props.list
-        const { domain, reverse, list } = { ...DefaultColorScale, ...props }
+        const { domain, reverse, listOrName } = { ...DefaultColorScaleProps, ...props }
+        const list = typeof listOrName === 'string' ? getColorListFromName(listOrName) : listOrName
+
         const colors = reverse ? list.slice().reverse() : list
         const count1 = colors.length - 1
 

+ 10 - 2
src/mol-util/param-definition.ts

@@ -82,12 +82,20 @@ export namespace ParamDefinition {
         return { type: 'number', label, description, defaultValue, min, max, step }
     }
 
-    export type Any = /* ValueParam<any> | */ Select<any> | MultiSelect<any> | Boolean | Range | Text | Color | Numeric
+    export interface Interval extends Base<[number, number]> {
+        type: 'interval'
+    }
+    export function Interval(label: string, description: string, defaultValue: [number, number]): Interval {
+        return { type: 'interval', label, description, defaultValue }
+    }
+
+    export type Any = /* GenericValue<any> | */ Select<any> | MultiSelect<any> | Boolean | Range | Text | Color | Numeric | Interval
     export type Params = { [k: string]: Any }
+    export type DefaultValues<T extends Params> = { [k in keyof T]: T[k]['defaultValue'] }
 
     export function getDefaultValues<T extends Params>(params: T) {
         const d: { [k: string]: any } = {}
         Object.keys(params).forEach(k => d[k] = params[k].defaultValue)
-        return d as { [k in keyof T]: T[k]['defaultValue'] }
+        return d as DefaultValues<T>
     }
 }

+ 7 - 1
webpack.config.js

@@ -1,6 +1,7 @@
 const path = require('path');
 const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin');
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+// const CircularDependencyPlugin = require('circular-dependency-plugin');
 module.exports = {
     module: {
         rules: [
@@ -30,6 +31,11 @@ module.exports = {
         ]
     },
     plugins: [
+        // new CircularDependencyPlugin({
+        //     include: [ path.resolve(__dirname, 'build/node_modules/') ],
+        //     failOnError: false,
+        //     cwd: process.cwd(),
+        // }),
         new ExtraWatchWebpackPlugin({
             files: [
                 './build/node_modules/**/*.vert',