Browse Source

plugin/state: helpers & fixes

David Sehnal 5 years ago
parent
commit
410123d933

+ 14 - 16
src/mol-plugin-state/actions/structure.ts

@@ -227,24 +227,28 @@ const DownloadStructure = StateAction.build({
 
     await state.transaction(async () => {
         if (downloadParams.length > 0 && asTrajectory) {
-            const data = await plugin.builders.data.downloadBlob({
+            const blob = await plugin.builders.data.downloadBlob({
                 sources: downloadParams.map((src, i) => ({ id: '' + i, url: src.url, isBinary: src.isBinary })),
                 maxConcurrency: 6
             }, { state: { isGhost: true } });        
-            const traj = await plugin.builders.structure.parseTrajectory(data, {
-                formats: downloadParams.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' }))
+            const { structure } = await plugin.builders.structure.parseStructure({
+                blob,
+                blobParams:  { formats: downloadParams.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' })) },
+                modelProperties: supportProps,
+                structureProperties: supportProps
             });
-            const { model } = await plugin.builders.structure.createModel(traj, { properties: supportProps });
-            const { structure } = await plugin.builders.structure.createStructure(model, { structure: src.params.structure.type, properties: supportProps });
             if (createRepr) {
                 await plugin.builders.representation.structurePreset(structure, 'auto');
             }
         } else {
             for (const download of downloadParams) {
                 const data = await plugin.builders.data.download(download, { state: { isGhost: true } });
-                const traj = await plugin.builders.structure.parseTrajectory(data, format);
-                const { model } = await plugin.builders.structure.createModel(traj, { properties: supportProps });
-                const { structure } = await plugin.builders.structure.createStructure(model, { structure: src.params.structure.type, properties: supportProps });
+                const { structure } = await plugin.builders.structure.parseStructure({
+                    data,
+                    dataFormat: format,
+                    modelProperties: supportProps,
+                    structureProperties: supportProps
+                });
                 if (createRepr) {
                     await plugin.builders.representation.structurePreset(structure, 'auto');
                 }
@@ -366,10 +370,7 @@ export const EnableModelCustomProps = StateAction.build({
     isApplicable(a, t, ctx: PluginContext) {
         return t.transformer !== CustomModelProperties;
     }
-})(({ ref, params, state }, ctx: PluginContext) => {
-    const root = state.build().to(ref).insert(CustomModelProperties, params);
-    return state.updateTree(root);
-});
+})(({ ref, params }, ctx: PluginContext) => ctx.builders.structure.insertModelProperties(ref, params));
 
 export const EnableStructureCustomProps = StateAction.build({
     display: { name: 'Custom Structure Properties', description: 'Enable parameters for custom properties of the structure.' },
@@ -380,10 +381,7 @@ export const EnableStructureCustomProps = StateAction.build({
     isApplicable(a, t, ctx: PluginContext) {
         return t.transformer !== CustomStructureProperties;
     }
-})(({ ref, params, state }, ctx: PluginContext) => {
-    const root = state.build().to(ref).insert(CustomStructureProperties, params);
-    return state.updateTree(root);
-});
+})(({ ref, params }, ctx: PluginContext) => ctx.builders.structure.insertStructureProperties(ref, params));
 
 export const TransformStructureConformation = StateAction.build({
     display: { name: 'Transform Conformation' },

+ 67 - 20
src/mol-plugin-state/builder/structure.ts

@@ -64,6 +64,39 @@ export class StructureBuilder {
         return trajectory.selector;
     }
 
+    async parseStructure(params: {
+        data?: StateObjectRef<SO.Data.Binary | SO.Data.String>,
+        dataFormat?: TrajectoryFormat,
+        blob?: StateObjectRef<SO.Data.Blob>
+        blobParams?: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>,
+        model?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>,
+        modelProperties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>,
+        structure?: RootStructureDefinition.Params,
+        structureProperties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']>
+    }) {
+        const trajectory = params.data 
+            ? await this.parseTrajectory(params.data, params.dataFormat! || 'cif')
+            : await this.parseTrajectoryBlob(params.blob!, params.blobParams!);
+        
+        const model = await this.createModel(trajectory, params.model);
+        const modelProperties = !!params.modelProperties 
+            ? await this.insertModelProperties(model, typeof params?.modelProperties !== 'boolean' ? params?.modelProperties : void 0) : void 0;
+        
+        const structure = await this.createStructure(modelProperties || model, params.structure);
+        const structureProperties = !!params.structureProperties 
+            ? await this.insertStructureProperties(structure, typeof params?.structureProperties !== 'boolean' ? params?.structureProperties : void 0) : void 0;
+    
+        return {
+            trajectory,
+            model: modelProperties || model,
+            modelBase: model,
+            modelProperties,
+            structure: structureProperties || structure,
+            structureBase: structure,
+            structureProperties
+        };
+    }
+
     async parseTrajectory(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: TrajectoryFormat): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
     async parseTrajectory(blob: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
     async parseTrajectory(data: StateObjectRef, params: any) {
@@ -79,43 +112,57 @@ export class StructureBuilder {
         }
     }
 
-    async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: {
-        model?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>,
-        properties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>
-    }) {
+    async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>) {
         const state = this.dataState;
 
         const model = state.build().to(trajectory)
-            .apply(StateTransforms.Model.ModelFromTrajectory, params?.model || void 0, { tags: StructureBuilderTags.Model });
+            .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model });
 
-        const props = !!params?.properties 
-            ? model.apply(StateTransforms.Model.CustomModelProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.ModelProperties, isDecorator: true })
-            : void 0;
+        // const props = !!params?.properties 
+        //     ? model.apply(StateTransforms.Model.CustomModelProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.ModelProperties, isDecorator: true })
+        //     : void 0;
 
         await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true }));
 
-        const modelSelector = model.selector, propertiesSelector = props?.selector;
+        return model.selector;
 
-        return { model: propertiesSelector || modelSelector, index: modelSelector, properties: propertiesSelector };
+        // const modelSelector = model.selector, propertiesSelector = props?.selector;
+
+        // return { model: propertiesSelector || modelSelector, index: modelSelector, properties: propertiesSelector };
     }
 
-    async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: {
-        structure?: RootStructureDefinition.Params,
-        properties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']>
-    }) {
+    async insertModelProperties(model: StateObjectRef<SO.Molecule.Model>, params?: StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>) {
+        const state = this.dataState;
+        const props = state.build().to(model)
+            .apply(StateTransforms.Model.CustomModelProperties, params, { tags: StructureBuilderTags.ModelProperties, isDecorator: true });
+        await this.plugin.runTask(this.dataState.updateTree(props, { revertOnError: true }));
+        return props.selector;
+    }
+
+    async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: RootStructureDefinition.Params) {
         const state = this.dataState;
         const structure = state.build().to(model)
-            .apply(StateTransforms.Model.StructureFromModel, { type: params?.structure || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure });        
+            .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure });        
 
-        const props = !!params?.properties 
-            ? structure.apply(StateTransforms.Model.CustomStructureProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.StructureProperties, isDecorator: true })
-            : void 0;
+        // const props = !!params?.properties 
+        //     ? structure.apply(StateTransforms.Model.CustomStructureProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.StructureProperties, isDecorator: true })
+        //     : void 0;
 
         await this.plugin.runTask(this.dataState.updateTree(structure, { revertOnError: true }));
 
-        const structureSelector = structure.selector, propertiesSelector = props?.selector;
+        return structure.selector;
 
-        return { structure: propertiesSelector || structureSelector, definition: structureSelector, properties: propertiesSelector };
+        // const structureSelector = structure.selector, propertiesSelector = props?.selector;
+
+        // return { structure: propertiesSelector || structureSelector, definition: structureSelector, properties: propertiesSelector };
+    }
+
+    async insertStructureProperties(structure: StateObjectRef<SO.Molecule.Structure>, params?: StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']>) {
+        const state = this.dataState;
+        const props = state.build().to(structure)
+            .apply(StateTransforms.Model.CustomStructureProperties, params, { tags: StructureBuilderTags.StructureProperties, isDecorator: true });
+        await this.plugin.runTask(this.dataState.updateTree(props, { revertOnError: true }));
+        return props.selector;
     }
 
     /** returns undefined if the component is empty/null */

+ 1 - 1
src/mol-plugin-ui/plugin.tsx

@@ -247,7 +247,7 @@ export class CurrentObject extends PluginUIComponent {
         let decorators: JSX.Element[] | undefined = decoratorChain.length > 1 ? [] : void 0;
         for (let i = decoratorChain.length - 2; i >= 0; i--) {
             const d = decoratorChain[i];
-            decorators!.push(<ExpandGroup header={d.transform.transformer.definition.display.name}>
+            decorators!.push(<ExpandGroup key={`${d.transform.transformer.id}-${i}`} header={d.transform.transformer.definition.display.name}>
                 <UpdateTransformControl state={current.state} transform={d.transform} customHeader='none' />
             </ExpandGroup>);
         }

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

@@ -146,8 +146,8 @@ export class StateObjectSelector<S extends StateObject = StateObject, T extends
 
     /** Create a new build and apply update or use the provided one. */
     update(params: StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder
-    update(params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder
-    update(params: ((old: StateTransformer.Params<T>) => StateTransformer.Params<T>) | StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder {
+    update(params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder
+    update(params: ((old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void) | StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder {
         if (!this.state) throw new Error(`To use update() from StateObjectSelector, 'state' must be defined.`);
         if (!builder) builder = this.state.build();
         (builder || this.state.build()).to(this).update(params);

+ 3 - 9
src/mol-state/state/builder.ts

@@ -114,13 +114,7 @@ namespace StateBuilder {
          * Apply the transformed to the parent node
          * If no params are specified (params <- undefined), default params are lazily resolved.
          */
-        apply<T extends StateTransformer<A, any, any>>(tr: T, params?: StateTransformer.Params<T>, options?: Partial<StateTransform.Options>): To<StateTransformer.To<T>, T> {
-            if (options?.isDecorator) {
-                const children = this.state.tree.children.get(this.ref);
-                if (children.size > 0) throw new Error('Decorators can only be applied to childless nodes.');
-            }
-
-            const t = tr.apply(this.ref, params, options);
+        apply<T extends StateTransformer<A, any, any>>(tr: T, params?: StateTransformer.Params<T>, options?: Partial<StateTransform.Options>): To<StateTransformer.To<T>, T> {const t = tr.apply(this.ref, params, options);
             this.state.tree.add(t);
             this.editInfo.count++;
             this.editInfo.lastUpdate = t.ref;
@@ -231,8 +225,8 @@ namespace StateBuilder {
             }
         }
 
-        update<T extends StateTransformer<any, A, any>>(transformer: T, params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>): Root
-        update(params: StateTransformer.Params<T> | ((old: StateTransformer.Params<T>) => StateTransformer.Params<T>)): Root
+        update<T extends StateTransformer<any, A, any>>(transformer: T, params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void): Root
+        update(params: StateTransformer.Params<T> | ((old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void)): Root
         update<T extends StateTransformer<any, A, any>>(paramsOrTransformer: T | any, provider?: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>) {
             let params: any;
             if (provider) {