Ver Fonte

add LoadTrajectory action

Alexander Rose há 3 anos atrás
pai
commit
664cacc7ac

+ 1 - 0
CHANGELOG.md

@@ -7,6 +7,7 @@ Note that since we don't clearly distinguish between a public and private interf
 ## [Unreleased]
 
 - Fix handling of mmcif with empty ``label_*`` fields
+- Add LoadTrajectory action
 
 ## [v3.3.1] - 2022-02-27
 

+ 121 - 3
src/mol-plugin-state/actions/structure.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -10,12 +10,12 @@ import { StateAction, StateSelection, StateTransformer } from '../../mol-state';
 import { Task } from '../../mol-task';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../builder/structure/representation-preset';
-import { BuiltInTrajectoryFormat, BuiltInTrajectoryFormats } from '../formats/trajectory';
+import { BuiltInTrajectoryFormat, BuiltInTrajectoryFormats, TrajectoryFormatCategory } from '../formats/trajectory';
 import { RootStructureDefinition } from '../helpers/root-structure';
 import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { Download } from '../transforms/data';
-import { CustomModelProperties, CustomStructureProperties, TrajectoryFromModelAndCoordinates } from '../transforms/model';
+import { CustomModelProperties, CustomStructureProperties, ModelFromTrajectory, TrajectoryFromModelAndCoordinates } from '../transforms/model';
 import { Asset } from '../../mol-util/assets';
 import { PluginConfig } from '../../mol-plugin/config';
 import { getFileInfo } from '../../mol-util/file-info';
@@ -311,4 +311,122 @@ export const AddTrajectory = StateAction.build({
         const structure = await ctx.builders.structure.createStructure(model.selector);
         await ctx.builders.structure.representation.applyPreset(structure, 'auto');
     }).runInContext(taskCtx);
+}));
+
+export const LoadTrajectory = StateAction.build({
+    display: { name: 'Load Trajectory', description: 'Load trajectory of model/topology and coordinates from URL or file.' },
+    from: PluginStateObject.Root,
+    params(a, ctx: PluginContext) {
+        const { options } = ctx.dataFormats;
+        const modelOptions = options.filter(o => o[2] === TrajectoryFormatCategory || o[0] === 'psf');
+        const coordinatesOptions = options.filter(o => o[0] === 'dcd' || o[0] === 'xtc');
+
+        const modelExtensions: string[] = [];
+        for (const { name, provider } of ctx.dataFormats.list) {
+            if (provider.category === TrajectoryFormatCategory || name === 'psf') {
+                if (provider.binaryExtensions) modelExtensions.push(...provider.binaryExtensions);
+                if (provider.stringExtensions) modelExtensions.push(...provider.stringExtensions);
+            }
+        }
+
+        return {
+            source: PD.MappedStatic('file', {
+                url: PD.Group({
+                    model: PD.Group({
+                        url: PD.Url(''),
+                        format: PD.Select(modelOptions[0][0], modelOptions),
+                        isBinary: PD.Boolean(false),
+                    }, { isExpanded: true }),
+                    coordinates: PD.Group({
+                        url: PD.Url(''),
+                        format: PD.Select(coordinatesOptions[0][0], coordinatesOptions),
+                    }, { isExpanded: true })
+                }, { isFlat: true }),
+                file: PD.Group({
+                    model: PD.File({ accept: modelExtensions.map(e => `.${e}`).join(','), label: 'Model' }),
+                    coordinates: PD.File({ accept: '.dcd,.xtc', label: 'Coordinates' }),
+                }, { isFlat: true }),
+            }, { options: [['url', 'URL'], ['file', 'File']] })
+        };
+    }
+})(({ params, state }, ctx: PluginContext) => Task.create('Load Trajectory', taskCtx => {
+    return state.transaction(async () => {
+        const s = params.source;
+
+        if (s.name === 'file' && (s.params.model === null || s.params.coordinates === null)) {
+            ctx.log.error('No file(s) selected');
+            return;
+        }
+
+        if (s.name === 'url' && (!s.params.model || !s.params.coordinates)) {
+            ctx.log.error('No URL(s) given');
+            return;
+        }
+
+        const processUrl = async (url: string | Asset.Url, format: string, isBinary: boolean) => {
+            const data = await ctx.builders.data.download({ url, isBinary });
+            const provider = ctx.dataFormats.get(format);
+
+            if (!provider) {
+                ctx.log.warn(`LoadTrajectory: could not find data provider for '${format}'`);
+                return;
+            }
+
+            return provider.parse(ctx, data);
+        };
+
+        const processFile = async (file: Asset.File | null) => {
+            if (!file) throw new Error('No file selected');
+
+            const info = getFileInfo(file.file!);
+            const isBinary = ctx.dataFormats.binaryExtensions.has(info.ext);
+            const { data } = await ctx.builders.data.readFile({ file, isBinary });
+            const provider = ctx.dataFormats.auto(info, data.cell?.obj!);
+
+            if (!provider) {
+                ctx.log.warn(`LoadTrajectory: could not find data provider for '${info.name}.${info.ext}'`);
+                return;
+            }
+
+            return provider.parse(ctx, data);
+        };
+
+        try {
+            const modelParsed = s.name === 'url'
+                ? await processUrl(s.params.model.url, s.params.model.format, s.params.model.isBinary)
+                : await processFile(s.params.model);
+
+            let model;
+            if ('trajectory' in modelParsed) {
+                model = await state.build().to(modelParsed.trajectory)
+                    .apply(ModelFromTrajectory, { modelIndex: 0 })
+                    .commit();
+            } else {
+                model = modelParsed.topology;
+            }
+
+            //
+
+            const coordinates = s.name === 'url'
+                ? await processUrl(s.params.coordinates.url, s.params.coordinates.format, true)
+                : await processFile(s.params.coordinates);
+
+            //
+
+            const dependsOn = [model.ref, coordinates.ref];
+            const traj = state.build().toRoot()
+                .apply(TrajectoryFromModelAndCoordinates, {
+                    modelRef: model.ref,
+                    coordinatesRef: coordinates.ref
+                }, { dependsOn })
+                .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
+
+            await state.updateTree(traj).runInContext(taskCtx);
+            const structure = await ctx.builders.structure.createStructure(traj.selector);
+            await ctx.builders.structure.representation.applyPreset(structure, 'auto');
+        } catch (e) {
+            console.error(e);
+            ctx.log.error(`Error loading trajectory`);
+        }
+    }).runInContext(taskCtx);
 }));

+ 5 - 5
src/mol-plugin-state/formats/registry.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -18,10 +18,10 @@ export class DataFormatRegistry {
     private _map = new Map<string, DataFormatProvider>();
     private _extensions: Set<string> | undefined = undefined;
     private _binaryExtensions: Set<string> | undefined = undefined;
-    private _options: [string, string, string][] | undefined = undefined;
+    private _options: [name: string, label: string, category: string][] | undefined = undefined;
 
-    get types(): [string, string][] {
-        return this._list.map(e => [e.name, e.provider.label] as [string, string]);
+    get types(): [name: string, label: string][] {
+        return this._list.map(e => [e.name, e.provider.label] as [name: string, label: string]);
     }
 
     get extensions() {
@@ -45,7 +45,7 @@ export class DataFormatRegistry {
 
     get options() {
         if (this._options) return this._options;
-        const options: [string, string, string][] = [];
+        const options: [name: string, label: string, category: string][] = [];
         this._list.forEach(({ name, provider }) => options.push([name, provider.label, provider.category || '']));
         this._options = options;
         return options;

+ 20 - 4
src/mol-plugin-state/formats/structure.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -10,7 +10,10 @@ import { DataFormatProvider } from './provider';
 
 export const StructureFormatCategory = 'Structure';
 
-export const PsfProvider = DataFormatProvider({
+//
+
+export { PsfProvider };
+const PsfProvider = DataFormatProvider({
     label: 'PSF',
     description: 'PSF',
     category: StructureFormatCategory,
@@ -26,8 +29,14 @@ export const PsfProvider = DataFormatProvider({
         return { format: format.selector, topology: topology.selector };
     }
 });
+type PsfProvider = typeof PsfProvider;
+
+export type TopologyProvider = PsfProvider;
+
+//
 
-export const DcdProvider = DataFormatProvider({
+export { DcdProvider };
+const DcdProvider = DataFormatProvider({
     label: 'DCD',
     description: 'DCD',
     category: StructureFormatCategory,
@@ -40,8 +49,10 @@ export const DcdProvider = DataFormatProvider({
         return coordinates.commit();
     }
 });
+type DcdProvider = typeof DcdProvider;
 
-export const XtcProvider = DataFormatProvider({
+export { XtcProvider };
+const XtcProvider = DataFormatProvider({
     label: 'XTC',
     description: 'XTC',
     category: StructureFormatCategory,
@@ -54,6 +65,11 @@ export const XtcProvider = DataFormatProvider({
         return coordinates.commit();
     }
 });
+type XtcProvider = typeof XtcProvider;
+
+export type CoordinatesProvider = DcdProvider | XtcProvider;
+
+//
 
 export const BuiltInStructureFormats = [
     ['psf', PsfProvider] as const,

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

@@ -64,10 +64,10 @@ namespace PluginSpec {
 export const DefaultPluginSpec = (): PluginSpec => ({
     actions: [
         PluginSpec.Action(StateActions.Structure.DownloadStructure),
-        PluginSpec.Action(StateActions.Structure.AddTrajectory),
         PluginSpec.Action(StateActions.Volume.DownloadDensity),
         PluginSpec.Action(StateActions.DataFormat.DownloadFile),
         PluginSpec.Action(StateActions.DataFormat.OpenFiles),
+        PluginSpec.Action(StateActions.Structure.LoadTrajectory),
         PluginSpec.Action(StateActions.Structure.EnableModelCustomProps),
         PluginSpec.Action(StateActions.Structure.EnableStructureCustomProps),