Ver código fonte

mol-plugin-state: StructureBuilder

David Sehnal 5 anos atrás
pai
commit
1f831f90e0

+ 20 - 32
src/mol-plugin-state/actions/structure.ts

@@ -227,21 +227,26 @@ const DownloadStructure = StateAction.build({
 
     await state.transaction(async () => {
         if (downloadParams.length > 0 && asTrajectory) {
-            const traj = await createSingleTrajectoryModel(plugin, state, downloadParams);
-            const struct = createStructure(state.build().to(traj), supportProps, src.params.structure.type);
-            await state.updateTree(struct, { revertIfAborted: true }).runInContext(ctx);
+            const data = 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 model = await plugin.builders.structure.createModel(traj, void 0, supportProps);
+            const struct = await plugin.builders.structure.createStructure(model, src.params.structure.type);
             if (createRepr) {
-                await plugin.builders.structureRepresentation.apply(struct.ref, 'auto');
+                await plugin.builders.representation.structurePreset(struct.ref, 'auto');
             }
         } else {
             for (const download of downloadParams) {
                 const data = await plugin.builders.data.download(download, { state: { isGhost: true } });
-                const traj = createModelTree(state.build().to(data), format);
-
-                const struct = createStructure(traj, supportProps, src.params.structure.type);
-                await state.updateTree(struct, { revertIfAborted: true }).runInContext(ctx);
+                const traj = await plugin.builders.structure.parseTrajectory(data, format);
+                const model = await plugin.builders.structure.createModel(traj, void 0, supportProps);
+                const struct = await plugin.builders.structure.createStructure(model, src.params.structure.type);
                 if (createRepr) {
-                    await plugin.builders.structureRepresentation.apply(struct.ref, 'auto');
+                    await plugin.builders.representation.structurePreset(struct.ref, 'auto');
                 }
             }
         }
@@ -257,23 +262,6 @@ function getDownloadParams(src: string, url: (id: string) => string, label: (id:
     return ret;
 }
 
-async function createSingleTrajectoryModel(plugin: PluginContext, state: State, sources: StateTransformer.Params<Download>[]) {
-    const data = await plugin.builders.data.downloadBlob({
-        sources: sources.map((src, i) => ({ id: '' + i, url: src.url, isBinary: src.isBinary })),
-        maxConcurrency: 6
-    }, { state: { isGhost: true } });
-
-    const trajectory = state.build().to(data)
-        .apply(StateTransforms.Data.ParseBlob, {
-            formats: sources.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' }))
-        }, { state: { isGhost: true } })
-        .apply(StateTransforms.Model.TrajectoryFromBlob)
-        .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
-
-    await plugin.runTask(state.updateTree(trajectory, { revertIfAborted: true }));
-    return trajectory.selector;
-}
-
 export function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: StructureFormat = 'cif') {
     let parsed: StateBuilder.To<PluginStateObject.Molecule.Trajectory>
     switch (format) {
@@ -314,28 +302,28 @@ function createStructureAndVisuals(ctx: PluginContext, b: StateBuilder.To<Plugin
 export const Create3DRepresentationPreset = StateAction.build({
     display: { name: '3D Representation Preset', description: 'Create one of preset 3D representations.' },
     from: PluginStateObject.Molecule.Structure,
-    isApplicable(a, _, plugin: PluginContext) { return plugin.builders.structureRepresentation.hasProvider(a.data); },
+    isApplicable(a, _, plugin: PluginContext) { return plugin.builders.representation.hasPreset(a.data); },
     params(a, plugin: PluginContext) {
         return {
-            type: plugin.builders.structureRepresentation.getOptions(a.data)
+            type: plugin.builders.representation.getPresets(a.data)
         };
     }
 })(({ ref, params }, plugin: PluginContext) => {
-    plugin.builders.structureRepresentation.apply(ref, params.type.name, params.type.params);
+    plugin.builders.representation.structurePreset(ref, params.type.name, params.type.params);
 });
 
 export const Remove3DRepresentationPreset = StateAction.build({
     display: { name: 'Remove 3D Representation Preset', description: 'Remove 3D representations.' },
     from: PluginStateObject.Molecule.Structure,
-    isApplicable(_, t, plugin: PluginContext) { return plugin.builders.structureRepresentation.hasManagedRepresentation(t.ref); },
+    isApplicable(_, t, plugin: PluginContext) { return plugin.builders.representation.hasPresetRepresentation(t.ref); },
     params(a, plugin: PluginContext) {
         return {
-            type: plugin.builders.structureRepresentation.getOptions(a.data).select
+            type: plugin.builders.representation.getPresets(a.data).select
         };
     }
 })(({ ref, params }, plugin: PluginContext) => {
     // TODO: this will be completely handled by the managed and is just for testing purposes
-    plugin.builders.structureRepresentation.remove(params.type, ref);
+    plugin.builders.representation.removePreset(params.type, ref);
 });
 
 export const UpdateTrajectory = StateAction.build({

+ 0 - 1
src/mol-plugin-state/builder/parser.ts

@@ -1 +0,0 @@
-// TODO: parsing

+ 17 - 14
src/mol-plugin-state/builder/structure-representation.ts → src/mol-plugin-state/builder/representation.ts

@@ -19,28 +19,28 @@ import { UniqueArray } from '../../mol-data/generic';
 // TODO: support quality
 // TODO: support ignore hydrogens
 
-export type StructureRepresentationProviderRef = keyof PresetStructureReprentations | StructureRepresentationProvider | string
+export type RepresentationProviderRef = keyof PresetStructureReprentations | StructureRepresentationProvider | string
 
-export class StructureRepresentationBuilder {
+export class RepresentationBuilder {
     private providers: StructureRepresentationProvider[] = [];
     private providerMap: Map<string, StructureRepresentationProvider> = new Map();
 
     readonly defaultProvider = PresetStructureReprentations.auto;
 
-    private resolveProvider(ref: StructureRepresentationProviderRef) {
+    private resolveProvider(ref: RepresentationProviderRef) {
         return typeof ref === 'string'
             ? PresetStructureReprentations[ref as keyof PresetStructureReprentations] ?? arrayFind(this.providers, p => p.id === ref)
             : ref;
     }
 
-    hasProvider(s: Structure) {
+    hasPreset(s: Structure) {
         for (const p of this.providers) {
             if (!p.isApplicable || p.isApplicable(s, this.plugin)) return true;
         }
         return false;
     }
 
-    getOptions(s: Structure) {
+    getPresets(s: Structure) {
         const options: [string, string][] = [];
         const map: { [K in string]: PD.Any } = Object.create(null);
         for (const p of this.providers) {
@@ -53,7 +53,7 @@ export class StructureRepresentationBuilder {
         return PD.MappedStatic(options[0][0], map, { options });
     }
 
-    hasManagedRepresentation(ref: StateObjectRef) {
+    hasPresetRepresentation(ref: StateObjectRef) {
         // TODO: make this state selection function?
         const tree = this.plugin.state.dataState.tree;
         const root = StateObjectRef.resolve(this.plugin.state.dataState, ref);
@@ -69,7 +69,7 @@ export class StructureRepresentationBuilder {
         }).found;
     }
 
-    getManagedRepresentations(ref: StateObjectRef) {
+    getPresetRepresentations(ref: StateObjectRef) {
         const tree = this.plugin.state.dataState.tree;
         const root = StateObjectRef.resolve(this.plugin.state.dataState, ref);
         if (!root) return [];
@@ -81,7 +81,7 @@ export class StructureRepresentationBuilder {
         }).found.array;
     }
 
-    register(provider: StructureRepresentationProvider) {
+    registerPreset(provider: StructureRepresentationProvider) {
         if (this.providerMap.has(provider.id)) {
             throw new Error(`Repr. provider with id '${provider.id}' already registered.`);
         }
@@ -90,7 +90,7 @@ export class StructureRepresentationBuilder {
         this.providerMap.set(provider.id, provider);
     }
 
-    remove(providerRef: StructureRepresentationProviderRef, structureRoot?: StateObjectRef) {
+    removePreset(providerRef: RepresentationProviderRef, structureRoot?: StateObjectRef) {
         const id = this.resolveProvider(providerRef)?.id;
         if (!id) return;
         
@@ -114,10 +114,10 @@ export class StructureRepresentationBuilder {
         return this.plugin.runTask(state.updateTree(builder));
     }
     
-    apply<K extends keyof PresetStructureReprentations>(parent: StateObjectRef, preset: K, params?: StructureRepresentationProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationProvider.State<PresetStructureReprentations[K]>> | undefined
-    apply<P = any, S = {}>(parent: StateObjectRef, providers: StructureRepresentationProvider<P, S>, params?: P): Promise<S> | undefined
-    apply(parent: StateObjectRef, providerId: string, params?: any): Promise<any> | undefined
-    apply(parent: StateObjectRef, providerRef: string | StructureRepresentationProvider, params?: any): Promise<any> | undefined {
+    structurePreset<K extends keyof PresetStructureReprentations>(parent: StateObjectRef, preset: K, params?: StructureRepresentationProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationProvider.State<PresetStructureReprentations[K]>> | undefined
+    structurePreset<P = any, S = {}>(parent: StateObjectRef, providers: StructureRepresentationProvider<P, S>, params?: P): Promise<S> | undefined
+    structurePreset(parent: StateObjectRef, providerId: string, params?: any): Promise<any> | undefined
+    structurePreset(parent: StateObjectRef, providerRef: string | StructureRepresentationProvider, params?: any): Promise<any> | undefined {
         const provider = this.resolveProvider(providerRef);
         if (!provider) return;
 
@@ -137,7 +137,10 @@ export class StructureRepresentationBuilder {
         return this.plugin.runTask(task);
     }
 
+    // TODO
+    // createOrUpdate(component: any, ) { }
+
     constructor(public plugin: PluginContext) {
-        objectForEach(PresetStructureReprentations, r => this.register(r));
+        objectForEach(PresetStructureReprentations, r => this.registerPreset(r));
     }
 }

+ 0 - 1
src/mol-plugin-state/builder/structure-model.ts

@@ -1 +0,0 @@
-// TODO: create managed trajectory / model / structure

+ 110 - 0
src/mol-plugin-state/builder/structure.ts

@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { PluginContext } from '../../mol-plugin/context';
+import { StateBuilder, StateObjectRef, StateObjectSelector, StateTransformer } from '../../mol-state';
+import { PluginStateObject as SO } from '../objects';
+import { StateTransforms } from '../transforms';
+import { RootStructureDefinition } from '../helpers/root-structure';
+
+type TrajectoryFormat = 'pdb' | 'cif' | 'gro' | '3dg'
+
+export enum StructureBuilderTags {
+    Trajectory = 'trajectory',
+    Model = 'model',
+    ModelProperties = 'model-properties',
+    Structure = 'structure'
+}
+
+export class StructureBuilder {
+    private get dataState() {
+        return this.plugin.state.dataState;
+    }
+
+    private async parseTrajectoryData(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: TrajectoryFormat) {
+        const state = this.dataState;
+        const root = state.build().to(data);
+        let parsed: StateBuilder.To<SO.Molecule.Trajectory>;
+        const tag = { tags: StructureBuilderTags.Trajectory };
+        switch (format) {
+            case 'cif':
+                parsed = root.apply(StateTransforms.Data.ParseCif, void 0, { state: { isGhost: true } })
+                    .apply(StateTransforms.Model.TrajectoryFromMmCif, void 0, tag)
+                break
+            case 'pdb':
+                parsed = root.apply(StateTransforms.Model.TrajectoryFromPDB, void 0, tag);
+                break
+            case 'gro':
+                parsed = root.apply(StateTransforms.Model.TrajectoryFromGRO, void 0, tag);
+                break
+            case '3dg':
+                parsed = root.apply(StateTransforms.Model.TrajectoryFrom3DG, void 0, tag);
+                break
+            default:
+                throw new Error('unsupported format')
+        }
+
+        await this.plugin.runTask(this.dataState.updateTree(parsed, { revertOnError: true }));
+
+        return parsed.selector;
+    }
+
+    private async parseTrajectoryBlob(data: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>) {
+        const state = this.dataState;
+        const trajectory = state.build().to(data)
+            .apply(StateTransforms.Data.ParseBlob, params, { state: { isGhost: true } })
+            .apply(StateTransforms.Model.TrajectoryFromBlob, void 0, { tags: StructureBuilderTags.Trajectory });        
+        await this.plugin.runTask(this.dataState.updateTree(trajectory, { revertOnError: true }));
+        return trajectory.selector;
+    }
+
+    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) {
+        // TODO: proper format support
+        // needs to integrated to transforms directly because of blobs
+        const cell = StateObjectRef.resolveAndCheck(this.dataState, data as StateObjectRef);
+        if (!cell) throw new Error('Invalid data cell.');
+
+        if (SO.Data.Blob.is(cell.obj)) {
+            return this.parseTrajectoryBlob(data, params);
+        } else {
+            return this.parseTrajectoryData(data, params);
+        }
+    }
+
+    async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>, supportProps?: boolean) {
+        const state = this.dataState;
+        if (supportProps) {
+            const model = state.build().to(trajectory)
+                .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 })
+                .apply(StateTransforms.Model.CustomModelProperties, void 0, { tags: [StructureBuilderTags.Model, StructureBuilderTags.ModelProperties] });
+            await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true }));
+            return model.selector;
+        } else {
+            const model = state.build().to(trajectory)
+                .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model });        
+            await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true }));
+            return model.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 || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure });        
+        await this.plugin.runTask(this.dataState.updateTree(structure, { revertOnError: true }));
+        return structure.selector;
+    }
+
+    // TODO
+    async makeComponent() {
+
+    }
+
+    constructor(public plugin: PluginContext) {
+    }
+}

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

@@ -16,4 +16,6 @@ export const StateTransforms = {
     Model,
     Volume,
     Representation
-}
+}
+
+export type StateTransforms = typeof StateTransforms

+ 5 - 3
src/mol-plugin/context.ts

@@ -43,10 +43,11 @@ import { StructureOverpaintHelper } from './util/structure-overpaint-helper';
 import { PluginToastManager } from './util/toast';
 import { StructureMeasurementManager } from './util/structure-measurement';
 import { ViewportScreenshotHelper } from './util/viewport-screenshot';
-import { StructureRepresentationBuilder } from '../mol-plugin-state/builder/structure-representation';
+import { RepresentationBuilder } from '../mol-plugin-state/builder/representation';
 import { CustomProperty } from '../mol-model-props/common/custom-property';
 import { PluginConfigManager } from './config';
 import { DataBuilder } from '../mol-plugin-state/builder/data';
+import { StructureBuilder } from '../mol-plugin-state/builder/structure';
 
 export class PluginContext {
     private disposed = false;
@@ -126,7 +127,8 @@ export class PluginContext {
 
     readonly builders = {
         data: new DataBuilder(this),
-        structureRepresentation: void 0 as any as StructureRepresentationBuilder
+        structure: new StructureBuilder(this),
+        representation: void 0 as any as RepresentationBuilder
     };
 
     readonly customModelProperties = new CustomProperty.Registry<Model>();
@@ -282,7 +284,7 @@ export class PluginContext {
         this.interactivity = new Interactivity(this);
         this.lociLabels = new LociLabelManager(this);
 
-        (this.builders.structureRepresentation as StructureRepresentationBuilder)= new StructureRepresentationBuilder(this);
+        (this.builders.representation as RepresentationBuilder)= new RepresentationBuilder(this);
 
         this.log.message(`Mol* Plugin ${PLUGIN_VERSION} [${PLUGIN_VERSION_DATE.toLocaleString()}]`);
         if (!isProductionMode) this.log.message(`Development mode enabled`);

+ 2 - 1
src/mol-state/state/builder.ts

@@ -6,7 +6,7 @@
 
 import { StateTree } from '../tree/immutable';
 import { TransientTree } from '../tree/transient';
-import { StateObject, StateObjectCell, StateObjectSelector } from '../object';
+import { StateObject, StateObjectCell, StateObjectSelector, StateObjectRef } from '../object';
 import { StateTransform } from '../transform';
 import { StateTransformer } from '../transformer';
 import { State } from '../state';
@@ -80,6 +80,7 @@ namespace StateBuilder {
         get currentTree() { return this.state.tree; }
 
         to<A extends StateObject, T extends StateTransformer>(ref: StateTransform.Ref): To<A, T>
+        to<A extends StateObject, T extends StateTransformer>(ref: StateObjectRef<A>): To<A, T>
         to<C extends StateObjectCell>(cell: C): To<StateObjectCell.Obj<C>, StateObjectCell.Transformer<C>>
         to<S extends StateObjectSelector>(selector: S): To<StateObjectSelector.Obj<S>, StateObjectSelector.Transformer<S>>
         to(refOrCellOrSelector: StateTransform.Ref | StateObjectCell | StateObjectSelector) {