ソースを参照

customize current interaction, wip refactroring repr registries

David Sehnal 5 年 前
コミット
ba7d4a5215
28 ファイル変更161 行追加105 行削除
  1. 3 2
      src/mol-model-props/computed/representations/interactions.ts
  2. 3 2
      src/mol-model-props/integrative/cross-link-restraint/representation.ts
  3. 4 3
      src/mol-model-props/rcsb/representations/validation-report-clashes.ts
  4. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/computed/interactions.ts
  5. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/integrative/cross-link-restraint.ts
  6. 2 2
      src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts
  7. 71 41
      src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts
  8. 1 5
      src/mol-plugin/spec.ts
  9. 13 6
      src/mol-repr/representation.ts
  10. 6 4
      src/mol-repr/structure/registry.ts
  11. 2 2
      src/mol-repr/structure/representation.ts
  12. 3 2
      src/mol-repr/structure/representation/ball-and-stick.ts
  13. 3 2
      src/mol-repr/structure/representation/carbohydrate.ts
  14. 3 2
      src/mol-repr/structure/representation/cartoon.ts
  15. 3 2
      src/mol-repr/structure/representation/ellipsoid.ts
  16. 3 2
      src/mol-repr/structure/representation/gaussian-surface.ts
  17. 3 2
      src/mol-repr/structure/representation/gaussian-volume.ts
  18. 3 2
      src/mol-repr/structure/representation/label.ts
  19. 3 2
      src/mol-repr/structure/representation/molecular-surface.ts
  20. 3 2
      src/mol-repr/structure/representation/orientation.ts
  21. 3 2
      src/mol-repr/structure/representation/point.ts
  22. 3 2
      src/mol-repr/structure/representation/putty.ts
  23. 3 2
      src/mol-repr/structure/representation/spacefill.ts
  24. 3 2
      src/mol-repr/volume/direct-volume.ts
  25. 3 2
      src/mol-repr/volume/isosurface.ts
  26. 6 3
      src/mol-repr/volume/registry.ts
  27. 2 1
      src/mol-repr/volume/representation.ts
  28. 2 2
      src/mol-util/param-definition.ts

+ 3 - 2
src/mol-model-props/computed/representations/interactions.ts

@@ -37,7 +37,8 @@ export function InteractionRepresentation(ctx: RepresentationContext, getParams:
     return Representation.createMulti('Interactions', ctx, getParams, StructureRepresentationStateBuilder, InteractionsVisuals as unknown as Representation.Def<Structure, InteractionsParams>)
 }
 
-export const InteractionsRepresentationProvider: StructureRepresentationProvider<InteractionsParams> = {
+export const InteractionsRepresentationProvider = StructureRepresentationProvider({
+    name: 'interactions',
     label: 'Non-covalent Interactions',
     description: 'Displays non-covalent interactions as dashed cylinders.',
     factory: InteractionRepresentation,
@@ -50,4 +51,4 @@ export const InteractionsRepresentationProvider: StructureRepresentationProvider
         attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => InteractionsProvider.ref(data, false)
     }
-}
+})

+ 3 - 2
src/mol-model-props/integrative/cross-link-restraint/representation.ts

@@ -134,7 +134,8 @@ export function CrossLinkRestraintRepresentation(ctx: RepresentationContext, get
     return Representation.createMulti('CrossLinkRestraint', ctx, getParams, StructureRepresentationStateBuilder, CrossLinkRestraintVisuals as unknown as Representation.Def<Structure, CrossLinkRestraintParams>)
 }
 
-export const CrossLinkRestraintRepresentationProvider: StructureRepresentationProvider<CrossLinkRestraintParams> = {
+export const CrossLinkRestraintRepresentationProvider = StructureRepresentationProvider({
+    name: CrossLinkRestraint.Tag.CrossLinkRestraint,
     label: 'Cross Link Restraint',
     description: 'Displays cross-link restraints.',
     factory: CrossLinkRestraintRepresentation,
@@ -147,4 +148,4 @@ export const CrossLinkRestraintRepresentationProvider: StructureRepresentationPr
         attach: (ctx: CustomProperty.Context, structure: Structure) => CrossLinkRestraintProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => CrossLinkRestraintProvider.ref(data, false)
     }
-}
+})

+ 4 - 3
src/mol-model-props/rcsb/representations/validation-report-clashes.ts

@@ -20,7 +20,7 @@ import { createLinkCylinderMesh, LinkCylinderParams, LinkCylinderStyle } from '.
 import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual, StructureGroup } from '../../../mol-repr/structure/units-visual';
 import { VisualUpdateState } from '../../../mol-repr/util';
 import { LocationIterator } from '../../../mol-geo/util/location-iterator';
-import { ClashesProvider, IntraUnitClashes, InterUnitClashes } from '../validation-report';
+import { ClashesProvider, IntraUnitClashes, InterUnitClashes, ValidationReport } from '../validation-report';
 import { CustomProperty } from '../../common/custom-property';
 import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../../../mol-repr/structure/complex-visual';
 import { Color } from '../../../mol-util/color';
@@ -278,7 +278,8 @@ export function ClashesRepresentation(ctx: RepresentationContext, getParams: Rep
     return repr
 }
 
-export const ClashesRepresentationProvider: StructureRepresentationProvider<ClashesParams> = {
+export const ClashesRepresentationProvider = StructureRepresentationProvider({
+    name: ValidationReport.Tag.Clashes,
     label: 'Validation Clashes',
     description: 'Displays clashes between atoms as disks. Data from wwPDB Validation Report, obtained via RCSB PDB.',
     factory: ClashesRepresentation,
@@ -291,4 +292,4 @@ export const ClashesRepresentationProvider: StructureRepresentationProvider<Clas
         attach: (ctx: CustomProperty.Context, structure: Structure) => ClashesProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => ClashesProvider.ref(data, false)
     }
-}
+})

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/computed/interactions.ts

@@ -101,14 +101,14 @@ export const Interactions = PluginBehavior.create<{ autoAttach: boolean, showToo
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add('interaction-type', InteractionTypeColorThemeProvider)
             this.ctx.managers.lociLabels.addProvider(this.label);
-            this.ctx.structureRepresentation.registry.add('interactions', InteractionsRepresentationProvider)
+            this.ctx.structureRepresentation.registry.add(InteractionsRepresentationProvider)
         }
 
         unregister() {
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('interaction-type')
             this.ctx.managers.lociLabels.removeProvider(this.label);
-            this.ctx.structureRepresentation.registry.remove('interactions')
+            this.ctx.structureRepresentation.registry.remove(InteractionsRepresentationProvider)
         }
     },
     params: () => ({

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/integrative/cross-link-restraint.ts

@@ -25,14 +25,14 @@ export const CrossLinkRestraint = PluginBehavior.create<{ }>({
             this.provider.formatRegistry.add('mmCIF', crossLinkRestraintFromMmcif)
 
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.CrossLinkRestraint, CrossLinkColorThemeProvider)
-            this.ctx.structureRepresentation.registry.add(Tag.CrossLinkRestraint, CrossLinkRestraintRepresentationProvider)
+            this.ctx.structureRepresentation.registry.add(CrossLinkRestraintRepresentationProvider)
         }
 
         unregister() {
             this.provider.formatRegistry.remove('mmCIF')
 
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.CrossLinkRestraint)
-            this.ctx.structureRepresentation.registry.remove(Tag.CrossLinkRestraint)
+            this.ctx.structureRepresentation.registry.remove(CrossLinkRestraintRepresentationProvider)
         }
     }
 });

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts

@@ -48,7 +48,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.GeometryQuality, GeometryQualityColorThemeProvider)
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.add(Tag.RandomCoilIndex, RandomCoilIndexColorThemeProvider)
 
-            this.ctx.structureRepresentation.registry.add(Tag.Clashes, ClashesRepresentationProvider)
+            this.ctx.structureRepresentation.registry.add(ClashesRepresentationProvider)
         }
 
         update(p: { autoAttach: boolean, showTooltip: boolean }) {
@@ -71,7 +71,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.GeometryQuality)
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove(Tag.RandomCoilIndex)
 
-            this.ctx.structureRepresentation.registry.remove(Tag.Clashes)
+            this.ctx.structureRepresentation.registry.remove(ClashesRepresentationProvider)
         }
     },
     params: () => ({

+ 71 - 41
src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts

@@ -20,6 +20,7 @@ import { Binding } from '../../../../mol-util/binding';
 import { ButtonsType, ModifiersKeys } from '../../../../mol-util/input/input-observer';
 import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
 import { PluginCommands } from '../../../commands';
+import { PluginContext } from '../../../context';
 
 const B = ButtonsType
 const M = ModifiersKeys
@@ -28,10 +29,38 @@ const Trigger = Binding.Trigger
 const DefaultStructureRepresentationInteractionBindings = {
     clickInteractionAroundOnly: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Show the structure interaction around only the clicked element using ${triggers}.'),
 }
-const StructureRepresentationInteractionParams = {
-    bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }),
+// const StructureRepresentationInteractionParams = {
+//     bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }),
+// }
+
+const StructureRepresentationInteractionParams = (plugin: PluginContext) => {
+    
+    const reprParams = StateTransforms.Representation.StructureRepresentation3D.definition.params!(void 0, plugin) as PD.Params;
+    return {
+        bindings: PD.Value(DefaultStructureRepresentationInteractionBindings, { isHidden: true }),
+        focusParams: PD.Group(reprParams, {
+            label: 'Focus',
+            customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', size: 'uniform' })
+        }),
+        surroundingsParams: PD.Group(reprParams, {
+            label: 'Surroundings',
+            customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', color: 'element-symbol', size: 'uniform' })
+        }),
+        nciParams: PD.Group(reprParams, {
+            label: 'Non-covalent Int.',
+            customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', color: 'element-symbol', size: 'uniform' })
+            // customDefault: createStructureRepresentationParams(plugin, void 0, {
+            //     type: InteractionsRepresentationProvider,
+            //     color: InteractionTypeColorThemeProvider,
+            //     size: BuiltInSizeThemes.uniform
+            // })
+        })
+    };
 }
-type StructureRepresentationInteractionProps = PD.Values<typeof StructureRepresentationInteractionParams>
+
+type StructureRepresentationInteractionProps = PD.ValuesFor<ReturnType<typeof StructureRepresentationInteractionParams>>
+
+//PD.Values<typeof StructureRepresentationInteractionParams>
 
 export enum StructureRepresentationInteractionTags {
     Group = 'structure-interaction-group',
@@ -45,65 +74,49 @@ export enum StructureRepresentationInteractionTags {
 const TagSet: Set<StructureRepresentationInteractionTags> = new Set([StructureRepresentationInteractionTags.Group, StructureRepresentationInteractionTags.ResidueSel, StructureRepresentationInteractionTags.ResidueRepr, StructureRepresentationInteractionTags.SurrSel, StructureRepresentationInteractionTags.SurrRepr, StructureRepresentationInteractionTags.SurrNciRepr])
 
 export class StructureRepresentationInteractionBehavior extends PluginBehavior.WithSubscribers<StructureRepresentationInteractionProps> {
-    private createResVisualParams(s: Structure) {
-        return createStructureRepresentationParams(this.plugin, s, {
-            type: 'ball-and-stick',
-            size: 'uniform'
-        });
-    }
-
-    private createSurVisualParams(s: Structure) {
-        return createStructureRepresentationParams(this.plugin, s, {
-            type: 'ball-and-stick',
-            color: 'element-symbol',
-            size: 'uniform'
-        });
-    }
-
-    private createSurNciVisualParams(s: Structure) {
-        return createStructureRepresentationParams(this.plugin, s, {
-            type: InteractionsRepresentationProvider,
-            color: InteractionTypeColorThemeProvider,
-            size: BuiltInSizeThemes.uniform
-        });
-    }
-
     private ensureShape(cell: StateObjectCell<PluginStateObject.Molecule.Structure>) {
         const state = this.plugin.state.dataState, tree = state.tree;
         const builder = state.build();
         const refs = StateSelection.findUniqueTagsInSubtree(tree, cell.transform.ref, TagSet);
 
         if (!refs['structure-interaction-group']) {
-            refs['structure-interaction-group'] = builder.to(cell).group(StateTransforms.Misc.CreateGroup,
-                { label: 'Current Focus' }, { tags: StructureRepresentationInteractionTags.Group }).ref;
+            refs['structure-interaction-group'] = builder
+                .to(cell)
+                .group(StateTransforms.Misc.CreateGroup, { label: 'Current Focus' }, { tags: StructureRepresentationInteractionTags.Group }).ref;
         }
 
         // Selections
         if (!refs[StructureRepresentationInteractionTags.ResidueSel]) {
-            refs[StructureRepresentationInteractionTags.ResidueSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromBundle,
-                { bundle: { } as any, label: 'Focus' }, { tags: StructureRepresentationInteractionTags.ResidueSel }).ref;
+            refs[StructureRepresentationInteractionTags.ResidueSel] = builder
+                .to(refs['structure-interaction-group'])
+                .apply(StateTransforms.Model.StructureSelectionFromBundle,
+                    { bundle: { } as any, label: 'Focus' }, { tags: StructureRepresentationInteractionTags.ResidueSel }).ref;
         }
 
         if (!refs[StructureRepresentationInteractionTags.SurrSel]) {
-            refs[StructureRepresentationInteractionTags.SurrSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromExpression,
-                { expression: { } as any, label: 'Surroundings' }, { tags: StructureRepresentationInteractionTags.SurrSel }).ref;
+            refs[StructureRepresentationInteractionTags.SurrSel] = builder
+                .to(refs['structure-interaction-group'])
+                .apply(StateTransforms.Model.StructureSelectionFromExpression,
+                    { expression: { } as any, label: 'Surroundings' }, { tags: StructureRepresentationInteractionTags.SurrSel }).ref;
         }
 
         // Representations
-        // TODO: ability to customize how it looks in the behavior params
         if (!refs[StructureRepresentationInteractionTags.ResidueRepr]) {
-            refs[StructureRepresentationInteractionTags.ResidueRepr] = builder.to(refs['structure-interaction-residue-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
-                this.createResVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.ResidueRepr }).ref;
+            refs[StructureRepresentationInteractionTags.ResidueRepr] = builder
+                .to(refs['structure-interaction-residue-sel']!)
+                .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.focusParams, { tags: StructureRepresentationInteractionTags.ResidueRepr }).ref;
         }
 
         if (!refs[StructureRepresentationInteractionTags.SurrRepr]) {
-            refs[StructureRepresentationInteractionTags.SurrRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
-                this.createSurVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.SurrRepr }).ref;
+            refs[StructureRepresentationInteractionTags.SurrRepr] = builder
+                .to(refs['structure-interaction-surr-sel']!)
+                .apply(StateTransforms.Representation.StructureRepresentation3D,this.params.nciParams, { tags: StructureRepresentationInteractionTags.SurrRepr }).ref;
         }
 
         if (!refs[StructureRepresentationInteractionTags.SurrNciRepr]) {
-            refs[StructureRepresentationInteractionTags.SurrNciRepr] = builder.to(refs['structure-interaction-surr-sel']!).apply(StateTransforms.Representation.StructureRepresentation3D,
-                this.createSurNciVisualParams(cell.obj!.data), { tags: StructureRepresentationInteractionTags.SurrNciRepr }).ref;
+            refs[StructureRepresentationInteractionTags.SurrNciRepr] = builder
+                .to(refs['structure-interaction-surr-sel']!)
+                .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.surroundingsParams, { tags: StructureRepresentationInteractionTags.SurrNciRepr }).ref;
         }
 
         return { state, builder, refs };
@@ -200,7 +213,24 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W
     }
 
     async update(params: StructureRepresentationInteractionProps) {
-        return false;
+        this.params = params;
+
+        const state = this.plugin.state.dataState;
+        const builder = state.build();
+
+        const all = StateSelection.Generators.root.subtree();        
+        for (const repr of state.select(all.withTag(StructureRepresentationInteractionTags.ResidueRepr))) {
+            builder.to(repr).update(this.params.focusParams);
+        }
+        for (const repr of state.select(all.withTag(StructureRepresentationInteractionTags.SurrRepr))) {
+            builder.to(repr).update(this.params.surroundingsParams);
+        }
+        for (const repr of state.select(all.withTag(StructureRepresentationInteractionTags.SurrNciRepr))) {
+            builder.to(repr).update(this.params.nciParams);
+        }
+
+        await PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });
+        return true;
     }
 }
 
@@ -209,5 +239,5 @@ export const StructureRepresentationInteraction = PluginBehavior.create({
     display: { name: 'Structure Representation Interaction' },
     category: 'interaction',
     ctor: StructureRepresentationInteractionBehavior,
-    params: () => StructureRepresentationInteractionParams
+    params: (_, plugin) => StructureRepresentationInteractionParams(plugin)
 });

+ 1 - 5
src/mol-plugin/spec.ts

@@ -9,7 +9,6 @@ import { StateTransformer, StateAction } from '../mol-state';
 import { StateTransformParameters } from '../mol-plugin-ui/state/common';
 import { PluginLayoutStateProps } from './layout';
 import { PluginStateAnimation } from '../mol-plugin-state/animation/model';
-import { ParamDefinition as PD } from '../mol-util/param-definition';
 import { PluginConfigItem } from './config';
 
 export { PluginSpec }
@@ -49,10 +48,7 @@ namespace PluginSpec {
     }
 
     export function Behavior<T extends StateTransformer>(transformer: T, defaultParams: Partial<StateTransformer.Params<T>> = {}): Behavior {
-        const params = transformer.definition.params
-            ? PD.getDefaultValues(transformer.definition.params(undefined, undefined))
-            : {}
-        return { transformer, defaultParams: { ...params, ...defaultParams } };
+        return { transformer, defaultParams };
     }
 
     export interface LayoutControls {

+ 13 - 6
src/mol-repr/representation.ts

@@ -39,7 +39,8 @@ export type RepresentationFactory<D, P extends PD.Params, S extends Representati
 
 //
 
-export interface RepresentationProvider<D = any, P extends PD.Params = any, S extends Representation.State = any> {
+export interface RepresentationProvider<D = any, P extends PD.Params = any, S extends Representation.State = any, Id extends string = string> {
+    readonly name: Id,
     readonly label: string
     readonly description: string
     readonly factory: RepresentationFactory<D, P, S>
@@ -86,10 +87,14 @@ export class RepresentationRegistry<D, S extends Representation.State> {
 
     constructor() {};
 
-    add<P extends PD.Params>(name: string, provider: RepresentationProvider<D, P, S>) {
-        this._list.push({ name, provider })
-        this._map.set(name, provider)
-        this._name.set(provider, name)
+    add<P extends PD.Params>(provider: RepresentationProvider<D, P, S>) {
+        if (this._map.has(provider.name)) {
+            throw new Error(`${provider.name} already registered.`);
+        }
+
+        this._list.push({ name: provider.name, provider })
+        this._map.set(provider.name, provider)
+        this._name.set(provider, provider.name)
     }
 
     getName(provider: RepresentationProvider<D, any, any>): string {
@@ -97,7 +102,9 @@ export class RepresentationRegistry<D, S extends Representation.State> {
         return this._name.get(provider)!;
     }
 
-    remove(name: string) {
+    remove(provider: RepresentationProvider<D, any, any>) {
+        const name = provider.name;
+
         this._list.splice(this._list.findIndex(e => e.name === name), 1)
         const p = this._map.get(name);
         if (p) {

+ 6 - 4
src/mol-repr/structure/registry.ts

@@ -5,7 +5,7 @@
  */
 
 import { Structure } from '../../mol-model/structure';
-import { RepresentationProvider, RepresentationRegistry } from '../representation';
+import { RepresentationRegistry } from '../representation';
 import { CartoonRepresentationProvider } from './representation/cartoon';
 import { BallAndStickRepresentationProvider } from './representation/ball-and-stick';
 import { GaussianSurfaceRepresentationProvider } from './representation/gaussian-surface';
@@ -18,13 +18,14 @@ import { MolecularSurfaceRepresentationProvider } from './representation/molecul
 import { EllipsoidRepresentationProvider } from './representation/ellipsoid';
 import { OrientationRepresentationProvider } from './representation/orientation';
 import { LabelRepresentationProvider } from './representation/label';
+import { objectForEach } from '../../mol-util/object';
 
 export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> {
     constructor() {
         super()
-        Object.keys(BuiltInStructureRepresentations).forEach(name => {
-            const p = (BuiltInStructureRepresentations as { [k: string]: RepresentationProvider<Structure, any, StructureRepresentationState> })[name]
-            this.add(name, p)
+        objectForEach(BuiltInStructureRepresentations, (p, k) => {
+            if (p.name !== k) throw new Error('Fix BuiltInStructureRepresentations to have matching names.');
+            this.add(p as any)
         })
     }
 }
@@ -43,6 +44,7 @@ export const BuiltInStructureRepresentations = {
     'putty': PuttyRepresentationProvider,
     'spacefill': SpacefillRepresentationProvider,
 }
+
 export type BuiltInStructureRepresentations = typeof BuiltInStructureRepresentations
 export type BuiltInStructureRepresentationsName = keyof typeof BuiltInStructureRepresentations
 export const BuiltInStructureRepresentationsNames = Object.keys(BuiltInStructureRepresentations)

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

@@ -38,8 +38,8 @@ export const StructureRepresentationStateBuilder: Representation.StateBuilder<St
 
 export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P, StructureRepresentationState> { }
 
-export type StructureRepresentationProvider<P extends PD.Params> = RepresentationProvider<Structure, P, StructureRepresentationState>
-
+export type StructureRepresentationProvider<P extends PD.Params, Id extends string = string> = RepresentationProvider<Structure, P, StructureRepresentationState, Id>
+export function StructureRepresentationProvider<P extends PD.Params, Id extends string>(p: StructureRepresentationProvider<P, Id>): StructureRepresentationProvider<P, Id> { return p; }
 //
 
 export const StructureParams = { ...BaseGeometry.Params }

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

@@ -41,7 +41,8 @@ export function BallAndStickRepresentation(ctx: RepresentationContext, getParams
     return Representation.createMulti('Ball & Stick', ctx, getParams, StructureRepresentationStateBuilder, BallAndStickVisuals as unknown as Representation.Def<Structure, BallAndStickParams>)
 }
 
-export const BallAndStickRepresentationProvider: StructureRepresentationProvider<BallAndStickParams> = {
+export const BallAndStickRepresentationProvider = StructureRepresentationProvider({
+    name: 'ball-and-stick',
     label: 'Ball & Stick',
     description: 'Displays atoms as spheres and bonds as cylinders.',
     factory: BallAndStickRepresentation,
@@ -50,4 +51,4 @@ export const BallAndStickRepresentationProvider: StructureRepresentationProvider
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'physical' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/carbohydrate.ts

@@ -36,7 +36,8 @@ export function CarbohydrateRepresentation(ctx: RepresentationContext, getParams
     return Representation.createMulti('Carbohydrate', ctx, getParams, StructureRepresentationStateBuilder, CarbohydrateVisuals as unknown as Representation.Def<Structure, CarbohydrateParams>)
 }
 
-export const CarbohydrateRepresentationProvider: StructureRepresentationProvider<CarbohydrateParams> = {
+export const CarbohydrateRepresentationProvider = StructureRepresentationProvider({
+    name: 'carbohydrate',
     label: 'Carbohydrate',
     description: 'Displays carbohydrate symbols (3D SNFG).',
     factory: CarbohydrateRepresentation,
@@ -45,4 +46,4 @@ export const CarbohydrateRepresentationProvider: StructureRepresentationProvider
     defaultColorTheme: { name: 'carbohydrate-symbol' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.carbohydrates.elements.length > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/cartoon.ts

@@ -55,7 +55,8 @@ export function CartoonRepresentation(ctx: RepresentationContext, getParams: Rep
     return Representation.createMulti('Cartoon', ctx, getParams, StructureRepresentationStateBuilder, CartoonVisuals as unknown as Representation.Def<Structure, CartoonParams>)
 }
 
-export const CartoonRepresentationProvider: StructureRepresentationProvider<CartoonParams> = {
+export const CartoonRepresentationProvider = StructureRepresentationProvider({
+    name: 'cartoon',
     label: 'Cartoon',
     description: 'Displays ribbons, planks, tubes smoothly following the trace atoms of polymers.',
     factory: CartoonRepresentation,
@@ -68,4 +69,4 @@ export const CartoonRepresentationProvider: StructureRepresentationProvider<Cart
         attach: (ctx: CustomProperty.Context, structure: Structure) => SecondaryStructureProvider.attach(ctx, structure, void 0, true),
         detach: (_, data) => SecondaryStructureProvider.ref(data, false)
     }
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/ellipsoid.ts

@@ -41,7 +41,8 @@ export function EllipsoidRepresentation(ctx: RepresentationContext, getParams: R
     return Representation.createMulti('Ellipsoid', ctx, getParams, StructureRepresentationStateBuilder, EllipsoidVisuals as unknown as Representation.Def<Structure, EllipsoidParams>)
 }
 
-export const EllipsoidRepresentationProvider: StructureRepresentationProvider<EllipsoidParams> = {
+export const EllipsoidRepresentationProvider = StructureRepresentationProvider({
+    name: 'ellipsoid',
     label: 'Ellipsoid',
     description: 'Displays anisotropic displacement ellipsoids of atomic elements plus bonds as cylinders.',
     factory: EllipsoidRepresentation,
@@ -50,4 +51,4 @@ export const EllipsoidRepresentationProvider: StructureRepresentationProvider<El
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => m.customProperties.has(AtomSiteAnisotrop.Descriptor))
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/gaussian-surface.ts

@@ -35,7 +35,8 @@ export function GaussianSurfaceRepresentation(ctx: RepresentationContext, getPar
     return Representation.createMulti('Gaussian Surface', ctx, getParams, StructureRepresentationStateBuilder, GaussianSurfaceVisuals as unknown as Representation.Def<Structure, GaussianSurfaceParams>)
 }
 
-export const GaussianSurfaceRepresentationProvider: StructureRepresentationProvider<GaussianSurfaceParams> = {
+export const GaussianSurfaceRepresentationProvider = StructureRepresentationProvider({
+    name: 'gaussian-surface',
     label: 'Gaussian Surface',
     description: 'Displays a gaussian molecular surface.',
     factory: GaussianSurfaceRepresentation,
@@ -44,4 +45,4 @@ export const GaussianSurfaceRepresentationProvider: StructureRepresentationProvi
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/gaussian-volume.ts

@@ -28,7 +28,8 @@ export function GaussianVolumeRepresentation(ctx: RepresentationContext, getPara
     return Representation.createMulti('Gaussian Volume', ctx, getParams, StructureRepresentationStateBuilder, GaussianVolumeVisuals as unknown as Representation.Def<Structure, GaussianVolumeParams>)
 }
 
-export const GaussianVolumeRepresentationProvider: StructureRepresentationProvider<GaussianVolumeParams> = {
+export const GaussianVolumeRepresentationProvider = StructureRepresentationProvider({
+    name: 'gaussian-volume',
     label: 'Gaussian Volume',
     description: 'Displays a gaussian molecular density using direct volume rendering.',
     factory: GaussianVolumeRepresentation,
@@ -37,4 +38,4 @@ export const GaussianVolumeRepresentationProvider: StructureRepresentationProvid
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/label.ts

@@ -32,7 +32,8 @@ export function LabelRepresentation(ctx: RepresentationContext, getParams: Repre
     return repr
 }
 
-export const LabelRepresentationProvider: StructureRepresentationProvider<LabelParams> = {
+export const LabelRepresentationProvider = StructureRepresentationProvider({
+    name: 'label',
     label: 'Label',
     description: 'Displays labels.',
     factory: LabelRepresentation,
@@ -41,4 +42,4 @@ export const LabelRepresentationProvider: StructureRepresentationProvider<LabelP
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/molecular-surface.ts

@@ -33,7 +33,8 @@ export function MolecularSurfaceRepresentation(ctx: RepresentationContext, getPa
     return Representation.createMulti('Molecular Surface', ctx, getParams, StructureRepresentationStateBuilder, MolecularSurfaceVisuals as unknown as Representation.Def<Structure, MolecularSurfaceParams>)
 }
 
-export const MolecularSurfaceRepresentationProvider: StructureRepresentationProvider<MolecularSurfaceParams> = {
+export const MolecularSurfaceRepresentationProvider = StructureRepresentationProvider({
+    name: 'molecular-surface',
     label: 'Molecular Surface',
     description: 'Displays a molecular surface.',
     factory: MolecularSurfaceRepresentation,
@@ -42,4 +43,4 @@ export const MolecularSurfaceRepresentationProvider: StructureRepresentationProv
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/orientation.ts

@@ -30,7 +30,8 @@ export function OrientationRepresentation(ctx: RepresentationContext, getParams:
     return Representation.createMulti('Orientation', ctx, getParams, StructureRepresentationStateBuilder, OrientationVisuals as unknown as Representation.Def<Structure, OrientationParams>)
 }
 
-export const OrientationRepresentationProvider: StructureRepresentationProvider<OrientationParams> = {
+export const OrientationRepresentationProvider = StructureRepresentationProvider({
+    name: 'orientation',
     label: 'Orientation',
     description: 'Displays orientation ellipsoids for polymer chains.',
     factory: OrientationRepresentation,
@@ -39,4 +40,4 @@ export const OrientationRepresentationProvider: StructureRepresentationProvider<
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/point.ts

@@ -31,7 +31,8 @@ export function PointRepresentation(ctx: RepresentationContext, getParams: Repre
     return Representation.createMulti('Point', ctx, getParams, StructureRepresentationStateBuilder, PointVisuals as unknown as Representation.Def<Structure, PointParams>)
 }
 
-export const PointRepresentationProvider: StructureRepresentationProvider<PointParams> = {
+export const PointRepresentationProvider = StructureRepresentationProvider({
+    name: 'point',
     label: 'Point',
     description: 'Displays elements (atoms, coarse spheres) as spheres.',
     factory: PointRepresentation,
@@ -40,4 +41,4 @@ export const PointRepresentationProvider: StructureRepresentationProvider<PointP
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'physical' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/putty.ts

@@ -43,7 +43,8 @@ export function PuttyRepresentation(ctx: RepresentationContext, getParams: Repre
     return Representation.createMulti('Putty', ctx, getParams, StructureRepresentationStateBuilder, PuttyVisuals as unknown as Representation.Def<Structure, PuttyParams>)
 }
 
-export const PuttyRepresentationProvider: StructureRepresentationProvider<PuttyParams> = {
+export const PuttyRepresentationProvider = StructureRepresentationProvider({
+    name: 'putty',
     label: 'Putty',
     description: 'Displays a tube smoothly following the trace atoms of polymers.',
     factory: PuttyRepresentation,
@@ -52,4 +53,4 @@ export const PuttyRepresentationProvider: StructureRepresentationProvider<PuttyP
     defaultColorTheme: { name: 'polymer-id' },
     defaultSizeTheme: { name: 'uncertainty' },
     isApplicable: (structure: Structure) => structure.polymerResidueCount > 0
-}
+})

+ 3 - 2
src/mol-repr/structure/representation/spacefill.ts

@@ -31,7 +31,8 @@ export function SpacefillRepresentation(ctx: RepresentationContext, getParams: R
     return Representation.createMulti('Spacefill', ctx, getParams, StructureRepresentationStateBuilder, SpacefillVisuals as unknown as Representation.Def<Structure, SpacefillParams>)
 }
 
-export const SpacefillRepresentationProvider: StructureRepresentationProvider<SpacefillParams> = {
+export const SpacefillRepresentationProvider = StructureRepresentationProvider({
+    name: 'spacefill',
     label: 'Spacefill',
     description: 'Displays atomic/coarse elements as spheres.',
     factory: SpacefillRepresentation,
@@ -40,4 +41,4 @@ export const SpacefillRepresentationProvider: StructureRepresentationProvider<Sp
     defaultColorTheme: { name: 'element-symbol' },
     defaultSizeTheme: { name: 'physical' },
     isApplicable: (structure: Structure) => structure.elementCount > 0
-}
+})

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

@@ -179,7 +179,8 @@ export function DirectVolumeRepresentation(ctx: RepresentationContext, getParams
     return VolumeRepresentation('Direct Volume', ctx, getParams, DirectVolumeVisual)
 }
 
-export const DirectVolumeRepresentationProvider: VolumeRepresentationProvider<DirectVolumeParams> = {
+export const DirectVolumeRepresentationProvider = VolumeRepresentationProvider({
+    name: 'direct-volume',
     label: 'Direct Volume',
     description: 'Direct volume rendering of volumetric data.',
     factory: DirectVolumeRepresentation,
@@ -188,4 +189,4 @@ export const DirectVolumeRepresentationProvider: VolumeRepresentationProvider<Di
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (volume: VolumeData) => volume.data.data.length > 0
-}
+})

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

@@ -164,7 +164,8 @@ export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams:
     return Representation.createMulti('Isosurface', ctx, getParams, Representation.StateBuilder, IsosurfaceVisuals as unknown as Representation.Def<VolumeData, IsosurfaceParams>)
 }
 
-export const IsosurfaceRepresentationProvider: VolumeRepresentationProvider<IsosurfaceParams> = {
+export const IsosurfaceRepresentationProvider = VolumeRepresentationProvider({
+    name: 'isosurface',
     label: 'Isosurface',
     description: 'Displays an isosurface of volumetric data.',
     factory: IsosurfaceRepresentation,
@@ -173,4 +174,4 @@ export const IsosurfaceRepresentationProvider: VolumeRepresentationProvider<Isos
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (volume: VolumeData) => volume.data.data.length > 0
-}
+})

+ 6 - 3
src/mol-repr/volume/registry.ts

@@ -4,16 +4,19 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { RepresentationProvider, RepresentationRegistry, Representation } from '../representation';
+import { RepresentationRegistry, Representation } from '../representation';
 import { VolumeData } from '../../mol-model/volume';
 import { IsosurfaceRepresentationProvider } from './isosurface';
+import { objectForEach } from '../../mol-util/object';
 
 export class VolumeRepresentationRegistry extends RepresentationRegistry<VolumeData, Representation.State> {
     constructor() {
         super()
         Object.keys(BuiltInVolumeRepresentations).forEach(name => {
-            const p = (BuiltInVolumeRepresentations as { [k: string]: RepresentationProvider<VolumeData, any, Representation.State> })[name]
-            this.add(name, p)
+            objectForEach(BuiltInVolumeRepresentations, (p, k) => {
+                if (p.name !== k) throw new Error('Fix BuiltInVolumeRepresentations to have matching names.');
+                this.add(p as any)
+            })
         })
     }
 }

+ 2 - 1
src/mol-repr/volume/representation.ts

@@ -200,7 +200,8 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
 
 export interface VolumeRepresentation<P extends VolumeParams> extends Representation<VolumeData, P> { }
 
-export type VolumeRepresentationProvider<P extends VolumeParams> = RepresentationProvider<VolumeData, P, Representation.State>
+export type VolumeRepresentationProvider<P extends VolumeParams, Id extends string = string> = RepresentationProvider<VolumeData, P, Representation.State, Id>
+export function VolumeRepresentationProvider<P extends VolumeParams, Id extends string>(p: VolumeRepresentationProvider<P, Id>): VolumeRepresentationProvider<P, Id> { return p; }
 
 //
 

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

@@ -199,8 +199,8 @@ export namespace ParamDefinition {
         isExpanded?: boolean,
         isFlat?: boolean
     }
-    export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean }): Group<Normalize<T>> {
-        const ret = setInfo<Group<Normalize<T>>>({ type: 'group', defaultValue: getDefaultValues(params as any as Params) as any, params: params as any as Params }, info);
+    export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any }): Group<Normalize<T>> {
+        const ret = setInfo<Group<Normalize<T>>>({ type: 'group', defaultValue: info?.customDefault || getDefaultValues(params as any as Params) as any, params: params as any as Params }, info);
         if (info && info.isExpanded) ret.isExpanded = info.isExpanded;
         if (info && info.isFlat) ret.isFlat = info.isFlat;
         return ret;