Browse Source

mol-plugin: refactor state transformers

David Sehnal 6 years ago
parent
commit
cfeb20038d

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

@@ -43,7 +43,7 @@ namespace PluginBehavior {
 
     export function create<P>(params: CreateParams<P>) {
         // TODO: cache groups etc
-        return PluginStateTransform.Create<Root, Behavior, P>({
+        return PluginStateTransform.CreateBuiltIn<Root, Behavior, P>({
             name: params.name,
             display: params.display,
             from: [Root],

+ 7 - 4
src/mol-plugin/state/objects.ts

@@ -46,8 +46,6 @@ export namespace PluginStateObject {
     export namespace Data {
         export class String extends Create<string>({ name: 'String Data', typeClass: 'Data', }) { }
         export class Binary extends Create<Uint8Array>({ name: 'Binary Data', typeClass: 'Data' }) { }
-        export class Json extends Create<any>({ name: 'JSON Data', typeClass: 'Data' }) { }
-        export class Cif extends Create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { }
 
         // TODO
         // export class MultipleRaw extends Create<{
@@ -55,6 +53,11 @@ export namespace PluginStateObject {
         // }>({ name: 'Data', typeClass: 'Data', shortName: 'MD', description: 'Multiple Keyed Data.' }) { }
     }
 
+    export namespace Format {
+        export class Json extends Create<any>({ name: 'JSON Data', typeClass: 'Data' }) { }
+        export class Cif extends Create<CifFile>({ name: 'CIF File', typeClass: 'Data' }) { }
+    }
+
     export namespace Molecule {
         export class Trajectory extends Create<ReadonlyArray<_Model>>({ name: 'Trajectory', typeClass: 'Object' }) { }
         export class Model extends Create<_Model>({ name: 'Model', typeClass: 'Object' }) { }
@@ -69,6 +72,6 @@ export namespace PluginStateObject {
 }
 
 export namespace PluginStateTransform {
-    export const Create = Transformer.factory('ms-plugin');
-    export const BuiltIn = Transformer.factory1('ms-plugin');
+    export const CreateBuiltIn = Transformer.factory('ms-plugin');
+    export const BuiltIn = Transformer.builderFactory('ms-plugin');
 }

+ 3 - 3
src/mol-plugin/state/transforms/data.ts

@@ -57,7 +57,7 @@ const ReadFile = PluginStateTransform.BuiltIn({
         file: PD.File(),
         label: PD.makeOptional(PD.Text('')),
         isBinary: PD.makeOptional(PD.Boolean(false, { description: 'If true, open file as as binary (string otherwise)' }))
-    }
+    },
 })({
     display: {
         name: 'Read File',
@@ -86,7 +86,7 @@ type ParseCif = typeof ParseCif
 const ParseCif = PluginStateTransform.BuiltIn({
     name: 'parse-cif',
     from: [SO.Data.String, SO.Data.Binary],
-    to: SO.Data.Cif
+    to: SO.Format.Cif
 })({
     display: {
         name: 'Parse CIF',
@@ -96,7 +96,7 @@ const ParseCif = PluginStateTransform.BuiltIn({
         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);
             if (parsed.isError) throw new Error(parsed.message);
-            return new SO.Data.Cif(parsed.result);
+            return new SO.Format.Cif(parsed.result);
         });
     }
 });

+ 48 - 42
src/mol-plugin/state/transforms/model.ts

@@ -16,22 +16,22 @@ import { StateObject } from 'mol-state';
 import { PluginContext } from 'mol-plugin/context';
 
 export { TrajectoryFromMmCif }
-namespace TrajectoryFromMmCif { export interface Params { blockHeader?: string } }
-const TrajectoryFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Molecule.Trajectory, TrajectoryFromMmCif.Params>({
+type TrajectoryFromMmCif = typeof TrajectoryFromMmCif
+const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({
     name: 'trajectory-from-mmcif',
-    display: {
-        name: 'Models from mmCIF',
-        description: 'Identify and create all separate models in the specified CIF data block'
-    },
-    from: [SO.Data.Cif],
-    to: [SO.Molecule.Trajectory],
+    from: SO.Format.Cif,
+    to: SO.Molecule.Trajectory,
     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' })
+            blockHeader: PD.makeOptional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' }))
         };
     },
+})({
+    display: {
+        name: 'Models from mmCIF',
+        description: 'Identify and create all separate models in the specified CIF data block'
+    },
     isApplicable: a => a.data.blocks.length > 0,
     apply({ a, params }) {
         return Task.create('Parse mmCIF', async ctx => {
@@ -48,16 +48,17 @@ const TrajectoryFromMmCif = PluginStateTransform.Create<SO.Data.Cif, SO.Molecule
 
 export { ModelFromTrajectory }
 const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1;
-namespace ModelFromTrajectory { export interface Params { modelIndex: number } }
-const ModelFromTrajectory = PluginStateTransform.Create<SO.Molecule.Trajectory, SO.Molecule.Model, ModelFromTrajectory.Params>({
+type ModelFromTrajectory = typeof ModelFromTrajectory
+const ModelFromTrajectory = PluginStateTransform.BuiltIn({
     name: 'model-from-trajectory',
+    from: SO.Molecule.Trajectory,
+    to: SO.Molecule.Model,
+    params: a => ({ modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) })
+})({
     display: {
         name: 'Model from Trajectory',
         description: 'Create a molecular structure from the specified model.'
     },
-    from: [SO.Molecule.Trajectory],
-    to: [SO.Molecule.Model],
-    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}`);
@@ -68,15 +69,16 @@ const ModelFromTrajectory = PluginStateTransform.Create<SO.Molecule.Trajectory,
 });
 
 export { StructureFromModel }
-namespace StructureFromModel { export interface Params { } }
-const StructureFromModel = PluginStateTransform.Create<SO.Molecule.Model, SO.Molecule.Structure, StructureFromModel.Params>({
+type StructureFromModel = typeof StructureFromModel
+const StructureFromModel = PluginStateTransform.BuiltIn({
     name: 'structure-from-model',
+    from: SO.Molecule.Model,
+    to: SO.Molecule.Structure,
+})({
     display: {
         name: 'Structure from Model',
         description: 'Create a molecular structure from the specified model.'
     },
-    from: [SO.Molecule.Model],
-    to: [SO.Molecule.Structure],
     apply({ a }) {
         let s = Structure.ofModel(a.data);
         const label = { label: a.data.label, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` };
@@ -89,19 +91,20 @@ function structureDesc(s: Structure) {
 }
 
 export { StructureAssemblyFromModel }
-namespace StructureAssemblyFromModel { export interface Params { /** if not specified, use the 1st */ id?: string } }
-const StructureAssemblyFromModel = PluginStateTransform.Create<SO.Molecule.Model, SO.Molecule.Structure, StructureAssemblyFromModel.Params>({
+type StructureAssemblyFromModel = typeof StructureAssemblyFromModel
+const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({
     name: 'structure-assembly-from-model',
-    display: {
-        name: 'Structure Assembly',
-        description: 'Create a molecular structure assembly.'
-    },
-    from: [SO.Molecule.Model],
-    to: [SO.Molecule.Structure],
+    from: SO.Molecule.Model,
+    to: SO.Molecule.Structure,
     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' }) };
+        return { id: PD.makeOptional(PD.Select(ids.length ? ids[0][0] : '', ids, { label: 'Asm Id', description: 'Assembly Id' })) };
+    }
+})({
+    display: {
+        name: 'Structure Assembly',
+        description: 'Create a molecular structure assembly.'
     },
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('Build Assembly', async ctx => {
@@ -126,19 +129,20 @@ const StructureAssemblyFromModel = PluginStateTransform.Create<SO.Molecule.Model
 });
 
 export { StructureSelection }
-namespace StructureSelection { export interface Params { query: Expression, label?: string } }
-const StructureSelection = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Structure, StructureSelection.Params>({
+type StructureSelection = typeof StructureSelection
+const StructureSelection = PluginStateTransform.BuiltIn({
     name: 'structure-selection',
+    from: SO.Molecule.Structure,
+    to: SO.Molecule.Structure,
+    params: () => ({
+        query: PD.Value<Expression>(MolScriptBuilder.struct.generator.all, { isHidden: true }),
+        label: PD.makeOptional(PD.Text('', { isHidden: true }))
+    })
+})({
     display: {
         name: 'Structure Selection',
         description: 'Create a molecular structure from the specified model.'
     },
-    from: [SO.Molecule.Structure],
-    to: [SO.Molecule.Structure],
-    params: () => ({
-        query: PD.Value<Expression>(MolScriptBuilder.struct.generator.all, { isHidden: true }),
-        label: PD.Text('', { isOptional: true, isHidden: true })
-    }),
     apply({ a, params }) {
         // TODO: use cache, add "update"
         const compiled = compile<Sel>(params.query);
@@ -150,16 +154,18 @@ const StructureSelection = PluginStateTransform.Create<SO.Molecule.Structure, SO
 });
 
 export { StructureComplexElement }
-namespace StructureComplexElement { export interface Params { type: 'atomic-sequence' | 'water' | 'atomic-het' | 'spheres' } }
-const StructureComplexElement = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Structure, StructureComplexElement.Params>({
+namespace StructureComplexElement { export type Types = 'atomic-sequence' | 'water' | 'atomic-het' | 'spheres' }
+type StructureComplexElement = typeof StructureComplexElement
+const StructureComplexElement = PluginStateTransform.BuiltIn({
     name: 'structure-complex-element',
+    from: SO.Molecule.Structure,
+    to: SO.Molecule.Structure,
+    params: () => ({ type: PD.Text<StructureComplexElement.Types>('atomic-sequence', { isHidden: true }) }),
+})({
     display: {
         name: 'Complex Element',
         description: 'Create a molecular structure from the specified model.'
     },
-    from: [SO.Molecule.Structure],
-    to: [SO.Molecule.Structure],
-    params: () => ({ type: PD.Text('sequence', { isHidden: true }) }),
     apply({ a, params }) {
         // TODO: update function.
 
@@ -169,7 +175,7 @@ const StructureComplexElement = PluginStateTransform.Create<SO.Molecule.Structur
             case 'water': query = Queries.internal.water(); label = 'Water'; break;
             case 'atomic-het': query = Queries.internal.atomicHet(); label = 'HET Groups/Ligands'; break;
             case 'spheres': query = Queries.internal.spheres(); label = 'Coarse Spheres'; break;
-            default: throw new Error(`${params.type} is a valid complex element.`);
+            default: throw new Error(`${params.type} is a not valid complex element.`);
         }
 
         const result = query(new QueryContext(a.data));

+ 11 - 16
src/mol-plugin/state/transforms/representation.ts

@@ -14,36 +14,31 @@ import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { createTheme } from 'mol-theme/theme';
 
 export { StructureRepresentation3D }
-namespace StructureRepresentation3D {
-    export interface Params {
-        type: { name: string, params: any /** TODO is there "common type" */ },
-        colorTheme: { name: string, params: any /** TODO is there "common type" */ },
-        sizeTheme: { name: string, params: any /** TODO is there "common type" */ },
-    }
-}
-const StructureRepresentation3D = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, StructureRepresentation3D.Params>({
+type StructureRepresentation3D = typeof StructureRepresentation3D
+const StructureRepresentation3D = PluginStateTransform.BuiltIn({
     name: 'structure-representation-3d',
-    display: { name: '3D Representation' },
-    from: [SO.Molecule.Structure],
-    to: [SO.Molecule.Representation3D],
+    from: SO.Molecule.Structure,
+    to: SO.Molecule.Representation3D,
     params: (a, ctx: PluginContext) => ({
-        type: PD.Mapped(
+        type: PD.Mapped<any>(
             ctx.structureRepresentation.registry.default.name,
             ctx.structureRepresentation.registry.types,
             name => PD.Group<any>(ctx.structureRepresentation.registry.get(name).getParams(ctx.structureRepresentation.themeCtx, a.data))),
-        colorTheme: PD.Mapped(
+        colorTheme: PD.Mapped<any>(
             // TODO how to get a default color theme dependent on the repr type?
             ctx.structureRepresentation.themeCtx.colorThemeRegistry.default.name,
             ctx.structureRepresentation.themeCtx.colorThemeRegistry.types,
             name => PD.Group<any>(ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(name).getParams({ structure: a.data }))
         ),
-        sizeTheme: PD.Mapped(
+        sizeTheme: PD.Mapped<any>(
             // TODO how to get a default size theme dependent on the repr type?
             ctx.structureRepresentation.themeCtx.sizeThemeRegistry.default.name,
             ctx.structureRepresentation.themeCtx.sizeThemeRegistry.types,
             name => PD.Group<any>(ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(name).getParams({ structure: a.data }))
-        ),
-    }),
+        )
+    })
+})({
+    display: { name: '3D Representation' },
     canAutoUpdate({ oldParams, newParams }) {
         // TODO: allow for small molecules
         return oldParams.type.name === newParams.type.name;

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

@@ -142,31 +142,22 @@ export namespace Transformer {
         return <A extends StateObject, B extends StateObject, P extends {} = {}>(definition: Definition<A, B, P>) => create(namespace, definition);
     }
 
-    export function factory1(namespace: string) {
+    export function builderFactory(namespace: string) {
         return Builder.build(namespace);
     }
 
     export namespace Builder {
         type ParamDefinition<P> = { [K in keyof P]-?: PD.Base<P[K]> }
 
-        export interface Type<A extends StateObject.Ctor, B extends StateObject.Ctor> {
+        export interface Type<A extends StateObject.Ctor, B extends StateObject.Ctor, P extends { }> {
             name: string,
             from: A | A[],
-            to: B | B[]
-        }
-
-        export interface TypeAndParams<A extends StateObject.Ctor, B extends StateObject.Ctor, P> extends Type<A, B> {
-            params: ParamDefinition<P>
-        }
-
-        export interface TypeAndParamProvider<A extends StateObject.Ctor, B extends StateObject.Ctor, P> extends Type<A, B> {
-            paramProvider(a: A, globalCtx: unknown): ParamDefinition<P>
+            to: B | B[],
+            params?: ParamDefinition<P> | ((a: StateObject.From<A>, globalCtx: any) => ParamDefinition<P>)
         }
 
         export interface Root {
-            <A extends StateObject.Ctor, B extends StateObject.Ctor>(info: Type<A, B>): Define<StateObject.From<A>, StateObject.From<B>, {}>,
-            <A extends StateObject.Ctor, B extends StateObject.Ctor, P>(info: TypeAndParams<A, B, P>): Define<StateObject.From<A>, StateObject.From<B>, Params<P>>,
-            <A extends StateObject.Ctor, B extends StateObject.Ctor, P>(info: TypeAndParamProvider<A, B, P>): Define<StateObject.From<A>, StateObject.From<B>, Params<P>>        
+            <A extends StateObject.Ctor, B extends StateObject.Ctor, P extends { }>(info: Type<A, B, P>): Define<StateObject.From<A>, StateObject.From<B>, Params<P>>
         }
 
         type Optionals<P> = { [K in keyof P]-?: undefined extends P[K] ? K : never }[keyof P]
@@ -177,15 +168,15 @@ export namespace Transformer {
             (def: DefinitionBase<A, B, P>): Transformer<A, B, P>
         }
 
-        function root(namespace: string, info: Type<any, any> & TypeAndParams<any, any, any> & TypeAndParamProvider<any, any, any>): Define<any, any, any> {
+        function root(namespace: string, info: Type<any, any, any>): Define<any, any, any> {
             return def => create(namespace, {
                 name: info.name,
                 from: info.from instanceof Array ? info.from : [info.from],
                 to: info.to instanceof Array ? info.to : [info.to],
-                params: info.paramProvider
-                    ? info.paramProvider as any
-                    : info.params
-                    ? () => info.params
+                params: typeof info.params === 'object'
+                    ? () => info.params as any
+                    : !!info.params
+                    ? info.params as any
                     : void 0,
                 ...def
             });

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

@@ -56,7 +56,7 @@ namespace ColorTheme {
         readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
         readonly getParams: (ctx: ThemeDataContext) => P
     }
-    export const EmptyProvider: Provider<{}> = { label: '', factory:EmptyFactory, getParams: () => ({}) }
+    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}) }
 
     export class Registry {
         private _list: { name: string, provider: Provider<any> }[] = []

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

@@ -68,11 +68,11 @@ export namespace ParamDefinition {
         return setInfo<Boolean>({ type: 'boolean', defaultValue }, info)
     }
 
-    export interface Text extends Base<string> {
+    export interface Text<T extends string = string> extends Base<T> {
         type: 'text'
     }
-    export function Text(defaultValue: string = '', info?: Info): Text {
-        return setInfo<Text>({ type: 'text', defaultValue }, info)
+    export function Text<T extends string = string>(defaultValue: string = '', info?: Info): Text<T> {
+        return setInfo<Text<T>>({ type: 'text', defaultValue: defaultValue as any }, info)
     }
 
     export interface Color extends Base<ColorData> {