Bläddra i källkod

basic mapped param support

David Sehnal 6 år sedan
förälder
incheckning
4f12144509
47 ändrade filer med 283 tillägg och 120 borttagningar
  1. 9 0
      src/mol-plugin/context.ts
  2. 28 2
      src/mol-plugin/spec.ts
  3. 7 1
      src/mol-plugin/state/actions/basic.ts
  4. 27 13
      src/mol-plugin/state/transforms/visuals.ts
  5. 94 8
      src/mol-plugin/ui/controls/parameters.tsx
  6. 11 4
      src/mol-repr/representation.ts
  7. 2 2
      src/mol-repr/shape/representation.ts
  8. 2 2
      src/mol-repr/structure/complex-representation.ts
  9. 10 10
      src/mol-repr/structure/complex-visual.ts
  10. 2 2
      src/mol-repr/structure/units-representation.ts
  11. 13 13
      src/mol-repr/structure/units-visual.ts
  12. 1 1
      src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
  13. 2 2
      src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
  14. 1 1
      src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
  15. 2 2
      src/mol-repr/structure/visual/element-point.ts
  16. 1 1
      src/mol-repr/structure/visual/element-sphere.ts
  17. 1 1
      src/mol-repr/structure/visual/gaussian-density-point.ts
  18. 1 1
      src/mol-repr/structure/visual/gaussian-density-volume.ts
  19. 1 1
      src/mol-repr/structure/visual/gaussian-surface-mesh.ts
  20. 1 1
      src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
  21. 1 1
      src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
  22. 1 1
      src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
  23. 1 1
      src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
  24. 1 1
      src/mol-repr/structure/visual/polymer-gap-cylinder.ts
  25. 1 1
      src/mol-repr/structure/visual/polymer-trace-mesh.ts
  26. 5 5
      src/mol-repr/structure/visual/util/common.ts
  27. 3 3
      src/mol-repr/volume/direct-volume.ts
  28. 2 2
      src/mol-repr/volume/isosurface-mesh.ts
  29. 11 11
      src/mol-repr/volume/representation.ts
  30. 1 1
      src/mol-theme/color.ts
  31. 1 1
      src/mol-theme/color/carbohydrate-symbol.ts
  32. 1 1
      src/mol-theme/color/chain-id.ts
  33. 1 1
      src/mol-theme/color/cross-link.ts
  34. 1 1
      src/mol-theme/color/element-index.ts
  35. 1 1
      src/mol-theme/color/element-symbol.ts
  36. 1 1
      src/mol-theme/color/molecule-type.ts
  37. 1 1
      src/mol-theme/color/polymer-index.ts
  38. 1 1
      src/mol-theme/color/residue-name.ts
  39. 1 1
      src/mol-theme/color/secondary-structure.ts
  40. 1 1
      src/mol-theme/color/sequence-id.ts
  41. 1 1
      src/mol-theme/color/shape-group.ts
  42. 1 1
      src/mol-theme/color/uniform.ts
  43. 1 1
      src/mol-theme/color/unit-index.ts
  44. 1 1
      src/mol-theme/size.ts
  45. 1 1
      src/mol-theme/size/physical.ts
  46. 1 1
      src/mol-theme/size/uniform.ts
  47. 24 9
      src/mol-util/param-definition.ts

+ 9 - 0
src/mol-plugin/context.ts

@@ -19,6 +19,10 @@ import { Representation } from 'mol-repr/representation';
 import { CreateStructureFromPDBe } from './state/actions/basic';
 import { LogEntry } from 'mol-util/log-entry';
 import { TaskManager } from './util/task-manager';
+import { StructureRepresentationRegistry } from 'mol-repr/structure/registry';
+import { ColorTheme } from 'mol-theme/color';
+import { SizeTheme } from 'mol-theme/size';
+import { ThemeRegistryContext } from 'mol-theme/theme';
 
 export class PluginContext {
     private disposed = false;
@@ -49,6 +53,11 @@ export class PluginContext {
         task: this.tasks.events
     };
 
+    readonly structureReprensentation = {
+        registry: new StructureRepresentationRegistry(),
+        themeCtx: { colorThemeRegistry: new ColorTheme.Registry(), sizeThemeRegistry: new SizeTheme.Registry() } as ThemeRegistryContext
+    }
+
     readonly behaviors = {
         // state: {
         //     data: this.state.dataState.behaviors,

+ 28 - 2
src/mol-plugin/spec.ts

@@ -4,8 +4,34 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-interface PluginSpec {
+import { StateAction } from 'mol-state/action';
+import { Transformer } from 'mol-state';
+import { StateTransformParameters } from './ui/state/parameters';
+
+export { PluginSpec }
 
+interface PluginSpec {
+    actions: PluginSpec.Action[],
+    behaviors: PluginSpec.Behavior[]
 }
 
-export { PluginSpec }
+namespace PluginSpec {
+    export interface Action {
+        action: StateAction | Transformer,
+        customControl?: StateTransformParameters.Class,
+        autoUpdate?: boolean
+    }
+
+    export function Action(action: StateAction | Transformer, params?: { customControl?: StateTransformParameters.Class, autoUpdate?: boolean }): Action {
+        return { action, customControl: params && params.customControl, autoUpdate: params && params.autoUpdate };
+    }
+
+    export interface Behavior {
+        transformer: Transformer,
+        defaultParams?: any
+    }
+
+    export function Behavior<T extends Transformer>(transformer: T, defaultParams?: Transformer.Params<T>): Behavior {
+        return { transformer, defaultParams };
+    }
+}

+ 7 - 1
src/mol-plugin/state/actions/basic.ts

@@ -9,6 +9,7 @@ import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { StateSelection } from 'mol-state/state/selection';
+import { CartoonParams } from 'mol-repr/structure/representation/cartoon';
 
 export const CreateStructureFromPDBe = StateAction.create<PluginStateObject.Root, void, { id: string }>({
     from: [PluginStateObject.Root],
@@ -45,7 +46,12 @@ export const CreateStructureFromPDBe = StateAction.create<PluginStateObject.Root
             .apply(StateTransforms.Model.CreateModelFromTrajectory, { modelIndex: 0 })
             .apply(StateTransforms.Model.CreateStructureAssembly)
             // .apply(StateTransforms.Model.CreateStructureSelection, { query, label: 'ALA residues' })
-            .apply(StateTransforms.Visuals.CreateStructureRepresentation)
+            .apply(StateTransforms.Visuals.CreateStructureRepresentation, {
+                type: {
+                    name: 'cartoon',
+                    params: PD.getDefaultValues(CartoonParams)
+                }
+            })
             .getTree();
 
         return state.update(newTree);

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

@@ -9,31 +9,45 @@ import { Task } from 'mol-task';
 import { PluginStateTransform } from '../objects';
 import { PluginStateObject as SO } from '../objects';
 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()
+import { ParamDefinition as PD } from 'mol-util/param-definition';
 
 export { CreateStructureRepresentation }
-namespace CreateStructureRepresentation { export interface Params { } }
+namespace CreateStructureRepresentation {
+    export interface Params {
+        type: { name: string, params: any /** todo is there "common type" */ }
+    }
+}
 const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, CreateStructureRepresentation.Params>({
     name: 'create-structure-representation',
     display: { name: 'Create 3D Representation' },
     from: [SO.Molecule.Structure],
     to: [SO.Molecule.Representation3D],
+    params: {
+        default: (a, ctx: PluginContext) => ({
+            type: {
+                name: ctx.structureReprensentation.registry.default.name,
+                params: PD.getDefaultValues(ctx.structureReprensentation.registry.default.provider.getParams(ctx.structureReprensentation.themeCtx, a.data))
+            }
+        }),
+        definition: (a, ctx: PluginContext) => ({
+            type: PD.Mapped('Type', '',
+                ctx.structureReprensentation.registry.default.name,
+                ctx.structureReprensentation.registry.types,
+                name => ctx.structureReprensentation.registry.get(name)!.getParams(ctx.structureReprensentation.themeCtx, a.data))
+        })
+    },
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
-            const repr = representationRegistry.create('cartoon', { colorThemeRegistry, sizeThemeRegistry }, a.data)
-            await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl, colorThemeRegistry, sizeThemeRegistry }, {}, {}, a.data).runInContext(ctx);
-            return new SO.Molecule.Representation3D(repr);
+            const repr = plugin.structureReprensentation.registry.create(params.type.name, plugin.structureReprensentation.themeCtx, a.data)
+            await repr.createOrUpdate({ webgl: plugin.canvas3d.webgl, ...plugin.structureReprensentation.themeCtx }, params.type.params || {}, {}, a.data).runInContext(ctx);
+            return new SO.Molecule.Representation3D(repr, { label: params.type.name });
         });
     },
-    update({ a, b }, plugin: PluginContext) {
+    update({ a, b, oldParams, newParams }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
-            await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl, colorThemeRegistry, sizeThemeRegistry }, b.data.props, {}, a.data).runInContext(ctx);
+            if (newParams.type.name !== oldParams.type.name) return Transformer.UpdateResult.Recreate;
+
+            await b.data.createOrUpdate({ webgl: plugin.canvas3d.webgl, ...plugin.structureReprensentation.themeCtx }, { ...b.data.props, ...newParams.type.params }, {}, a.data).runInContext(ctx);
             return Transformer.UpdateResult.Updated;
         });
     }

+ 94 - 8
src/mol-plugin/ui/controls/parameters.tsx

@@ -19,14 +19,18 @@ export interface ParameterControlsProps<P extends PD.Params = PD.Params> {
 export class ParameterControls<P extends PD.Params> extends React.PureComponent<ParameterControlsProps<P>, {}> {
     render() {
         const common = {
-            changes: this.props.onChange,
+            onChange: this.props.onChange,
             isEnabled: this.props.isEnabled,
             onEnter: this.props.onEnter,
         }
         const params = this.props.params;
         const values = this.props.values;
         return <div style={{ width: '100%' }}>
-            {Object.keys(params).map(key => <ParamWrapper control={controlFor(params[key])} param={params[key]} key={key} {...common} name={key} value={values[key]} />)}
+            {Object.keys(params).map(key => {
+                const param = params[key];
+                if (param.type === 'mapped') return <MappedControl param={param} key={key} {...common} name={key} value={values[key]} />
+                return <ParamWrapper control={controlFor(param)} param={param} key={key} {...common} name={key} value={values[key]} />
+            })}
         </div>;
     }
 }
@@ -36,22 +40,27 @@ function controlFor(param: PD.Any): ValueControl {
         case 'boolean': return BoolControl;
         case 'number': return NumberControl;
         case 'range': return NumberControl;
-        case 'multi-select': throw new Error('nyi');
-        case 'color': throw new Error('nyi');
+        case 'multi-select': return MultiSelectControl;
+        case 'color': return ColorControl;
         case 'select': return SelectControl;
         case 'text': return TextControl;
+        case 'interval': return IntervalControl;
+        case 'group': return GroupControl;
+        case 'mapped': throw Error('Must be handled separately');
     }
-    throw new Error('not supporter');
+    throw new Error('not supported');
 }
 
-type ParamWrapperProps = { name: string, value: any, param: PD.Base<any>, changes: ParamOnChange, control: ValueControl, onEnter?: () => void, isEnabled?: boolean }
+type MappedWrapperProps = { name: string, value: PD.Mapped<any>['defaultValue'], param: PD.Mapped<any>, onChange: ParamOnChange, onEnter?: () => void, isEnabled?: boolean }
+
+type ParamWrapperProps = { name: string, value: any, param: PD.Base<any>, onChange: ParamOnChange, control: ValueControl, onEnter?: () => void, isEnabled?: boolean }
 export type ParamOnChange = (params: { param: PD.Base<any>, name: string, value: any }) => void
 type ValueControlProps<P extends PD.Base<any> = PD.Base<any>> = { value: any, param: P, isEnabled?: boolean, onChange: (v: any) => void, onEnter?: () => void }
 type ValueControl = React.ComponentClass<ValueControlProps<any>>
 
 export class ParamWrapper extends React.PureComponent<ParamWrapperProps> {
     onChange = (value: any) => {
-        this.props.changes({ param: this.props.param, name: this.props.name, value });
+        this.props.onChange({ param: this.props.param, name: this.props.name, value });
     }
 
     render() {
@@ -124,7 +133,84 @@ export class SelectControl extends React.PureComponent<ValueControlProps<PD.Sele
 
     render() {
         return <select value={this.props.value || ''} onChange={this.onChange}>
-            {this.props.param.options.map(([value, label]) => <option key={label} value={value}>{label}</option>)}
+            {this.props.param.options.map(([value, label]) => <option key={value} value={value}>{label}</option>)}
         </select>;
     }
+}
+
+
+export class MultiSelectControl extends React.PureComponent<ValueControlProps<PD.MultiSelect<any>>> {
+    // onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
+    //     this.setState({ value: e.target.value });
+    //     this.props.onChange(e.target.value);
+    // }
+
+    render() {
+        return <span>multiselect TODO</span>;
+        // return <select value={this.props.value || ''} onChange={this.onChange}>
+        //     {this.props.param.options.map(([value, label]) => <option key={label} value={value}>{label}</option>)}
+        // </select>;
+    }
+}
+
+export class IntervalControl extends React.PureComponent<ValueControlProps<PD.Interval>> {
+    // onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
+    //     this.setState({ value: e.target.value });
+    //     this.props.onChange(e.target.value);
+    // }
+
+    render() {
+        return <span>interval TODO</span>;
+    }
+}
+
+export class ColorControl extends React.PureComponent<ValueControlProps<PD.Color>> {
+    // onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
+    //     this.setState({ value: e.target.value });
+    //     this.props.onChange(e.target.value);
+    // }
+
+    render() {
+        return <span>color TODO</span>;
+    }
+}
+
+export class GroupControl extends React.PureComponent<ValueControlProps<PD.Group<any>>> {
+    // onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
+    //     this.setState({ value: e.target.value });
+    //     this.props.onChange(e.target.value);
+    // }
+
+    render() {
+        return <span>group TODO</span>;
+    }
+}
+
+export class MappedControl extends React.PureComponent<MappedWrapperProps> {
+    change(value: PD.Mapped<any>['defaultValue'] ) {
+        this.props.onChange({ name: this.props.name, param: this.props.param, value });
+    }
+
+    onChangeName: ParamOnChange = e => {
+        this.change({ name: e.value, params: PD.getDefaultValues(this.props.param.map(e.value)) });
+    }
+
+    onChangeParam: ParamOnChange = e => {
+        const value: PD.Mapped<any>['defaultValue'] = this.props.value;
+        this.change({ name: value.name, params: { ...value.params, [e.name]: e.value } });
+    }
+
+    render() {
+        const value: PD.Mapped<any>['defaultValue'] = this.props.value;
+        const params = this.props.param.map(value.name);
+
+        return <div>
+            <ParamWrapper control={SelectControl} param={this.props.param.select}
+                isEnabled={this.props.isEnabled} onChange={this.onChangeName} onEnter={this.props.onEnter}
+                name={'name'} value={value.name} />
+            <div style={{ borderLeft: '5px solid #777', paddingLeft: '5px' }}>
+                <ParameterControls params={params} onChange={this.onChangeParam} values={value.params} onEnter={this.props.onEnter} isEnabled={this.props.isEnabled} />
+            </div>
+        </div>
+    }
 }

+ 11 - 4
src/mol-repr/representation.ts

@@ -29,12 +29,19 @@ export type RepresentationParamsGetter<D, P extends PD.Params> = (ctx: ThemeRegi
 export interface RepresentationProvider<D, P extends PD.Params> {
     readonly factory: (getParams: RepresentationParamsGetter<D, P>) => Representation<D, P>
     readonly getParams: (ctx: ThemeRegistryContext, data: D) => P
+    // TODO
+    // readonly defaultParams: PD.Values<P>
 }
 
 export class RepresentationRegistry<D> {
     private _list: { name: string, provider: RepresentationProvider<D, any> }[] = []
     private _map = new Map<string, RepresentationProvider<D, any>>()
 
+    get default() { return this._list[0]; }
+    get types(): [string, string][] {
+        return this._list.map(e => [e.name, e.name] as [string, string]);
+    }
+
     constructor() {};
 
     add<P extends PD.Params>(name: string, factory: RepresentationProvider<D, P>['factory'], getParams: RepresentationProvider<D, P>['getParams']) {
@@ -70,9 +77,9 @@ interface Representation<D, P extends PD.Params = {}> {
     readonly label: string
     readonly updated: BehaviorSubject<number>
     readonly renderObjects: ReadonlyArray<RenderObject>
-    readonly props: Readonly<PD.DefaultValues<P>>
+    readonly props: Readonly<PD.Values<P>>
     readonly params: Readonly<P>
-    createOrUpdate: (ctx: RepresentationContext, props?: Partial<PD.DefaultValues<P>>, themeProps?: ThemeProps, data?: D) => Task<void>
+    createOrUpdate: (ctx: RepresentationContext, props?: Partial<PD.Values<P>>, themeProps?: ThemeProps, data?: D) => Task<void>
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     destroy: () => void
@@ -93,7 +100,7 @@ namespace Representation {
         const updated = new BehaviorSubject(0)
 
         let currentParams: P
-        let currentProps: PD.DefaultValues<P>
+        let currentProps: PD.Values<P>
         let currentData: D
 
         const reprMap: { [k: number]: string } = {}
@@ -174,7 +181,7 @@ export interface VisualContext {
 
 export interface Visual<D, P extends PD.Params> {
     readonly renderObject: RenderObject | undefined
-    createOrUpdate: (ctx: VisualContext, theme: Theme, props?: Partial<PD.DefaultValues<P>>, data?: D) => Promise<void>
+    createOrUpdate: (ctx: VisualContext, theme: Theme, props?: Partial<PD.Values<P>>, data?: D) => Promise<void>
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     destroy: () => void

+ 2 - 2
src/mol-repr/shape/representation.ts

@@ -35,10 +35,10 @@ export function ShapeRepresentation<P extends ShapeParams>(): ShapeRepresentatio
     const renderObjects: RenderObject[] = []
     let _renderObject: MeshRenderObject | undefined
     let _shape: Shape
-    let currentProps: PD.DefaultValues<P> = PD.getDefaultValues(ShapeParams) as PD.DefaultValues<P>
+    let currentProps: PD.Values<P> = PD.getDefaultValues(ShapeParams) as PD.Values<P>
     let currentParams: P
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, shape?: Shape) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, shape?: Shape) {
         currentProps = Object.assign({}, currentProps, props)
         if (shape) _shape = shape
 

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

@@ -23,10 +23,10 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
 
     let _structure: Structure
     let _params: P
-    let _props: PD.DefaultValues<P>
+    let _props: PD.Values<P>
     let _theme: Theme
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
         if (structure && structure !== _structure) {
             _params = getParams(ctx, structure)
             _structure = structure

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

@@ -37,18 +37,18 @@ type ComplexParams = typeof ComplexParams
 type ComplexRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
 
 interface ComplexVisualBuilder<P extends ComplexParams, G extends Geometry> {
-    defaultProps: PD.DefaultValues<P>
-    createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.DefaultValues<P>, geometry?: G): Promise<G>
+    defaultProps: PD.Values<P>
+    createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G>
     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: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme): void
+    setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
 }
 
 interface ComplexVisualGeometryBuilder<P extends ComplexParams, G extends Geometry> extends ComplexVisualBuilder<P, G> {
     createEmptyGeometry(geometry?: G): G
-    createRenderObject(ctx: VisualContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.DefaultValues<P>): Promise<ComplexRenderObject>
-    updateValues(values: RenderableValues, newProps: PD.DefaultValues<P>): void
+    createRenderObject(ctx: VisualContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): Promise<ComplexRenderObject>
+    updateValues(values: RenderableValues, newProps: PD.Values<P>): void
 }
 
 export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeometryBuilder<P, Geometry>): ComplexVisual<P> {
@@ -57,14 +57,14 @@ export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeo
     const updateState = VisualUpdateState.create()
 
     let renderObject: ComplexRenderObject | undefined
-    let currentProps: PD.DefaultValues<P>
+    let currentProps: PD.Values<P>
     let currentTheme: Theme
     let geometry: Geometry
     let currentStructure: Structure
     let locationIt: LocationIterator
     let conformationHash: number
 
-    async function create(ctx: VisualContext, structure: Structure, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) {
+    async function create(ctx: VisualContext, structure: Structure, theme: Theme, props: Partial<PD.Values<P>> = {}) {
         currentProps = Object.assign({}, defaultProps, props)
         currentTheme = theme
         currentStructure = structure
@@ -76,7 +76,7 @@ export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeo
         renderObject = await createRenderObject(ctx, structure, geometry, locationIt, theme, currentProps)
     }
 
-    async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>>) {
+    async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>>) {
         const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
 
         if (!renderObject) return false
@@ -123,7 +123,7 @@ export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeo
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}, structure?: Structure) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, structure?: Structure) {
             if (!structure && !currentStructure) {
                 throw new Error('missing structure')
             } else if (structure && (!currentStructure || !renderObject)) {
@@ -182,7 +182,7 @@ export interface ComplexMeshVisualBuilder<P extends ComplexMeshParams> extends C
 export function ComplexMeshVisual<P extends ComplexMeshParams>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> {
     return ComplexVisual({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },

+ 2 - 2
src/mol-repr/structure/units-representation.ts

@@ -34,10 +34,10 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, getPar
     let _structure: Structure
     let _groups: ReadonlyArray<Unit.SymmetryGroup>
     let _params: P
-    let _props: PD.DefaultValues<P>
+    let _props: PD.Values<P>
     let _theme: Theme
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, structure?: Structure) {
         if (structure && structure !== _structure) {
             _params = getParams(ctx, structure)
             if (!_props) _props = PD.getDefaultValues(_params)

+ 13 - 13
src/mol-repr/structure/units-visual.ts

@@ -44,18 +44,18 @@ function sameGroupConformation(groupA: Unit.SymmetryGroup, groupB: Unit.Symmetry
 type UnitsRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
 
 interface UnitsVisualBuilder<P extends UnitsParams, G extends Geometry> {
-    defaultProps: PD.DefaultValues<P>
-    createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.DefaultValues<P>, geometry?: G): Promise<G>
+    defaultProps: PD.Values<P>
+    createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G>
     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: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme): void
+    setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
 }
 
 interface UnitsVisualGeometryBuilder<P extends UnitsParams, G extends Geometry> extends UnitsVisualBuilder<P, G> {
     createEmptyGeometry(geometry?: G): G
-    createRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.DefaultValues<P>): Promise<UnitsRenderObject>
-    updateValues(values: RenderableValues, newProps: PD.DefaultValues<P>): void
+    createRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): Promise<UnitsRenderObject>
+    updateValues(values: RenderableValues, newProps: PD.Values<P>): void
 }
 
 export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryBuilder<P, Geometry>): UnitsVisual<P> {
@@ -64,7 +64,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
     const updateState = VisualUpdateState.create()
 
     let renderObject: UnitsRenderObject | undefined
-    let currentProps: PD.DefaultValues<P>
+    let currentProps: PD.Values<P>
     let currentTheme: Theme
     let geometry: Geometry
     let currentGroup: Unit.SymmetryGroup
@@ -72,7 +72,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
     let locationIt: LocationIterator
     let currentConformationId: UUID
 
-    async function create(ctx: VisualContext, group: Unit.SymmetryGroup, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) {
+    async function create(ctx: VisualContext, group: Unit.SymmetryGroup, theme: Theme, props: Partial<PD.Values<P>> = {}) {
         currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure })
         currentTheme = theme
         currentGroup = group
@@ -88,7 +88,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         renderObject = await createRenderObject(ctx, group, geometry, locationIt, theme, currentProps)
     }
 
-    async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) {
+    async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}) {
         if (!renderObject) return
 
         const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
@@ -147,7 +147,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}, structureGroup?: StructureGroup) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, structureGroup?: StructureGroup) {
             if (structureGroup) currentStructure = structureGroup.structure
             const group = structureGroup ? structureGroup.group : undefined
             if (!group && !currentGroup) {
@@ -211,7 +211,7 @@ export interface UnitsMeshVisualBuilder<P extends UnitsMeshParams> extends Units
 export function UnitsMeshVisual<P extends UnitsMeshParams>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
     return UnitsVisual({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
@@ -235,7 +235,7 @@ export function UnitsPointsVisual<P extends UnitsPointsParams>(builder: UnitsPoi
         ...builder,
         createEmptyGeometry: Points.createEmpty,
         createRenderObject: createUnitsPointsRenderObject,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
@@ -257,7 +257,7 @@ export function UnitsLinesVisual<P extends UnitsLinesParams>(builder: UnitsLines
         ...builder,
         createEmptyGeometry: Lines.createEmpty,
         createRenderObject: createUnitsLinesRenderObject,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
@@ -279,7 +279,7 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeParams>(build
         ...builder,
         createEmptyGeometry: DirectVolume.createEmpty,
         createRenderObject: createUnitsDirectVolumeRenderObject,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>, newTheme: Theme, currentTheme: Theme) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },

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

@@ -74,7 +74,7 @@ export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkParams>
         createLocationIterator: CarbohydrateLinkIterator,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<CarbohydrateLinkParams>, currentProps: PD.DefaultValues<CarbohydrateLinkParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CarbohydrateLinkParams>, currentProps: PD.Values<CarbohydrateLinkParams>) => {
             state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })

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

@@ -44,7 +44,7 @@ const diamondPrism = DiamondPrism()
 const pentagonalPrism = PentagonalPrism()
 const hexagonalPrism = HexagonalPrism()
 
-async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.DefaultValues<CarbohydrateSymbolParams>, mesh?: Mesh) {
+async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<CarbohydrateSymbolParams>, mesh?: Mesh) {
     const builder = MeshBuilder.create(256, 128, mesh)
 
     const { detail } = props
@@ -158,7 +158,7 @@ export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolPara
         createLocationIterator: CarbohydrateElementIterator,
         getLoci: getCarbohydrateLoci,
         mark: markCarbohydrate,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<CarbohydrateSymbolParams>, currentProps: PD.DefaultValues<CarbohydrateSymbolParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CarbohydrateSymbolParams>, currentProps: PD.Values<CarbohydrateSymbolParams>) => {
             state.createGeometry = newProps.detail !== currentProps.detail
         }
     })

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

@@ -63,7 +63,7 @@ export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintPara
         createLocationIterator: CrossLinkRestraintIterator,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<CrossLinkRestraintParams>, currentProps: PD.DefaultValues<CrossLinkRestraintParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CrossLinkRestraintParams>, currentProps: PD.Values<CrossLinkRestraintParams>) => {
             state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })

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

@@ -24,7 +24,7 @@ export type ElementPointParams = typeof ElementPointParams
 
 // TODO size
 
-export async function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.DefaultValues<ElementPointParams>, points: Points) {
+export async function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ElementPointParams>, points: Points) {
     const elements = unit.elements
     const n = elements.length
     const builder = PointsBuilder.create(n, n / 10, points)
@@ -50,7 +50,7 @@ export function ElementPointVisual(): UnitsVisual<ElementPointParams> {
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<ElementPointParams>, currentProps: PD.DefaultValues<ElementPointParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementPointParams>, currentProps: PD.Values<ElementPointParams>) => {
 
         }
     })

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

@@ -27,7 +27,7 @@ export function ElementSphereVisual(): UnitsVisual<ElementSphereParams> {
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<ElementSphereParams>, currentProps: PD.DefaultValues<ElementSphereParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementSphereParams>, currentProps: PD.Values<ElementSphereParams>) => {
             state.createGeometry = newProps.detail !== currentProps.detail
         }
     })

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

@@ -62,7 +62,7 @@ export function GaussianDensityPointVisual(): UnitsVisual<GaussianDensityPointPa
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: () => EmptyLoci,
         mark: () => false,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianDensityPointParams>, currentProps: PD.DefaultValues<GaussianDensityPointParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianDensityPointParams>, currentProps: PD.Values<GaussianDensityPointParams>) => {
             if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true

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

@@ -40,7 +40,7 @@ export function GaussianDensityVolumeVisual(): UnitsVisual<GaussianDensityVolume
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianDensityVolumeParams>, currentProps: PD.DefaultValues<GaussianDensityVolumeParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianDensityVolumeParams>, currentProps: PD.Values<GaussianDensityVolumeParams>) => {
             if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) {

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

@@ -47,7 +47,7 @@ export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceParams> {
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianSurfaceParams>, currentProps: PD.DefaultValues<GaussianSurfaceParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianSurfaceParams>, currentProps: PD.Values<GaussianSurfaceParams>) => {
             if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true

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

@@ -46,7 +46,7 @@ export function GaussianWireframeVisual(): UnitsVisual<GaussianWireframeParams>
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<GaussianWireframeParams>, currentProps: PD.DefaultValues<GaussianWireframeParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<GaussianWireframeParams>, currentProps: PD.Values<GaussianWireframeParams>) => {
             if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true

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

@@ -62,7 +62,7 @@ export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkParams> {
         createLocationIterator: LinkIterator.fromStructure,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<InterUnitLinkParams>, currentProps: PD.DefaultValues<InterUnitLinkParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<InterUnitLinkParams>, currentProps: PD.Values<InterUnitLinkParams>) => {
             if (newProps.linkScale !== currentProps.linkScale) state.createGeometry = true
             if (newProps.linkSpacing !== currentProps.linkSpacing) state.createGeometry = true
             if (newProps.radialSegments !== currentProps.radialSegments) state.createGeometry = true

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

@@ -20,7 +20,7 @@ import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualContext } from 'mol-repr/representation';
 import { Theme } from 'mol-theme/theme';
 
-async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.DefaultValues<IntraUnitLinkParams>, mesh?: Mesh) {
+async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitLinkParams>, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
     const location = StructureElement.create(unit)

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

@@ -79,7 +79,7 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneParams> {
         createLocationIterator: StructureElementIterator.fromGroup,
         getLoci: getElementLoci,
         mark: markElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<PolymerBackboneParams>, currentProps: PD.DefaultValues<PolymerBackboneParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerBackboneParams>, currentProps: PD.Values<PolymerBackboneParams>) => {
             state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })

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

@@ -95,7 +95,7 @@ export function PolymerGapVisual(): UnitsVisual<PolymerGapParams> {
         createLocationIterator: PolymerGapLocationIterator.fromGroup,
         getLoci: getPolymerGapElementLoci,
         mark: markPolymerGapElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<PolymerGapParams>, currentProps: PD.DefaultValues<PolymerGapParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerGapParams>, currentProps: PD.Values<PolymerGapParams>) => {
             state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
         }
     })

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

@@ -99,7 +99,7 @@ export function PolymerTraceVisual(): UnitsVisual<PolymerTraceParams> {
         createLocationIterator: PolymerLocationIterator.fromGroup,
         getLoci: getPolymerElementLoci,
         mark: markPolymerElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<PolymerTraceParams>, currentProps: PD.DefaultValues<PolymerTraceParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerTraceParams>, currentProps: PD.Values<PolymerTraceParams>) => {
             state.createGeometry = (
                 newProps.linearSegments !== currentProps.linearSegments ||
                 newProps.radialSegments !== currentProps.radialSegments ||

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

@@ -49,14 +49,14 @@ export function includesUnitKind(unitKinds: UnitKind[], unit: Unit) {
 
 // mesh
 
-export async function createComplexMeshRenderObject(ctx: VisualContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureMeshParams>) {
+export async function createComplexMeshRenderObject(ctx: VisualContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureMeshParams>) {
     const transform = createIdentityTransform()
     const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, theme, props)
     const state = createRenderableState(props)
     return createMeshRenderObject(values, state)
 }
 
-export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureMeshParams>) {
+export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureMeshParams>) {
     const transform = createUnitsTransform(group)
     const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, theme, props)
     const state = createRenderableState(props)
@@ -65,7 +65,7 @@ export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Uni
 
 // points
 
-export async function createUnitsPointsRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructurePointsParams>) {
+export async function createUnitsPointsRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructurePointsParams>) {
     const transform = createUnitsTransform(group)
     const values = await Points.createValues(ctx.runtime, points, transform, locationIt, theme, props)
     const state = createRenderableState(props)
@@ -74,7 +74,7 @@ export async function createUnitsPointsRenderObject(ctx: VisualContext, group: U
 
 // lines
 
-export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureLinesParams>) {
+export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureLinesParams>) {
     const transform = createUnitsTransform(group)
     const values = await Lines.createValues(ctx.runtime, lines, transform, locationIt, theme, props)
     const state = createRenderableState(props)
@@ -83,7 +83,7 @@ export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Un
 
 // direct-volume
 
-export async function createUnitsDirectVolumeRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<StructureDirectVolumeParams>) {
+export async function createUnitsDirectVolumeRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureDirectVolumeParams>) {
     const transform = createUnitsTransform(group)
     const values = await DirectVolume.createValues(ctx.runtime, directVolume, transform, locationIt, theme, props)
     const state = createRenderableState(props)

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

@@ -143,7 +143,7 @@ export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, v
 
 //
 
-export async function createDirectVolume(ctx: VisualContext, volume: VolumeData, props: PD.DefaultValues<DirectVolumeParams>, directVolume?: DirectVolume) {
+export async function createDirectVolume(ctx: VisualContext, volume: VolumeData, props: PD.Values<DirectVolumeParams>, directVolume?: DirectVolume) {
     const { runtime, webgl } = ctx
     if (webgl === undefined) throw new Error('DirectVolumeVisual requires `webgl` in props')
 
@@ -170,9 +170,9 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeParams> {
         createGeometry: createDirectVolume,
         getLoci: () => EmptyLoci,
         mark: () => false,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<DirectVolumeParams>, currentProps: PD.DefaultValues<DirectVolumeParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<DirectVolumeParams>, currentProps: PD.Values<DirectVolumeParams>) => {
         },
-        createRenderObject: async (ctx: VisualContext, geometry: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<DirectVolumeParams>) => {
+        createRenderObject: async (ctx: VisualContext, geometry: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.Values<DirectVolumeParams>) => {
             const transform = createIdentityTransform()
             const values = await DirectVolume.createValues(ctx.runtime, geometry, transform, locationIt, theme, props)
             const state = createRenderableState(props)

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

@@ -55,10 +55,10 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceParams> {
         createGeometry: createVolumeIsosurface,
         getLoci: () => EmptyLoci,
         mark: () => false,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.DefaultValues<IsosurfaceParams>, currentProps: PD.DefaultValues<IsosurfaceParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IsosurfaceParams>, currentProps: PD.Values<IsosurfaceParams>) => {
             if (newProps.isoValueAbsolute !== currentProps.isoValueAbsolute) state.createGeometry = true
         },
-        createRenderObject: async (ctx: VisualContext, geometry: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.DefaultValues<IsosurfaceParams>) => {
+        createRenderObject: async (ctx: VisualContext, geometry: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.Values<IsosurfaceParams>) => {
             const transform = createIdentityTransform()
             const values = await Mesh.createValues(ctx.runtime, geometry, transform, locationIt, theme, props)
             const state = createRenderableState(props)

+ 11 - 11
src/mol-repr/volume/representation.ts

@@ -27,16 +27,16 @@ export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData,
 type VolumeRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
 
 interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> {
-    defaultProps: PD.DefaultValues<P>
-    createGeometry(ctx: VisualContext, volumeData: VolumeData, props: PD.DefaultValues<P>, geometry?: G): Promise<G>
+    defaultProps: PD.Values<P>
+    createGeometry(ctx: VisualContext, volumeData: VolumeData, props: PD.Values<P>, geometry?: G): Promise<G>
     getLoci(pickingId: PickingId, id: number): Loci
     mark(loci: Loci, apply: (interval: Interval) => boolean): boolean
-    setUpdateState(state: VisualUpdateState, newProps: PD.DefaultValues<P>, currentProps: PD.DefaultValues<P>): void
+    setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>): void
 }
 
 interface VolumeVisualGeometryBuilder<P extends VolumeParams, G extends Geometry> extends VolumeVisualBuilder<P, G> {
-    createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: PD.DefaultValues<P>): Promise<VolumeRenderObject>
-    updateValues(values: RenderableValues, newProps: PD.DefaultValues<P>): void
+    createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): Promise<VolumeRenderObject>
+    updateValues(values: RenderableValues, newProps: PD.Values<P>): void
 }
 
 export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeometryBuilder<P, Geometry>) {
@@ -44,13 +44,13 @@ export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeomet
     const { createRenderObject, updateValues } = builder
     const updateState = VisualUpdateState.create()
 
-    let currentProps: PD.DefaultValues<P>
+    let currentProps: PD.Values<P>
     let renderObject: VolumeRenderObject | undefined
     let currentVolume: VolumeData
     let geometry: Geometry
     let locationIt: LocationIterator
 
-    async function create(ctx: VisualContext, volume: VolumeData, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) {
+    async function create(ctx: VisualContext, volume: VolumeData, theme: Theme, props: Partial<PD.Values<P>> = {}) {
         currentProps = Object.assign({}, defaultProps, props)
         if (props.isoValueRelative) {
             currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
@@ -62,7 +62,7 @@ export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeomet
         renderObject = await createRenderObject(ctx, geometry, locationIt, theme, currentProps)
     }
 
-    async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}) {
+    async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}) {
         if (!renderObject) return
         const newProps = Object.assign({}, currentProps, props)
 
@@ -87,7 +87,7 @@ export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeomet
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.DefaultValues<P>> = {}, volume?: VolumeData) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
             if (!volume && !currentVolume) {
                 throw new Error('missing volume')
             } else if (volume && (!currentVolume || !renderObject)) {
@@ -152,12 +152,12 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, getP
     let visual: VolumeVisual<P>
 
     let _volume: VolumeData
-    let _props: PD.DefaultValues<P>
+    let _props: PD.Values<P>
     let _params: P
     let _theme: Theme
     let busy = false
 
-    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.DefaultValues<P>> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<PD.Values<P>> = {}, themeProps: ThemeProps = {}, volume?: VolumeData) {
         if (volume && volume !== _volume) {
             _params = getParams(ctx, volume)
             _volume = volume

+ 1 - 1
src/mol-theme/color.ts

@@ -63,7 +63,7 @@ namespace ColorTheme {
     }
 
     export interface Provider<P extends PD.Params> {
-        readonly factory: (ctx: ThemeDataContext, props: PD.DefaultValues<P>) => ColorTheme<PD.DefaultValues<P>>
+        readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<PD.Values<P>>
         readonly params: (ctx: ThemeDataContext) => P
     }
 

+ 1 - 1
src/mol-theme/color/carbohydrate-symbol.ts

@@ -29,7 +29,7 @@ export const CarbohydrateSymbolColorThemeParams = {
 export function getCarbohydrateSymbolColorThemeParams(ctx: ThemeDataContext) {
     return CarbohydrateSymbolColorThemeParams // TODO return copy
 }
-export type CarbohydrateSymbolColorThemeProps = PD.DefaultValues<typeof CarbohydrateSymbolColorThemeParams>
+export type CarbohydrateSymbolColorThemeProps = PD.Values<typeof CarbohydrateSymbolColorThemeParams>
 
 export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: CarbohydrateSymbolColorThemeProps): ColorTheme<CarbohydrateSymbolColorThemeProps> {
     let color: LocationColor

+ 1 - 1
src/mol-theme/color/chain-id.ts

@@ -22,7 +22,7 @@ export const ChainIdColorThemeParams = {
 export function getChainIdColorThemeParams(ctx: ThemeDataContext) {
     return ChainIdColorThemeParams // TODO return copy
 }
-export type ChainIdColorThemeProps = PD.DefaultValues<typeof ChainIdColorThemeParams>
+export type ChainIdColorThemeProps = PD.Values<typeof ChainIdColorThemeParams>
 
 function getAsymId(unit: Unit): StructureElement.Property<string> {
     switch (unit.kind) {

+ 1 - 1
src/mol-theme/color/cross-link.ts

@@ -24,7 +24,7 @@ export const CrossLinkColorThemeParams = {
 export function getCrossLinkColorThemeParams(ctx: ThemeDataContext) {
     return CrossLinkColorThemeParams // TODO return copy
 }
-export type CrossLinkColorThemeProps = PD.DefaultValues<typeof CrossLinkColorThemeParams>
+export type CrossLinkColorThemeProps = PD.Values<typeof CrossLinkColorThemeParams>
 
 const distVecA = Vec3.zero(), distVecB = Vec3.zero()
 function linkDistance(link: Link.Location) {

+ 1 - 1
src/mol-theme/color/element-index.ts

@@ -22,7 +22,7 @@ export const ElementIndexColorThemeParams = {
 export function getElementIndexColorThemeParams(ctx: ThemeDataContext) {
     return ElementIndexColorThemeParams // TODO return copy
 }
-export type ElementIndexColorThemeProps = PD.DefaultValues<typeof ElementIndexColorThemeParams>
+export type ElementIndexColorThemeProps = PD.Values<typeof ElementIndexColorThemeParams>
 
 export function ElementIndexColorTheme(ctx: ThemeDataContext, props: ElementIndexColorThemeProps): ColorTheme<ElementIndexColorThemeProps> {
     let color: LocationColor

+ 1 - 1
src/mol-theme/color/element-symbol.ts

@@ -24,7 +24,7 @@ export const ElementSymbolColorThemeParams = {}
 export function getElementSymbolColorThemeParams(ctx: ThemeDataContext) {
     return ElementSymbolColorThemeParams // TODO return copy
 }
-export type ElementSymbolColorThemeProps = PD.DefaultValues<typeof ElementSymbolColorThemeParams>
+export type ElementSymbolColorThemeProps = PD.Values<typeof ElementSymbolColorThemeParams>
 
 export function elementSymbolColor(element: ElementSymbol): Color {
     const c = (ElementSymbolColors as { [k: string]: Color })[element];

+ 1 - 1
src/mol-theme/color/molecule-type.ts

@@ -30,7 +30,7 @@ export const MoleculeTypeColorThemeParams = {}
 export function getMoleculeTypeColorThemeParams(ctx: ThemeDataContext) {
     return MoleculeTypeColorThemeParams // TODO return copy
 }
-export type MoleculeTypeColorThemeProps = PD.DefaultValues<typeof MoleculeTypeColorThemeParams>
+export type MoleculeTypeColorThemeProps = PD.Values<typeof MoleculeTypeColorThemeParams>
 
 export function moleculeTypeColor(unit: Unit, element: ElementIndex): Color {
     const moleculeType = getElementMoleculeType(unit, element)

+ 1 - 1
src/mol-theme/color/polymer-index.ts

@@ -21,7 +21,7 @@ export const PolymerIndexColorThemeParams = {
 export function getPolymerIndexColorThemeParams(ctx: ThemeDataContext) {
     return PolymerIndexColorThemeParams // TODO return copy
 }
-export type PolymerIndexColorThemeProps = PD.DefaultValues<typeof PolymerIndexColorThemeParams>
+export type PolymerIndexColorThemeProps = PD.Values<typeof PolymerIndexColorThemeParams>
 
 export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PolymerIndexColorThemeProps): ColorTheme<PolymerIndexColorThemeProps> {
     let color: LocationColor

+ 1 - 1
src/mol-theme/color/residue-name.ts

@@ -65,7 +65,7 @@ export const ResidueNameColorThemeParams = {}
 export function getResidueNameColorThemeParams(ctx: ThemeDataContext) {
     return ResidueNameColorThemeParams // TODO return copy
 }
-export type ResidueNameColorThemeProps = PD.DefaultValues<typeof ResidueNameColorThemeParams>
+export type ResidueNameColorThemeProps = PD.Values<typeof ResidueNameColorThemeParams>
 
 export function residueNameColor(residueName: string): Color {
     const c = (ResidueNameColors as { [k: string]: Color })[residueName];

+ 1 - 1
src/mol-theme/color/secondary-structure.ts

@@ -35,7 +35,7 @@ export const SecondaryStructureColorThemeParams = {}
 export function getSecondaryStructureColorThemeParams(ctx: ThemeDataContext) {
     return SecondaryStructureColorThemeParams // TODO return copy
 }
-export type SecondaryStructureColorThemeProps = PD.DefaultValues<typeof SecondaryStructureColorThemeParams>
+export type SecondaryStructureColorThemeProps = PD.Values<typeof SecondaryStructureColorThemeParams>
 
 export function secondaryStructureColor(unit: Unit, element: ElementIndex): Color {
     let secStrucType = SecondaryStructureType.create(SecondaryStructureType.Flag.None)

+ 1 - 1
src/mol-theme/color/sequence-id.ts

@@ -22,7 +22,7 @@ export const SequenceIdColorThemeParams = {
 export function getSequenceIdColorThemeParams(ctx: ThemeDataContext) {
     return SequenceIdColorThemeParams // TODO return copy
 }
-export type SequenceIdColorThemeProps = PD.DefaultValues<typeof SequenceIdColorThemeParams>
+export type SequenceIdColorThemeProps = PD.Values<typeof SequenceIdColorThemeParams>
 
 function getSeqId(unit: Unit, element: ElementIndex): number {
     const { model } = unit

+ 1 - 1
src/mol-theme/color/shape-group.ts

@@ -18,7 +18,7 @@ export const ShapeGroupColorThemeParams = {}
 export function getShapeGroupColorThemeParams(ctx: ThemeDataContext) {
     return ShapeGroupColorThemeParams // TODO return copy
 }
-export type ShapeGroupColorThemeProps = PD.DefaultValues<typeof ShapeGroupColorThemeParams>
+export type ShapeGroupColorThemeProps = PD.Values<typeof ShapeGroupColorThemeParams>
 
 export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: ShapeGroupColorThemeProps): ColorTheme<ShapeGroupColorThemeProps> {
     return {

+ 1 - 1
src/mol-theme/color/uniform.ts

@@ -18,7 +18,7 @@ export const UniformColorThemeParams = {
 export function getUniformColorThemeParams(ctx: ThemeDataContext) {
     return UniformColorThemeParams // TODO return copy
 }
-export type UniformColorThemeProps = PD.DefaultValues<typeof UniformColorThemeParams>
+export type UniformColorThemeProps = PD.Values<typeof UniformColorThemeParams>
 
 export function UniformColorTheme(ctx: ThemeDataContext, props: UniformColorThemeProps): ColorTheme<UniformColorThemeProps> {
     const color = props.value || DefaultColor

+ 1 - 1
src/mol-theme/color/unit-index.ts

@@ -21,7 +21,7 @@ export const UnitIndexColorThemeParams = {
 export function getUnitIndexColorThemeParams(ctx: ThemeDataContext) {
     return UnitIndexColorThemeParams // TODO return copy
 }
-export type UnitIndexColorThemeProps = PD.DefaultValues<typeof UnitIndexColorThemeParams>
+export type UnitIndexColorThemeProps = PD.Values<typeof UnitIndexColorThemeParams>
 
 export function UnitIndexColorTheme(ctx: ThemeDataContext, props: UnitIndexColorThemeProps): ColorTheme<UnitIndexColorThemeProps> {
     let color: LocationColor

+ 1 - 1
src/mol-theme/size.ts

@@ -27,7 +27,7 @@ namespace SizeTheme {
     }
 
     export interface Provider<P extends PD.Params> {
-        readonly factory: (ctx: ThemeDataContext, props: PD.DefaultValues<P>) => SizeTheme<PD.DefaultValues<P>>
+        readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => SizeTheme<PD.Values<P>>
         readonly params: (ctx: ThemeDataContext) => P
     }
 

+ 1 - 1
src/mol-theme/size/physical.ts

@@ -18,7 +18,7 @@ export const PhysicalSizeThemeParams = {}
 export function getPhysicalSizeThemeParams(ctx: ThemeDataContext) {
     return PhysicalSizeThemeParams // TODO return copy
 }
-export type PhysicalSizeThemeProps = PD.DefaultValues<typeof PhysicalSizeThemeParams>
+export type PhysicalSizeThemeProps = PD.Values<typeof PhysicalSizeThemeParams>
 
 export function getPhysicalRadius(unit: Unit, element: ElementIndex): number {
     if (Unit.isAtomic(unit)) {

+ 1 - 1
src/mol-theme/size/uniform.ts

@@ -16,7 +16,7 @@ export const UniformSizeThemeParams = {
 export function getUniformSizeThemeParams(ctx: ThemeDataContext) {
     return UniformSizeThemeParams // TODO return copy
 }
-export type UniformSizeThemeProps = PD.DefaultValues<typeof UniformSizeThemeParams>
+export type UniformSizeThemeProps = PD.Values<typeof UniformSizeThemeParams>
 
 export function UniformSizeTheme(ctx: ThemeDataContext, props: UniformSizeThemeProps): SizeTheme<UniformSizeThemeProps> {
     const size = props.value

+ 24 - 9
src/mol-util/param-definition.ts

@@ -90,24 +90,39 @@ export namespace ParamDefinition {
         return { type: 'interval', label, description, defaultValue }
     }
 
-    export interface Obj<P extends Params> extends Base<{ [K in keyof P]: P[K]['defaultValue']}> {
-        type: 'obj',
-        pivot?: keyof P,
-        params: P
+    export interface Group<T extends { [key: string]: any }> extends Base<T> {
+        type: 'group',
+        params: T
     }
-    export function Obj<P extends Params>(label: string, description: string, params: P, pivot?: keyof P): Obj<P> {
-        return { type: 'obj', label, description, defaultValue: getDefaultValues(params), params, pivot };
+    export function Group<T extends { [key: string]: any }>(label: string, description: string, params: T): Group<T> {
+        return { type: 'group', label, description, defaultValue: getDefaultValues(params), params };
     }
 
-    export type Any = Select<any> | MultiSelect<any> | Boolean | Range | Text | Color | Numeric | Interval | Obj<any>
+    export interface Mapped<T> extends Base<{ name: string, params: T }> {
+        type: 'mapped',
+        select: Select<string>,
+        map(name: string): Params
+    }
+    export function Mapped<T>(label: string, description: string, defaultKey: string, names: [string, string][], map: Mapped<T>['map']): Mapped<T> {
+        return {
+            type: 'mapped',
+            label,
+            description,
+            defaultValue: { name: defaultKey, params: getDefaultValues(map(defaultKey)) as any },
+            select: Select<string>(label, description, defaultKey, names),
+            map
+        };
+    }
+
+    export type Any = Select<any> | MultiSelect<any> | Boolean | Range | Text | Color | Numeric | Interval | Group<any> | Mapped<any>
 
     export type Params = { [k: string]: Any }
-    export type DefaultValues<T extends Params> = { [k in keyof T]: T[k]['defaultValue'] }
+    export type Values<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 DefaultValues<T>
+        return d as Values<T>
     }
 
     export function clone<P extends Params>(params: P): P {