From 9d567aca1ad91a4d38821aa1c90b604ff07aa2b8 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Tue, 13 Nov 2018 15:24:17 -0800
Subject: [PATCH] wip, repr provider

---
 src/apps/canvas/component/structure-view.tsx  |  2 +-
 src/apps/canvas/component/volume-view.tsx     |  2 +-
 src/mol-repr/representation.ts                | 19 +++---
 src/mol-repr/shape/representation.ts          |  2 +
 .../structure/complex-representation.ts       |  6 +-
 src/mol-repr/structure/registry.ts            |  2 +-
 .../representation/ball-and-stick.ts          | 20 +++---
 .../structure/representation/cartoon.ts       | 25 +++----
 .../structure/units-representation.ts         | 17 +++--
 src/mol-repr/volume/direct-volume.ts          | 68 +++----------------
 src/mol-repr/volume/isosurface-mesh.ts        | 41 +++--------
 src/mol-repr/volume/registry.ts               |  2 +-
 src/mol-repr/volume/representation.ts         | 57 ++++++++++------
 13 files changed, 106 insertions(+), 157 deletions(-)

diff --git a/src/apps/canvas/component/structure-view.tsx b/src/apps/canvas/component/structure-view.tsx
index 3bfd62563..4f8178115 100644
--- a/src/apps/canvas/component/structure-view.tsx
+++ b/src/apps/canvas/component/structure-view.tsx
@@ -176,7 +176,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
                                 <RepresentationComponent
                                     repr={structureRepresentations[k] as Representation<any>}
                                     params={
-                                        structureView.app.structureRepresentationRegistry.get(k)!.params(structureView.app.reprCtx, structure!)
+                                        structureView.app.structureRepresentationRegistry.get(k)!.getParams(structureView.app.reprCtx, structure!)
                                     }
                                     canvas3d={structureView.canvas3d}
                                     app={structureView.app}
diff --git a/src/apps/canvas/component/volume-view.tsx b/src/apps/canvas/component/volume-view.tsx
index bf351b4dc..51239bb52 100644
--- a/src/apps/canvas/component/volume-view.tsx
+++ b/src/apps/canvas/component/volume-view.tsx
@@ -91,7 +91,7 @@ export class VolumeViewComponent extends React.Component<VolumeViewComponentProp
                                 <RepresentationComponent
                                     repr={volumeRepresentations[k] as Representation<any>}
                                     params={
-                                        volumeView.app.volumeRepresentationRegistry.get(k)!.params(volumeView.app.reprCtx, volume!)
+                                        volumeView.app.volumeRepresentationRegistry.get(k)!.getParams(volumeView.app.reprCtx, volume!)
                                     }
                                     canvas3d={volumeView.viewer}
                                     app={volumeView.app}
diff --git a/src/mol-repr/representation.ts b/src/mol-repr/representation.ts
index 59f07b03e..1a79e1c87 100644
--- a/src/mol-repr/representation.ts
+++ b/src/mol-repr/representation.ts
@@ -21,11 +21,13 @@ import { ThemeProps, Theme, ThemeRegistryContext } from 'mol-theme/theme';
 // }
 export type RepresentationProps = { [k: string]: any }
 
+export type RepresentationParamsGetter<D> = (ctx: ThemeRegistryContext, data: D) => PD.Params
+
 //
 
 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
+    readonly factory: (getParams: RepresentationParamsGetter<D>) => Representation<D, PD.DefaultValues<P>>
+    readonly getParams: (ctx: ThemeRegistryContext, data: D) => P
 }
 
 export class RepresentationRegistry<D> {
@@ -34,8 +36,8 @@ export class RepresentationRegistry<D> {
 
     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>
+    add<P extends PD.Params>(name: string, factory: RepresentationProvider<D, P>['factory'], getParams: RepresentationProvider<D, P>['getParams']) {
+        const provider = { factory, getParams } as RepresentationProvider<D, P>
         this._list.push({ name, provider })
         this._map.set(name, provider)
     }
@@ -46,7 +48,7 @@ export class RepresentationRegistry<D> {
 
     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
+        return provider ? provider.factory(provider.getParams) : Representation.Empty
     }
 
     get list() {
@@ -83,9 +85,9 @@ namespace Representation {
         destroy: () => {}
     }
 
-    export type Def<P extends RepresentationProps = {}> = { [k: string]: (defaultProps: P) => Representation<any, P> }
+    export type Def<D, P extends RepresentationProps = {}> = { [k: string]: (getParams: RepresentationParamsGetter<D>) => Representation<any, P> }
 
-    export function createMulti<D, P extends RepresentationProps = {}>(label: string, getParams: (ctx: ThemeRegistryContext, data: D) => PD.Params, reprDefs: Def<P>): Representation<D, P> {
+    export function createMulti<D, P extends RepresentationProps = {}>(label: string, getParams: RepresentationParamsGetter<D>, reprDefs: Def<D, P>): Representation<D, P> {
         let currentParams: PD.Params
         let currentProps: P
         let currentData: D
@@ -93,7 +95,7 @@ namespace Representation {
         const reprMap: { [k: number]: string } = {}
         const reprList: Representation<D, P>[] = Object.keys(reprDefs).map((name, i) => {
             reprMap[i] = name
-            return reprDefs[name](defaultProps)
+            return reprDefs[name](getParams)
         })
 
         return {
@@ -117,7 +119,6 @@ namespace Representation {
                 return currentParams
             },
             createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, data?: D) => {
-                if (data) currentData = data
                 if (data && data !== currentData) {
                     currentParams = getParams(ctx, data)
                     currentData = data
diff --git a/src/mol-repr/shape/representation.ts b/src/mol-repr/shape/representation.ts
index 718f68ce3..98e3ee8f2 100644
--- a/src/mol-repr/shape/representation.ts
+++ b/src/mol-repr/shape/representation.ts
@@ -38,6 +38,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
     let _renderObject: MeshRenderObject | undefined
     let _shape: Shape
     let currentProps: P
+    let currentParams: PD.Params
 
     function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, shape?: Shape) {
         currentProps = Object.assign({}, DefaultShapeProps, currentProps, props)
@@ -64,6 +65,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
     return {
         label: 'Shape mesh',
         get renderObjects () { return renderObjects },
+        get params () { return currentParams },
         get props () { return currentProps },
         createOrUpdate,
         getLoci(pickingId: PickingId) {
diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts
index 67cc444de..186af8d68 100644
--- a/src/mol-repr/structure/complex-representation.ts
+++ b/src/mol-repr/structure/complex-representation.ts
@@ -12,11 +12,11 @@ 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/representation';
-import { Theme, ThemeProps, createTheme, ThemeRegistryContext } from 'mol-theme/theme';
+import { RepresentationContext, RepresentationParamsGetter } from 'mol-repr/representation';
+import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 
-export function ComplexRepresentation<P extends StructureProps>(label: string, getParams: (ctx: ThemeRegistryContext, data: Structure) => PD.Params, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> {
+export function ComplexRepresentation<P extends StructureProps>(label: string, getParams: RepresentationParamsGetter<Structure>, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> {
     let visual: ComplexVisual<P> | undefined
 
     let _structure: Structure
diff --git a/src/mol-repr/structure/registry.ts b/src/mol-repr/structure/registry.ts
index c6fce5ebf..5dcfe8aaf 100644
--- a/src/mol-repr/structure/registry.ts
+++ b/src/mol-repr/structure/registry.ts
@@ -14,7 +14,7 @@ export class StructureRepresentationRegistry extends RepresentationRegistry<Stru
         super()
         Object.keys(BuiltInStructureRepresentations).forEach(name => {
             const p = (BuiltInStructureRepresentations as { [k: string]: RepresentationProvider<Structure, any> })[name]
-            this.add(name, p.factory, p.params)
+            this.add(name, p.factory, p.getParams)
         })
     }
 }
diff --git a/src/mol-repr/structure/representation/ball-and-stick.ts b/src/mol-repr/structure/representation/ball-and-stick.ts
index a6e3d51ce..126230226 100644
--- a/src/mol-repr/structure/representation/ball-and-stick.ts
+++ b/src/mol-repr/structure/representation/ball-and-stick.ts
@@ -4,25 +4,25 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ElementSphereVisual, ElementSphereParams, ElementSphereProps } from '../visual/element-sphere';
+import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere';
 import { IntraUnitLinkVisual, IntraUnitLinkParams } from '../visual/intra-unit-link-cylinder';
-import { InterUnitLinkVisual, InterUnitLinkParams, InterUnitLinkProps } from '../visual/inter-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, StructureRepresentationProvider } from '../representation';
-import { Representation } from 'mol-repr/representation';
+import { Representation, RepresentationParamsGetter } from 'mol-repr/representation';
 import { ThemeRegistryContext } from 'mol-theme/theme';
 import { Structure } from 'mol-model/structure';
-import { IntraUnitLinkProps } from '../visual/polymer-gap-cylinder';
 import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size';
 import { BuiltInColorThemeName, BuiltInColorThemeOptions } from 'mol-theme/color';
 import { UnitKind, UnitKindOptions } from '../visual/util/common';
 
+type ParamsGetter = RepresentationParamsGetter<Structure>
 const BallAndStickVisuals = {
-    'element-sphere': (defaultProps: ElementSphereProps) => UnitsRepresentation('Element sphere mesh', defaultProps, ElementSphereVisual),
-    'intra-link': (defaultProps: IntraUnitLinkProps) => UnitsRepresentation('Intra-unit link cylinder', defaultProps, IntraUnitLinkVisual),
-    'inter-link': (defaultProps: InterUnitLinkProps) => ComplexRepresentation('Inter-unit link cylinder', defaultProps, InterUnitLinkVisual),
+    'element-sphere': (getParams: ParamsGetter) => UnitsRepresentation('Element sphere mesh', getParams, ElementSphereVisual),
+    'intra-link': (getParams: ParamsGetter) => UnitsRepresentation('Intra-unit link cylinder', getParams, IntraUnitLinkVisual),
+    'inter-link': (getParams: ParamsGetter) => ComplexRepresentation('Inter-unit link cylinder', getParams, InterUnitLinkVisual),
 }
 type BallAndStickVisualName = keyof typeof BallAndStickVisuals
 const BallAndStickVisualOptions = Object.keys(BallAndStickVisuals).map(name => [name, name] as [BallAndStickVisualName, string])
@@ -44,10 +44,10 @@ export type BallAndStickProps = PD.DefaultValues<typeof BallAndStickParams>
 
 export type BallAndStickRepresentation = StructureRepresentation<BallAndStickProps>
 
-export function BallAndStickRepresentation(defaultProps: BallAndStickProps): BallAndStickRepresentation {
-    return Representation.createMulti('Ball & Stick', defaultProps, BallAndStickVisuals as unknown as Representation.Def<BallAndStickProps>)
+export function BallAndStickRepresentation(getParams: ParamsGetter): BallAndStickRepresentation {
+    return Representation.createMulti('Ball & Stick', getParams, BallAndStickVisuals as unknown as Representation.Def<Structure, BallAndStickProps>)
 }
 
 export const BallAndStickRepresentationProvider: StructureRepresentationProvider<typeof BallAndStickParams> = {
-    factory: BallAndStickRepresentation, params: getBallAndStickParams
+    factory: BallAndStickRepresentation, getParams: getBallAndStickParams
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/representation/cartoon.ts b/src/mol-repr/structure/representation/cartoon.ts
index da2483be5..38c04eb7e 100644
--- a/src/mol-repr/structure/representation/cartoon.ts
+++ b/src/mol-repr/structure/representation/cartoon.ts
@@ -4,24 +4,25 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-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 { PolymerTraceVisual,  PolymerTraceParams } from '../visual/polymer-trace-mesh';
+import { PolymerGapVisual, PolymerGapParams } from '../visual/polymer-gap-cylinder';
+import { NucleotideBlockVisual, NucleotideBlockParams } from '../visual/nucleotide-block-mesh';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { UnitsRepresentation } from '../units-representation';
 import { StructureRepresentation, StructureRepresentationProvider } from '../representation';
-import { Representation } from 'mol-repr/representation';
-import { PolymerDirectionVisual, PolymerDirectionParams, PolymerDirectionProps } from '../visual/polymer-direction-wedge';
+import { Representation, RepresentationParamsGetter } from 'mol-repr/representation';
+import { PolymerDirectionVisual, PolymerDirectionParams } from '../visual/polymer-direction-wedge';
 import { Structure } from 'mol-model/structure';
 import { ThemeRegistryContext } from 'mol-theme/theme';
 import { BuiltInSizeThemeName, BuiltInSizeThemeOptions } from 'mol-theme/size';
 import { BuiltInColorThemeOptions, BuiltInColorThemeName } from 'mol-theme/color';
 
+type ParamsGetter = RepresentationParamsGetter<Structure>
 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)
+    'polymer-trace': (getParams: ParamsGetter) => UnitsRepresentation('Polymer trace mesh', getParams, PolymerTraceVisual),
+    'polymer-gap': (getParams: ParamsGetter) => UnitsRepresentation('Polymer gap cylinder', getParams, PolymerGapVisual),
+    'nucleotide-block': (getParams: ParamsGetter) => UnitsRepresentation('Nucleotide block mesh', getParams, NucleotideBlockVisual),
+    'direction-wedge': (getParams: ParamsGetter) => UnitsRepresentation('Polymer direction wedge', getParams, PolymerDirectionVisual)
 }
 type CartoonVisualName = keyof typeof CartoonVisuals
 const CartoonVisualOptions = Object.keys(CartoonVisuals).map(name => [name, name] as [CartoonVisualName, string])
@@ -42,10 +43,10 @@ export function getCartoonParams(ctx: ThemeRegistryContext, structure: Structure
 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(getParams: ParamsGetter): CartoonRepresentation {
+    return Representation.createMulti('Cartoon', getParams, CartoonVisuals as unknown as Representation.Def<Structure, CartoonProps>)
 }
 
 export const CartoonRepresentationProvider: StructureRepresentationProvider<typeof CartoonParams> = {
-    factory: CartoonRepresentation, params: getCartoonParams
+    factory: CartoonRepresentation, getParams: getCartoonParams
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts
index 3f1b1026d..94a512c76 100644
--- a/src/mol-repr/structure/units-representation.ts
+++ b/src/mol-repr/structure/units-representation.ts
@@ -8,25 +8,31 @@
 import { Structure, Unit } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { RenderObject } from 'mol-gl/render-object';
-import { RepresentationProps, Visual, RepresentationContext } from '../representation';
+import { RepresentationProps, Visual, RepresentationContext, RepresentationParamsGetter } from '../representation';
 import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
 import { StructureGroup } from './units-visual';
 import { StructureProps, StructureRepresentation } from './representation';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
 
 export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { }
 
-export function UnitsRepresentation<P extends StructureProps>(label: string, defaultProps: P, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> {
+export function UnitsRepresentation<P extends StructureProps>(label: string, getParams: RepresentationParamsGetter<Structure>, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> {
     let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>()
 
     let _structure: Structure
     let _groups: ReadonlyArray<Unit.SymmetryGroup>
-    let _props: P = Object.assign({}, defaultProps)
+    let _params: PD.Params
+    let _props: P
     let _theme: Theme
 
     function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
+        if (structure && structure !== _structure) {
+            _params = getParams(ctx, structure)
+            if (!_props) _props = PD.getDefaultValues(_params) as P
+        }
         _props = Object.assign({}, _props, props)
         _theme = createTheme(ctx, { structure: structure || _structure }, props, themeProps, _theme)
 
@@ -139,9 +145,8 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, def
             })
             return renderObjects
         },
-        get props() {
-            return _props
-        },
+        get props() { return _props },
+        get params() { return _params },
         createOrUpdate,
         getLoci,
         mark,
diff --git a/src/mol-repr/volume/direct-volume.ts b/src/mol-repr/volume/direct-volume.ts
index 18c0ba431..59052c648 100644
--- a/src/mol-repr/volume/direct-volume.ts
+++ b/src/mol-repr/volume/direct-volume.ts
@@ -8,7 +8,7 @@ import { VolumeData } from 'mol-model/volume'
 import { RuntimeContext } from 'mol-task'
 import { VolumeVisual, VolumeRepresentation } from './representation';
 import { createDirectVolumeRenderObject } from 'mol-gl/render-object';
-import { Loci, EmptyLoci } from 'mol-model/loci';
+import { EmptyLoci } from 'mol-model/loci';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { Box3D } from 'mol-math/geometry';
@@ -18,11 +18,9 @@ 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 } 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/representation';
-import { ThemeProps, Theme } from 'mol-theme/theme';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme, ThemeRegistryContext } from 'mol-theme/theme';
 
 function getBoundingBox(gridDimension: Vec3, transform: Mat4) {
     const bbox = Box3D.empty()
@@ -114,8 +112,6 @@ function createVolumeTexture3d(volume: VolumeData) {
     const array = new Uint8Array(width * height * depth * 4)
     const textureVolume = { array, width, height, depth }
 
-    console.log('stats', stats)
-
     let i = 0
     for (let z = 0; z < depth; ++z) {
         for (let y = 0; y < height; ++y) {
@@ -124,36 +120,11 @@ function createVolumeTexture3d(volume: VolumeData) {
                     console.log(get(data, x, y, z), ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255)
                 }
                 array[i + 3] = ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255
-                // array[i + 3] = ((get(data, x, z, y) - stats.min) / (stats.max - stats.min)) * 255
-                // array[i + 3] = ((get(data, y, x, z) - stats.min) / (stats.max - stats.min)) * 255
-                // array[i + 3] = ((get(data, y, z, x) - stats.min) / (stats.max - stats.min)) * 255
-                // array[i + 3] = ((get(data, z, y, x) - stats.min) / (stats.max - stats.min)) * 255
-                // array[i + 3] = ((get(data, z, x, y) - stats.min) / (stats.max - stats.min)) * 255
                 i += 4
             }
         }
     }
 
-    // let i = 0
-    // for (let z = 0; z < depth; ++z) {
-    //     for (let x = 0; x < width; ++x) {
-    //         for (let y = 0; y < height; ++y) {
-    //             array[i + 3] = ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255
-    //             i += 4
-    //         }
-    //     }
-    // }
-
-    // let i = 0
-    // for (let x = 0; x < width; ++x) {
-    //     for (let y = 0; y < height; ++y) {
-    //         for (let z = 0; z < depth; ++z) {
-    //             array[i + 3] = ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255
-    //             i += 4
-    //         }
-    //     }
-    // }
-
     return textureVolume
 }
 
@@ -188,12 +159,14 @@ export const DirectVolumeParams = {
     ...Geometry.Params,
     ...DirectVolume.Params
 }
-export const DefaultDirectVolumeProps = PD.getDefaultValues(DirectVolumeParams)
-export type DirectVolumeProps = typeof DefaultDirectVolumeProps
+export function getDirectVolumeParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+    return DirectVolumeParams // TODO return copy
+}
+export type DirectVolumeProps = PD.DefaultValues<typeof DirectVolumeParams>
 
 export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
     return VolumeVisual<DirectVolumeProps>({
-        defaultProps: DefaultDirectVolumeProps,
+        defaultProps: PD.getDefaultValues(DirectVolumeParams),
         createGeometry: createDirectVolume,
         getLoci: () => EmptyLoci,
         mark: () => false,
@@ -210,28 +183,5 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
 }
 
 export function DirectVolumeRepresentation(): VolumeRepresentation<DirectVolumeProps> {
-    let currentProps: DirectVolumeProps
-    const volumeRepr = VolumeRepresentation(DirectVolumeVisual)
-    return {
-        label: 'Direct Volume',
-        get renderObjects() {
-            return [ ...volumeRepr.renderObjects ]
-        },
-        get props() {
-            return { ...volumeRepr.props }
-        },
-        createOrUpdate: (ctx: RepresentationContext, props: Partial<DirectVolumeProps> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) => {
-            currentProps = Object.assign({}, DefaultDirectVolumeProps, currentProps, props)
-            return volumeRepr.createOrUpdate(ctx, currentProps, themeProps, volume)
-        },
-        getLoci: (pickingId: PickingId) => {
-            return volumeRepr.getLoci(pickingId)
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            return volumeRepr.mark(loci, action)
-        },
-        destroy() {
-            volumeRepr.destroy()
-        }
-    }
+    return VolumeRepresentation('Direct Volume', getDirectVolumeParams, DirectVolumeVisual)
 }
\ No newline at end of file
diff --git a/src/mol-repr/volume/isosurface-mesh.ts b/src/mol-repr/volume/isosurface-mesh.ts
index 7ba955e50..78b896a04 100644
--- a/src/mol-repr/volume/isosurface-mesh.ts
+++ b/src/mol-repr/volume/isosurface-mesh.ts
@@ -8,18 +8,16 @@
 import { VolumeData } from 'mol-model/volume'
 import { VolumeVisual, VolumeRepresentation } from './representation';
 import { createMeshRenderObject } from 'mol-gl/render-object';
-import { Loci, EmptyLoci } from 'mol-model/loci';
+import { EmptyLoci } from 'mol-model/loci';
 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 { LocationIterator } from 'mol-geo/util/location-iterator';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
 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/representation';
-import { ThemeProps, Theme } from 'mol-theme/theme';
+import { VisualContext } from 'mol-repr/representation';
+import { Theme, ThemeRegistryContext } from 'mol-theme/theme';
 
 interface VolumeIsosurfaceProps {
     isoValueAbsolute: number
@@ -46,12 +44,14 @@ export const IsosurfaceParams = {
     isoValueAbsolute: PD.Range('Iso Value Absolute', '', 0.22, -1, 1, 0.01),
     isoValueRelative: PD.Range('Iso Value Relative', '', 2, -10, 10, 0.1),
 }
-export const DefaultIsosurfaceProps = PD.getDefaultValues(IsosurfaceParams)
-export type IsosurfaceProps = typeof DefaultIsosurfaceProps
+export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+    return IsosurfaceParams // TODO return copy
+}
+export type IsosurfaceProps = PD.DefaultValues<typeof IsosurfaceParams>
 
 export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
     return VolumeVisual<IsosurfaceProps>({
-        defaultProps: DefaultIsosurfaceProps,
+        defaultProps: PD.getDefaultValues(IsosurfaceParams),
         createGeometry: createVolumeIsosurface,
         getLoci: () => EmptyLoci,
         mark: () => false,
@@ -69,28 +69,5 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
 }
 
 export function IsosurfaceRepresentation(): VolumeRepresentation<IsosurfaceProps> {
-    let currentProps: IsosurfaceProps
-    const volumeRepr = VolumeRepresentation(IsosurfaceVisual)
-    return {
-        label: 'Isosurface',
-        get renderObjects() {
-            return [ ...volumeRepr.renderObjects ]
-        },
-        get props() {
-            return { ...volumeRepr.props }
-        },
-        createOrUpdate: (ctx: RepresentationContext, props: Partial<IsosurfaceProps> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) => {
-            currentProps = Object.assign({}, DefaultIsosurfaceProps, currentProps, props)
-            return volumeRepr.createOrUpdate(ctx, currentProps, themeProps, volume)
-        },
-        getLoci: (pickingId: PickingId) => {
-            return volumeRepr.getLoci(pickingId)
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            return volumeRepr.mark(loci, action)
-        },
-        destroy() {
-            volumeRepr.destroy()
-        }
-    }
+    return VolumeRepresentation('Isosurface', getIsosurfaceParams, IsosurfaceVisual)
 }
\ No newline at end of file
diff --git a/src/mol-repr/volume/registry.ts b/src/mol-repr/volume/registry.ts
index 653649b28..30f5902bc 100644
--- a/src/mol-repr/volume/registry.ts
+++ b/src/mol-repr/volume/registry.ts
@@ -12,7 +12,7 @@ export class VolumeRepresentationRegistry extends RepresentationRegistry<VolumeD
         super()
         Object.keys(BuiltInVolumeRepresentations).forEach(name => {
             const p = (BuiltInVolumeRepresentations as { [k: string]: RepresentationProvider<VolumeData, any> })[name]
-            this.add(name, p.factory, p.params)
+            this.add(name, p.factory, p.getParams)
         })
     }
 }
diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts
index bca67799d..c4240356a 100644
--- a/src/mol-repr/volume/representation.ts
+++ b/src/mol-repr/volume/representation.ts
@@ -5,7 +5,7 @@
  */
 
 import { Task } from 'mol-task'
-import { RepresentationProps, Representation, Visual, RepresentationContext, VisualContext, RepresentationProvider } from '../representation';
+import { RepresentationProps, Representation, Visual, RepresentationContext, VisualContext, RepresentationProvider, RepresentationParamsGetter } from '../representation';
 import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
 import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
@@ -147,13 +147,21 @@ export const VolumeParams = {
 export const DefaultVolumeProps = PD.getDefaultValues(VolumeParams)
 export type VolumeProps = typeof DefaultVolumeProps
 
-export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeData: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> {
-    let visual: VolumeVisual<any>
+export function VolumeRepresentation<P extends VolumeProps>(label: string, getParams: RepresentationParamsGetter<VolumeData>, visualCtor: (volume: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> {
+    let visual: VolumeVisual<P>
+
+    let _volume: VolumeData
     let _props: P
+    let _params: PD.Params
     let _theme: Theme
     let busy = false
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, volumeData?: VolumeData) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) {
+        if (volume && volume !== _volume) {
+            _params = getParams(ctx, volume)
+            _volume = volume
+            if (!_props) _props = PD.getDefaultValues(_params) as P
+        }
         _props = Object.assign({}, DefaultVolumeProps, _props, props)
         _theme = createTheme(ctx, _props, themeProps, {}, _theme)
 
@@ -161,38 +169,43 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
             // TODO queue it somehow
             if (busy) return
 
-            if (!visual && !volumeData) {
-                throw new Error('volumeData missing')
-            } else if (volumeData && !visual) {
+            if (!visual && !volume) {
+                throw new Error('volume data missing')
+            } else if (volume && !visual) {
                 busy = true
-                visual = visualCtor(volumeData)
-                await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volumeData)
+                visual = visualCtor(volume)
+                await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volume)
                 busy = false
             } else {
                 busy = true
-                await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volumeData)
+                await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volume)
                 busy = false
             }
         });
     }
 
+    function getLoci(pickingId: PickingId) {
+        return visual ? visual.getLoci(pickingId) : EmptyLoci
+    }
+
+    function mark(loci: Loci, action: MarkerAction) {
+        return visual ? visual.mark(loci, action) : false
+    }
+
+    function destroy() {
+        if (visual) visual.destroy()
+    }
+
     return {
-        label: 'Volume',
+        label,
         get renderObjects() {
             return visual && visual.renderObject ? [ visual.renderObject ] : []
         },
         get props () { return _props },
+        get params() { return _params },
         createOrUpdate,
-        getLoci(pickingId: PickingId) {
-            // TODO
-            return EmptyLoci
-        },
-        mark(loci: Loci, action: MarkerAction) {
-            // TODO
-            return false
-        },
-        destroy() {
-            // TODO
-        }
+        getLoci,
+        mark,
+        destroy
     }
 }
\ No newline at end of file
-- 
GitLab