Bladeren bron

mol-state: param definition

David Sehnal 6 jaren geleden
bovenliggende
commit
e1efe21897

+ 2 - 1
src/mol-plugin/behavior/behavior.ts

@@ -10,6 +10,7 @@ import { Task } from 'mol-task';
 import { PluginContext } from 'mol-plugin/context';
 import { PluginCommand } from '../command';
 import { Observable } from 'rxjs';
+import { ParamDefinition } from 'mol-util/param-definition';
 
 export { PluginBehavior }
 
@@ -36,7 +37,7 @@ namespace PluginBehavior {
             group: string,
             description?: string
         },
-        params?: Transformer.Definition<Root, Behavior, P>['params'],
+        params(a: Root, globalCtx: PluginContext): { [K in keyof P]: ParamDefinition.Any }
     }
 
     export function create<P>(params: CreateParams<P>) {

+ 4 - 2
src/mol-plugin/behavior/dynamic/representation.ts

@@ -25,7 +25,8 @@ export const HighlightLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Highlight Loci on Canvas', group: 'Data' }
+    display: { name: 'Highlight Loci on Canvas', group: 'Data' },
+    params: () => ({})
 });
 
 export const SelectLoci = PluginBehavior.create({
@@ -38,5 +39,6 @@ export const SelectLoci = PluginBehavior.create({
             });
         }
     },
-    display: { name: 'Select Loci on Canvas', group: 'Data' }
+    display: { name: 'Select Loci on Canvas', group: 'Data' },
+    params: () => ({})
 });

+ 5 - 10
src/mol-plugin/state/actions/basic.ts

@@ -17,13 +17,7 @@ export const CreateStructureFromPDBe = StateAction.create<PluginStateObject.Root
         name: 'Entry from PDBe',
         description: 'Download a structure from PDBe and create its default Assembly and visual'
     },
-    params: {
-        default: () => ({ id: '1grm' }),
-        definition: () => ({
-            id: PD.Text('1grm', { label: 'PDB id' }),
-        }),
-        // validate: p => !p.id || !p.id.trim() ? [['Enter id.', 'id']] : void 0
-    },
+    params: () => ({ id: PD.Text('1grm', { label: 'PDB id' }) }),
     apply({ params, state }) {
         const url = `http://www.ebi.ac.uk/pdbe/static/entry/${params.id.toLowerCase()}_updated.cif`;
         const b = state.build();
@@ -63,9 +57,10 @@ export const UpdateTrajectory = StateAction.create<PluginStateObject.Root, void,
     display: {
         name: 'Update Trajectory'
     },
-    params: {
-        default: () => ({ action: 'reset', by: 1 })
-    },
+    params: () => ({
+        action: PD.Select('advance', [['advance', 'Advance'], ['reset', 'Reset']]),
+        by: PD.Numeric(1, { min: -1, max: 1, step: 1 }, { isOptional: true })
+    }),
     apply({ params, state }) {
         const models = state.select(q => q.rootsOfType(PluginStateObject.Molecule.Model).filter(c => c.transform.transformer === StateTransforms.Model.CreateModelFromTrajectory));
 

+ 6 - 11
src/mol-plugin/state/transforms/data.ts

@@ -22,17 +22,11 @@ const Download = PluginStateTransform.Create<SO.Root, SO.Data.String | SO.Data.B
     },
     from: [SO.Root],
     to: [SO.Data.String, SO.Data.Binary],
-    params: {
-        default: () => ({
-            url: 'https://www.ebi.ac.uk/pdbe/static/entry/1cbs_updated.cif'
-        }),
-        definition: () => ({
-            url: PD.Text('', { description: 'Resource URL. Must be the same domain or support CORS.' }),
-            label: PD.Text(''),
-            isBinary: PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)' })
-        }),
-        // validate: p => !p.url || !p.url.trim() ? [['Enter url.', 'url']] : void 0
-    },
+    params: () => ({
+        url: PD.Text('https://www.ebi.ac.uk/pdbe/static/entry/1cbs_updated.cif', { description: 'Resource URL. Must be the same domain or support CORS.' }),
+        label: PD.Text('', { isOptional: true }),
+        isBinary: PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)', isOptional: true })
+    }),
     apply({ params: p }, globalCtx: PluginContext) {
         return Task.create('Download', async ctx => {
             // TODO: track progress
@@ -62,6 +56,7 @@ const ParseCif = PluginStateTransform.Create<SO.Data.String | SO.Data.Binary, SO
     },
     from: [SO.Data.String, SO.Data.Binary],
     to: [SO.Data.Cif],
+    params: () => ({}),
     apply({ a }) {
         return Task.create('Parse CIF', async ctx => {
             const parsed = await (SO.Data.String.is(a) ? CIF.parse(a.data) : CIF.parseBinary(a.data)).runInContext(ctx);

+ 18 - 20
src/mol-plugin/state/transforms/model.ts

@@ -12,6 +12,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition';
 import Expression from 'mol-script/language/expression';
 import { compile } from 'mol-script/runtime/query/compiler';
 import { Mat4 } from 'mol-math/linear-algebra';
+import { MolScriptBuilder } from 'mol-script/language/builder';
 
 export { ParseTrajectoryFromMmCif }
 namespace ParseTrajectoryFromMmCif { export interface Params { blockHeader?: string } }
@@ -23,16 +24,14 @@ const ParseTrajectoryFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Mol
     },
     from: [SO.Data.Cif],
     to: [SO.Molecule.Trajectory],
-    params: {
-        default: a => ({ blockHeader: a.data.blocks[0].header }),
-        definition(a) {
-            const { blocks } = a.data;
-            if (blocks.length === 0) return {};
-            return {
-                blockHeader: PD.Select(blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })
-            };
-        }
+    params(a) {
+        const { blocks } = a.data;
+        if (blocks.length === 0) return { };
+        return {
+            blockHeader: PD.Select(blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })
+        };
     },
+    isApplicable: a => a.data.blocks.length > 0,
     apply({ a, params }) {
         return Task.create('Parse mmCIF', async ctx => {
             const header = params.blockHeader || a.data.blocks[0].header;
@@ -57,10 +56,7 @@ const CreateModelFromTrajectory = PluginStateTransform.Create<SO.Molecule.Trajec
     },
     from: [SO.Molecule.Trajectory],
     to: [SO.Molecule.Model],
-    params: {
-        default: () => ({ modelIndex: 0 }),
-        definition: a => ({ modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) })
-    },
+    params: a => ({ modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) }),
     isApplicable: a => a.data.length > 0,
     apply({ a, params }) {
         if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
@@ -80,6 +76,7 @@ const CreateStructure = PluginStateTransform.Create<SO.Molecule.Model, SO.Molecu
     },
     from: [SO.Molecule.Model],
     to: [SO.Molecule.Structure],
+    params: () => ({}),
     apply({ a, params }) {
         let s = Structure.ofModel(a.data);
         if (params.transform3d) s = Structure.transform(s, params.transform3d);
@@ -102,13 +99,10 @@ const CreateStructureAssembly = PluginStateTransform.Create<SO.Molecule.Model, S
     },
     from: [SO.Molecule.Model],
     to: [SO.Molecule.Structure],
-    params: {
-        default: () => ({ id: void 0 }),
-        definition(a) {
-            const model = a.data;
-            const ids = model.symmetry.assemblies.map(a => [a.id, a.id] as [string, string]);
-            return { id: PD.Select(ids.length ? ids[0][0] : '', ids, { label: 'Asm Id', description: 'Assembly Id' }) };
-        }
+    params(a) {
+        const model = a.data;
+        const ids = model.symmetry.assemblies.map(a => [a.id, a.id] as [string, string]);
+        return { id: PD.Select(ids.length ? ids[0][0] : '', ids, { label: 'Asm Id', description: 'Assembly Id' }) };
     },
     apply({ a, params }) {
         return Task.create('Build Assembly', async ctx => {
@@ -136,6 +130,10 @@ const CreateStructureSelection = PluginStateTransform.Create<SO.Molecule.Structu
     },
     from: [SO.Molecule.Structure],
     to: [SO.Molecule.Structure],
+    params: () => ({
+        query: PD.Value<Expression>(MolScriptBuilder.struct.generator.all),
+        label: PD.Text('', { isOptional: true })
+    }),
     apply({ a, params }) {
         // TODO: use cache, add "update"
         const compiled = compile<StructureSelection>(params.query);

+ 10 - 19
src/mol-plugin/state/transforms/visuals.ts

@@ -25,25 +25,16 @@ const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.St
     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: ctx.structureReprensentation.registry.default.provider.defaultValues
-            }
-        }),
-        definition: (a, ctx: PluginContext) => ({
-            type: PD.Mapped(
-                ctx.structureReprensentation.registry.default.name,
-                ctx.structureReprensentation.registry.types,
-                name => PD.Group(
-                    ctx.structureReprensentation.registry.get(name).getParams(ctx.structureReprensentation.themeCtx, a.data),
-                    { label: 'Type Parameters' }
-                ),
-                { label: 'Type' }
-            )
-        })
-    },
+    params: (a, ctx: PluginContext) => ({
+        type: PD.Mapped(
+            ctx.structureReprensentation.registry.default.name,
+            ctx.structureReprensentation.registry.types,
+            name => PD.Group<any>(
+                ctx.structureReprensentation.registry.get(name).getParams(ctx.structureReprensentation.themeCtx, a.data),
+                { label: 'Type Parameters' }
+            ),
+            { label: 'Type' })
+    }),
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
             const provider = plugin.structureReprensentation.registry.get(params.type.name)

+ 3 - 3
src/mol-plugin/ui/state/apply-action.tsx

@@ -10,6 +10,7 @@ import { State, Transform } from 'mol-state';
 import { StateAction } from 'mol-state/action';
 import { memoizeOne } from 'mol-util/memoize';
 import { StateTransformParameters, TransformContolBase } from './common';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
 
 export { ApplyActionContol };
 
@@ -55,13 +56,12 @@ class ApplyActionContol extends TransformContolBase<ApplyActionContol.Props, App
         if (version === state.version) return null;
 
         const source = props.state.cells.get(props.nodeRef)!.obj!;
-        const definition = props.action.definition.params || { };
-        const initialValues = definition.default ? definition.default(source, props.plugin) : {};
+        const params = PD.getDefaultValues(props.action.definition.params(source, props.plugin));
 
         const newState: Partial<ApplyActionContol.ComponentState> = {
             ref: props.nodeRef,
             version,
-            params: initialValues,
+            params,
             isInitial: true,
             error: void 0
         };

+ 8 - 11
src/mol-plugin/ui/state/common.tsx

@@ -17,9 +17,7 @@ export { StateTransformParameters, TransformContolBase };
 
 class StateTransformParameters extends PurePluginComponent<StateTransformParameters.Props> {
     getDefinition() {
-        const controls = this.props.info.definition.definition;
-        if (!controls) return { };
-        return controls!(this.props.info.source, this.plugin)
+        return this.props.info.createDefinition(this.props.info.source, this.plugin)
     }
 
     validate(params: any) {
@@ -45,7 +43,7 @@ class StateTransformParameters extends PurePluginComponent<StateTransformParamet
 namespace StateTransformParameters {
     export interface Props {
         info: {
-            definition: Transformer.ParamsDefinition,
+            createDefinition: Transformer.ParamsDefinition,
             params: PD.Params,
             initialValues: any,
             source: StateObject,
@@ -63,12 +61,11 @@ namespace StateTransformParameters {
 
     export function infoFromAction(plugin: PluginContext, state: State, action: StateAction, nodeRef: Transform.Ref): Props['info'] {
         const source = state.cells.get(nodeRef)!.obj!;
-        const definition = action.definition.params || { };
-        const initialValues = definition.default ? definition.default(source, plugin) : {};
-        const params = definition.definition ? definition.definition(source, plugin) : {};
+        const params = action.definition.params(source, plugin);
+        const initialValues = PD.getDefaultValues(params);
         return {
             source,
-            definition: action.definition.params || { },
+            createDefinition: action.definition.params,
             initialValues,
             params,
             isEmpty: Object.keys(params).length === 0
@@ -78,11 +75,11 @@ namespace StateTransformParameters {
     export function infoFromTransform(plugin: PluginContext, state: State, transform: Transform): Props['info'] {
         const cell = state.cells.get(transform.ref)!;
         const source: StateObjectCell | undefined = (cell.sourceRef && state.cells.get(cell.sourceRef)!) || void 0;
-        const definition = transform.transformer.definition.params || { };
-        const params = definition.definition ? definition.definition((source && source.obj) as any, plugin) : {};
+        const createDefinition = transform.transformer.definition.params;
+        const params = createDefinition((source && source.obj) as any, plugin);
         return {
             source: (source && source.obj) as any,
-            definition,
+            createDefinition,
             initialValues: transform.params,
             params,
             isEmpty: Object.keys(params).length === 0

+ 2 - 2
src/mol-state/action.ts

@@ -46,7 +46,7 @@ namespace StateAction {
          */
         apply(params: ApplyParams<A, P>, globalCtx: unknown): T | Task<T>,
 
-        readonly params?: Transformer.ParamsDefinition<A, P>
+        params(a: A, globalCtx: unknown): { [K in keyof P]: PD.Any },
 
         /** Test if the transform can be applied to a given node */
         isApplicable?(a: A, globalCtx: unknown): boolean
@@ -66,7 +66,7 @@ namespace StateAction {
         return create<Transformer.From<T>, void, Transformer.Params<T>>({
             from: def.from,
             display: def.display,
-            params: def.params as Transformer<Transformer.From<T>, any, Transformer.Params<T>>['definition']['params'],
+            params: def.params as Transformer.ParamsDefinition<Transformer.From<T>, Transformer.Params<T>>,
             apply({ cell, state, params }) {
                 const tree = state.build().to(cell.transform.ref).apply(transformer, params);
                 return state.update(tree);

+ 6 - 10
src/mol-state/transformer.ts

@@ -23,7 +23,6 @@ export namespace Transformer {
     export type Params<T extends Transformer<any, any, any>> = T extends Transformer<any, any, infer P> ? P : unknown;
     export type From<T extends Transformer<any, any, any>> = T extends Transformer<infer A, any, any> ? A : unknown;
     export type To<T extends Transformer<any, any, any>> = T extends Transformer<any, infer B, any> ? B : unknown;
-    export type ControlsFor<Props> = { [P in keyof Props]?: PD.Any }
 
     export function is(obj: any): obj is Transformer {
         return !!obj && typeof (obj as Transformer).toAction === 'function' && typeof (obj as Transformer).apply === 'function';
@@ -47,14 +46,10 @@ export namespace Transformer {
 
     export enum UpdateResult { Unchanged, Updated, Recreate }
 
-    export interface ParamsDefinition<A extends StateObject = StateObject, P = any> {
-        /** Check the parameters and return a list of errors if the are not valid. */
-        default?(a: A, globalCtx: unknown): P,
-        /** Specify default control descriptors for the parameters */
-        definition?(a: A, globalCtx: unknown): { [K in keyof P]?: PD.Any },
-        /** Optional custom parameter equality. Use shallow structural equal by default. */
-        areEqual?(oldParams: P, newParams: P): boolean
-    }
+    export const EmptyParams = () => ({});
+
+    /** Specify default control descriptors for the parameters */
+    export type ParamsDefinition<A extends StateObject = StateObject, P = any> = (a: A, globalCtx: unknown) => { [K in keyof P]: PD.Any }
 
     export interface Definition<A extends StateObject = StateObject, B extends StateObject = StateObject, P extends {} = {}> {
         readonly name: string,
@@ -75,7 +70,7 @@ export namespace Transformer {
          */
         update?(params: UpdateParams<A, B, P>, globalCtx: unknown): Task<UpdateResult> | UpdateResult,
 
-        readonly params?: ParamsDefinition<A, P>,
+        params(a: A, globalCtx: unknown): { [K in keyof P]: PD.Any },
 
         /** Test if the transform can be applied to a given node */
         isApplicable?(a: A, globalCtx: unknown): boolean,
@@ -141,6 +136,7 @@ export namespace Transformer {
         name: 'root',
         from: [],
         to: [],
+        params: EmptyParams,
         apply() { throw new Error('should never be applied'); },
         update() { return UpdateResult.Unchanged; }
     })

+ 3 - 7
src/mol-state/tree/transient.ts

@@ -138,13 +138,9 @@ class TransientTree implements StateTree {
         ensurePresent(this.transforms, ref);
 
         const transform = this.transforms.get(ref)!;
-        const def = transform.transformer.definition;
-        if (def.params && def.params.areEqual) {
-            if (def.params.areEqual(transform.params, params)) return false;
-        } else {
-            if (shallowEqual(transform.params, params)) {
-                return false;
-            }
+        // TODO: should this be here?
+        if (shallowEqual(transform.params, params)) {
+            return false;
         }
 
         if (!this.changedNodes) {

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

@@ -12,14 +12,16 @@ import { camelCaseToWords } from './string';
 
 export namespace ParamDefinition {
     export interface Info {
-        label?: string
-        description?: string
+        label?: string,
+        description?: string,
+        isOptional?: boolean
     }
 
     function setInfo<T extends Info>(param: T, info?: Info): T {
         if (!info) return param;
         if (info.description) param.description = info.description;
         if (info.label) param.label = info.label;
+        if (info.isOptional) param.isOptional = info.isOptional;
         return param;
     }