Browse Source

mol-plugin-state: added TrajectoryHierarchy presets
refactored representation presets

David Sehnal 5 years ago
parent
commit
91bc6f07c5

+ 28 - 52
src/mol-plugin-state/actions/structure.ts

@@ -18,6 +18,7 @@ import { StateTransforms } from '../transforms';
 import { Download, ParsePsf } from '../transforms/data';
 import { CoordinatesFromDcd, CustomModelProperties, CustomStructureProperties, TopologyFromPsf, TrajectoryFromModelAndCoordinates } from '../transforms/model';
 import { DataFormatProvider, guessCifVariant } from './data-format';
+import { applyTrajectoryHierarchyPreset } from '../builder/structure/hierarchy-preset';
 
 // TODO make unitcell creation part of preset
 
@@ -33,12 +34,9 @@ export const MmcifProvider: DataFormatProvider<PluginStateObject.Data.String | P
         return false
     },
     getDefaultBuilder: (ctx: PluginContext, data, options) => {
-        return Task.create('mmCIF default builder', async taskCtx => {
-            const { structure, model } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'mmcif' });
-            if (options.visuals) {
-                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
-                await ctx.builders.structure.tryCreateUnitcell(model, undefined, { isHidden: true })
-            }
+        return Task.create('mmCIF default builder', async () => {
+            const trajectory = await ctx.builders.structure.parseTrajectory(data, 'mmcif');
+            await applyTrajectoryHierarchyPreset(ctx, trajectory, 'first-model', { showUnitcell: options.visuals, noRepresentation: !options.visuals });
         })
     }
 }
@@ -53,11 +51,8 @@ export const PdbProvider: DataFormatProvider<any> = {
     },
     getDefaultBuilder: (ctx: PluginContext, data, options) => {
         return Task.create('PDB default builder', async () => {
-            const { structure, model } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'pdb' });
-            if (options.visuals) {
-                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
-                await ctx.builders.structure.tryCreateUnitcell(model, undefined, { isHidden: true })
-            }
+            const trajectory = await ctx.builders.structure.parseTrajectory(data, 'pdb');
+            await applyTrajectoryHierarchyPreset(ctx, trajectory, 'first-model', { showUnitcell: options.visuals, noRepresentation: !options.visuals });
         })
     }
 }
@@ -72,11 +67,8 @@ export const GroProvider: DataFormatProvider<any> = {
     },
     getDefaultBuilder: (ctx: PluginContext, data, options) => {
         return Task.create('GRO default builder', async () => {
-            const { structure, model } = await ctx.builders.structure.parseStructure({ data, dataFormat: 'gro' });
-            if (options.visuals) {
-                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
-                await ctx.builders.structure.tryCreateUnitcell(model, undefined, { isHidden: true })
-            }
+            const trajectory = await ctx.builders.structure.parseTrajectory(data, 'gro');
+            await applyTrajectoryHierarchyPreset(ctx, trajectory, 'first-model', { showUnitcell: options.visuals, noRepresentation: !options.visuals });
         })
     }
 }
@@ -91,11 +83,8 @@ export const Provider3dg: DataFormatProvider<any> = {
     },
     getDefaultBuilder: (ctx: PluginContext, data, options) => {
         return Task.create('3DG default builder', async () => {
-            const { structure, model } = await ctx.builders.structure.parseStructure({ data, dataFormat: '3dg' });
-            if (options.visuals) {
-                await ctx.builders.structure.representation.applyPreset(structure, 'auto');
-                await ctx.builders.structure.tryCreateUnitcell(model, undefined, { isHidden: true })
-            }
+            const trajectory = await ctx.builders.structure.parseTrajectory(data, '3dg');
+            await applyTrajectoryHierarchyPreset(ctx, trajectory, 'first-model', { showUnitcell: options.visuals, noRepresentation: !options.visuals });
         })
     }
 }
@@ -140,7 +129,7 @@ const DownloadModelRepresentationOptions = PD.Group({
 }, { isExpanded: false });
 
 const DownloadStructurePdbIdSourceOptions = PD.Group({
-    supportProps: PD.Optional(PD.Boolean(false)),
+    // supportProps: PD.Optional(PD.Boolean(false)),
     asTrajectory: PD.Optional(PD.Boolean(false, { description: 'Load all entries into a single trajectory.' }))
 });
 
@@ -182,7 +171,7 @@ const DownloadStructure = StateAction.build({
                 isBinary: PD.Boolean(false),
                 structure: DownloadModelRepresentationOptions,
                 options: PD.Group({
-                    supportProps: PD.Optional(PD.Boolean(false)),
+                    // supportProps: PD.Optional(PD.Boolean(false)),
                     createRepresentation: PD.Optional(PD.Boolean(true))
                 })
             }, { isFlat: true, label: 'URL' })
@@ -193,22 +182,23 @@ const DownloadStructure = StateAction.build({
 
     const src = params.source;
     let downloadParams: StateTransformer.Params<Download>[];
-    let supportProps = false, asTrajectory = false, format: BuiltInTrajectoryFormat = 'mmcif';
+    // let supportProps = false
+    let asTrajectory = false, format: BuiltInTrajectoryFormat = 'mmcif';
 
     switch (src.name) {
         case 'url':
             downloadParams = [{ url: src.params.url, isBinary: src.params.isBinary }];
-            supportProps = !!src.params.options.supportProps;
+            // supportProps = !!src.params.options.supportProps;
             format = src.params.format
             break;
         case 'pdbe-updated':
             downloadParams = getDownloadParams(src.params.id, id => `https://www.ebi.ac.uk/pdbe/static/entry/${id.toLowerCase()}_updated.cif`, id => `PDBe: ${id}`, false);
-            supportProps = !!src.params.options.supportProps;
+            // supportProps = !!src.params.options.supportProps;
             asTrajectory = !!src.params.options.asTrajectory;
             break;
         case 'rcsb':
             downloadParams = getDownloadParams(src.params.id, id => `https://files.rcsb.org/download/${id.toUpperCase()}.cif`, id => `RCSB: ${id}`, false);
-            supportProps = !!src.params.options.supportProps;
+            // supportProps = !!src.params.options.supportProps;
             asTrajectory = !!src.params.options.asTrajectory;
             break;
         case 'pdb-dev':
@@ -220,17 +210,17 @@ const DownloadStructure = StateAction.build({
                 id => id.toUpperCase().startsWith('PDBDEV_') ? id : `PDBDEV_${id.padStart(8, '0')}`,
                 false
             );
-            supportProps = !!src.params.options.supportProps;
+            // supportProps = !!src.params.options.supportProps;
             asTrajectory = !!src.params.options.asTrajectory;
             break;
         case 'bcif-static':
             downloadParams = getDownloadParams(src.params.id, id => `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${id.toLowerCase()}`, id => `BinaryCIF: ${id}`, true);
-            supportProps = !!src.params.options.supportProps;
+            // supportProps = !!src.params.options.supportProps;
             asTrajectory = !!src.params.options.asTrajectory;
             break;
         case 'swissmodel':
             downloadParams = getDownloadParams(src.params.id, id => `https://swissmodel.expasy.org/repository/uniprot/${id.toUpperCase()}.pdb`, id => `SWISS-MODEL: ${id}`, false);
-            supportProps = !!src.params.options.supportProps;
+            // supportProps = !!src.params.options.supportProps;
             asTrajectory = !!src.params.options.asTrajectory;
             format = 'pdb'
             break;
@@ -245,29 +235,15 @@ const DownloadStructure = StateAction.build({
                 sources: downloadParams.map((src, i) => ({ id: '' + i, url: src.url, isBinary: src.isBinary })),
                 maxConcurrency: 6
             }, { state: { isGhost: true } });
-            const { structure, model } = await plugin.builders.structure.parseStructure({
-                blob,
-                blobParams:  { formats: downloadParams.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' })) },
-                modelProperties: supportProps,
-                structureProperties: supportProps
-            });
-            if (createRepr) {
-                await plugin.builders.structure.representation.applyPreset(structure, 'auto');
-                await plugin.builders.structure.tryCreateUnitcell(model, undefined, { isHidden: true })
-            }
+            const trajectory = await plugin.builders.structure.parseTrajectory(blob, { formats: downloadParams.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' })) });
+
+            await applyTrajectoryHierarchyPreset(plugin, trajectory, 'first-model', { showUnitcell: createRepr, noRepresentation: !createRepr });
         } else {
             for (const download of downloadParams) {
                 const data = await plugin.builders.data.download(download, { state: { isGhost: true } });
-                const { structure, model } = await plugin.builders.structure.parseStructure({
-                    data,
-                    dataFormat: format,
-                    modelProperties: supportProps,
-                    structureProperties: supportProps
-                });
-                if (createRepr) {
-                    await plugin.builders.structure.representation.applyPreset(structure, 'auto');
-                    await plugin.builders.structure.tryCreateUnitcell(model, undefined, { isHidden: true })
-                }
+                const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
+
+                await applyTrajectoryHierarchyPreset(plugin, trajectory, 'first-model', { showUnitcell: createRepr, noRepresentation: !createRepr });
             }
         }
     }).runInContext(ctx);
@@ -308,10 +284,10 @@ export function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary
 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.structure.representation.hasPreset(a.data); },
+    isApplicable(a, _, plugin: PluginContext) { return plugin.builders.structure.representation.hasPreset(a); },
     params(a, plugin: PluginContext) {
         return {
-            type: plugin.builders.structure.representation.getPresetsWithOptions(a.data)
+            type: plugin.builders.structure.representation.getPresetsWithOptions(a)
         };
     }
 })(({ ref, params }, plugin: PluginContext) => {

+ 17 - 0
src/mol-plugin-state/builder/preset-provider.ts

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { StateObject, StateObjectRef } from '../../mol-state';
+import { PluginContext } from '../../mol-plugin/context';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+
+export interface PresetProvider<O extends StateObject = StateObject, P = any, S = {}> {
+    id: string,
+    display: { name: string, group: string, description?: string },
+    isApplicable?(a: O, plugin: PluginContext): boolean,
+    params?(a: O | undefined, plugin: PluginContext): PD.For<P>,
+    apply(a: StateObjectRef<O>, params: P, plugin: PluginContext): Promise<S> | S
+}

+ 32 - 32
src/mol-plugin-state/builder/structure.ts

@@ -54,38 +54,38 @@ export class StructureBuilder {
 
     readonly representation = new StructureRepresentationBuilder(this.plugin);
 
-    async parseStructure(params: {
-        data?: StateObjectRef<SO.Data.Binary | SO.Data.String>,
-        dataFormat?: BuiltInTrajectoryFormat | TrajectoryFormatProvider,
-        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,
-            modelRoot: model,
-            modelProperties,
-            structure: structureProperties || structure,
-            structureRoot: structure,
-            structureProperties
-        };
-    }
+    // async parseStructure(params: {
+    //     data?: StateObjectRef<SO.Data.Binary | SO.Data.String>,
+    //     dataFormat?: BuiltInTrajectoryFormat | TrajectoryFormatProvider,
+    //     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,
+    //         modelRoot: model,
+    //         modelProperties,
+    //         structure: structureProperties || structure,
+    //         structureRoot: structure,
+    //         structureProperties
+    //     };
+    // }
 
     async parseTrajectory(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: BuiltInTrajectoryFormat | TrajectoryFormatProvider): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
     async parseTrajectory(blob: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>): Promise<StateObjectSelector<SO.Molecule.Trajectory>>

+ 122 - 0
src/mol-plugin-state/builder/structure/hierarchy-preset.ts

@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { PresetProvider } from '../preset-provider';
+import { PluginStateObject } from '../../objects';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { StateObjectRef, StateTransformer } from '../../../mol-state';
+import { StateTransforms } from '../../transforms';
+import { RootStructureDefinition } from '../../helpers/root-structure';
+import { PresetStructureReprentations } from './representation-preset';
+import { PluginContext } from '../../../mol-plugin/context';
+import { isProductionMode } from '../../../mol-util/debug';
+import { Task } from '../../../mol-task';
+
+export interface TrajectoryHierarchyPresetProvider<P = any, S = {}> extends PresetProvider<PluginStateObject.Molecule.Trajectory, P, S> { }
+export namespace TrajectoryHierarchyPresetProvider {
+    export type Params<P extends TrajectoryHierarchyPresetProvider> = P extends TrajectoryHierarchyPresetProvider<infer T> ? T : never;
+    export type State<P extends TrajectoryHierarchyPresetProvider> = P extends TrajectoryHierarchyPresetProvider<infer _, infer S> ? S : never;
+}
+export function TrajectoryHierarchyPresetProvider<P, S>(preset: TrajectoryHierarchyPresetProvider<P, S>) { return preset; }
+
+const CommonParams = (a: PluginStateObject.Molecule.Trajectory | undefined, plugin: PluginContext) => ({
+    modelProperties: PD.Optional(PD.Group(StateTransformer.getParamDefinition(StateTransforms.Model.CustomModelProperties, void 0, plugin))),
+    structureProperties: PD.Optional(PD.Group(StateTransformer.getParamDefinition(StateTransforms.Model.CustomStructureProperties, void 0, plugin))),
+    representationPreset: PD.Optional(PD.Text<keyof PresetStructureReprentations>('auto' as const)),
+})
+
+const FirstModelParams = (a: PluginStateObject.Molecule.Trajectory | undefined, plugin: PluginContext) =>  ({
+    model: PD.Optional(PD.Group(StateTransformer.getParamDefinition(StateTransforms.Model.ModelFromTrajectory, a, plugin))),
+    showUnitcell: PD.Optional(PD.Boolean(true)),
+    structure: PD.Optional(RootStructureDefinition.getParams(void 0, 'assembly').type),
+    noRepresentation: PD.Optional(PD.Boolean(false)),
+    ...CommonParams(a, plugin)
+});
+
+const firstModel = TrajectoryHierarchyPresetProvider({
+    id: 'preset-trajectory-first-model',
+    display: { name: 'First Model', group: 'Preset' },
+    params: FirstModelParams,
+    async apply(trajectory, params, plugin) {
+        const builder = plugin.builders.structure;
+
+        const model = await builder.createModel(trajectory, params.model);
+        const modelProperties = !!params.modelProperties
+            ? await builder.insertModelProperties(model, params.modelProperties) : void 0;
+        const modelChildParent = modelProperties || model;
+
+        const structure = await builder.createStructure(modelProperties || model, params.structure);
+        const structureProperties = !!params.structureProperties
+            ? await builder.insertStructureProperties(structure, params.structureProperties) : void 0;
+        const structureChildParent = structureProperties || structure;
+
+        const unitcell = params.showUnitcell === void 0 || !!params.showUnitcell ? await builder.tryCreateUnitcell(modelChildParent, undefined, { isHidden: true }) : void 0;
+
+        const representation = !params.noRepresentation
+            ? await plugin.builders.structure.representation.applyPreset(structureChildParent, params.representationPreset || 'auto')
+            : void 0;
+
+        return {
+            model: modelChildParent,
+            modelRoot: model,
+            modelProperties,
+            unitcell,
+            structure: structureChildParent,
+            structureRoot: structure,
+            structureProperties,
+            representation
+        };
+    }
+});
+
+const allModels = TrajectoryHierarchyPresetProvider({
+    id: 'preset-trajectory-all-models',
+    display: { name: 'All Models', group: 'Preset' },
+    params: CommonParams,
+    async apply(trajectory, params, plugin) {
+        const tr = StateObjectRef.resolveAndCheck(plugin.state.data, trajectory)?.obj?.data;
+        if (!tr) return { };
+
+        const builder = plugin.builders.structure;
+
+        const models = [], structures = [];
+
+        for (let i = 0; i < tr.length; i++) {
+            const model = await builder.createModel(trajectory, { modelIndex: i }, { isCollapsed: true });
+            const structure = await builder.createStructure(model, { name: 'deposited', params: {} });
+            models.push(model);
+            structures.push(structure);
+            await builder.representation.applyPreset(structure, params.representationPreset || 'auto', { globalThemeName: 'model-index' });
+        }
+
+        return { models, structures };
+    }
+});
+
+export const PresetStructureTrajectoryHierarchy = {
+    'first-model': firstModel,
+    'all-models': allModels
+};
+export type PresetStructureTrajectoryHierarchy = typeof PresetStructureTrajectoryHierarchy;
+
+// TODO: should there be a registry like for representations?
+
+export function applyTrajectoryHierarchyPreset<K extends keyof PresetStructureTrajectoryHierarchy>(plugin: PluginContext, parent: StateObjectRef<PluginStateObject.Molecule.Trajectory>, preset: K, params?: Partial<TrajectoryHierarchyPresetProvider.Params<PresetStructureTrajectoryHierarchy[K]>>): Promise<TrajectoryHierarchyPresetProvider.State<PresetStructureTrajectoryHierarchy[K]>> | undefined
+export function applyTrajectoryHierarchyPreset<P = any, S = {}>(plugin: PluginContext, parent: StateObjectRef<PluginStateObject.Molecule.Trajectory>, provider: TrajectoryHierarchyPresetProvider<P, S>, params?: P): Promise<S> | undefined
+export function applyTrajectoryHierarchyPreset(plugin: PluginContext, parent: StateObjectRef, providerRef: string | TrajectoryHierarchyPresetProvider, params?: any): Promise<any> | undefined {
+    const provider = typeof providerRef === 'string' ? (PresetStructureTrajectoryHierarchy as any)[providerRef] : providerRef;
+    if (!provider) return;
+
+    const state = plugin.state.data;
+    const cell = StateObjectRef.resolveAndCheck(state, parent);
+    if (!cell) {
+        if (!isProductionMode) console.warn(`Applying hierarchy preset provider to bad cell.`);
+        return;
+    }
+    const prms = { ...PD.getDefaultValues(provider.params(cell.obj!, plugin) as PD.Params), ...params };
+    const task = Task.create(`${provider.display.name}`, () => provider.apply(cell, prms, plugin) as Promise<any>);
+    return plugin.runTask(task);
+}

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

@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2019 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 { State, StateObjectCell } from '../../../mol-state';
-import { RuntimeContext } from '../../../mol-task';
-import { Structure } from '../../../mol-model/structure';
-import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { PluginStateObject } from '../../objects';
-
-export interface StructureRepresentationProvider<P = any, S = {}> {
-    id: string,
-    display: { name: string, group: string, description?: string },
-    isApplicable?(structure: Structure, plugin: PluginContext): boolean,
-    params?(structure: Structure | undefined, plugin: PluginContext): PD.For<P>,
-    apply(ctx: RuntimeContext, state: State, structure: StateObjectCell<PluginStateObject.Molecule.Structure>, params: P, plugin: PluginContext): Promise<S> | S
-}
-
-export namespace StructureRepresentationProvider {
-    export type Params<P extends StructureRepresentationProvider> = P extends StructureRepresentationProvider<infer T> ? T : never;
-    export type State<P extends StructureRepresentationProvider> = P extends StructureRepresentationProvider<infer _, infer S> ? S : never;
-}
-
-export const enum RepresentationProviderTags {
-    Representation = 'structure-representation',
-    Component = 'preset-structure-component'
-}
-
-export function StructureRepresentationProvider<P, S>(repr: StructureRepresentationProvider<P, S>) { return repr; }

+ 66 - 46
src/mol-plugin-state/builder/structure/preset.ts → src/mol-plugin-state/builder/structure/representation-preset.ts

@@ -5,42 +5,50 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+import { PresetProvider } from '../preset-provider';
+import { PluginStateObject } from '../../objects';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { VisualQuality, VisualQualityOptions } from '../../../mol-geo/geometry/base';
+import { ColorTheme } from '../../../mol-theme/color';
 import { Structure } from '../../../mol-model/structure';
 import { PluginContext } from '../../../mol-plugin/context';
 import { StateObjectRef } from '../../../mol-state';
-import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { StaticStructureComponentType } from '../../helpers/structure-component';
 import { StructureSelectionQueries as Q } from '../../helpers/structure-selection-query';
-import { PluginStateObject } from '../../objects';
-import { RepresentationProviderTags, StructureRepresentationProvider } from './provider';
-import { ColorTheme } from '../../../mol-theme/color';
 
-export const CommonStructureRepresentationParams = {
+export interface StructureRepresentationPresetProvider<P = any, S = {}> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { }
+export namespace StructureRepresentationPresetProvider {
+    export type Params<P extends StructureRepresentationPresetProvider> = P extends StructureRepresentationPresetProvider<infer T> ? T : never;
+    export type State<P extends StructureRepresentationPresetProvider> = P extends StructureRepresentationPresetProvider<infer _, infer S> ? S : never;
+}
+export function StructureRepresentationPresetProvider<P, S>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; }
+
+export const CommonStructureRepresentationPresetParams = {
     ignoreHydrogens: PD.Optional(PD.Boolean(false)),
     quality: PD.Optional(PD.Select<VisualQuality>('auto', VisualQualityOptions)),
     globalThemeName: PD.Optional(PD.Text<ColorTheme.BuiltIn>(''))
 }
-export type CommonStructureRepresentationParams = PD.ValuesFor<typeof CommonStructureRepresentationParams>
+export type CommonStructureRepresentationParams = PD.ValuesFor<typeof CommonStructureRepresentationPresetParams>
 
-const auto = StructureRepresentationProvider({
+const auto = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-auto',
     display: { name: 'Automatic', group: 'Preset' },
-    params: () => CommonStructureRepresentationParams,
-    apply(ctx, state, structureCell, params, plugin) {
-        const structure = structureCell.obj!.data;
+    params: () => CommonStructureRepresentationPresetParams,
+    apply(ref, params, plugin) {
+        const structure = StateObjectRef.resolveAndCheck(plugin.state.data, ref)?.obj?.data;
+        if (!structure) return { };
         const size = Structure.getSize(structure)
 
         switch (size) {
             case Structure.Size.Gigantic:
             case Structure.Size.Huge:
-                return coarseSurface.apply(ctx, state, structureCell, params, plugin);
+                return coarseSurface.apply(ref, params, plugin);
             case Structure.Size.Large:
-                return polymerCartoon.apply(ctx, state, structureCell, params, plugin);
+                return polymerCartoon.apply(ref, params, plugin);
             case Structure.Size.Medium:
-                return polymerAndLigand.apply(ctx, state, structureCell, params, plugin);
+                return polymerAndLigand.apply(ref, params, plugin);
             case Structure.Size.Small:
-                return atomicDetail.apply(ctx, state, structureCell, params, plugin);
+                return atomicDetail.apply(ref, params, plugin);
         }
     }
 });
@@ -59,11 +67,14 @@ function reprBuilder(plugin: PluginContext, params: CommonStructureRepresentatio
     return { update, builder, color, typeParams };
 }
 
-const polymerAndLigand = StructureRepresentationProvider({
+const polymerAndLigand = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-polymer-and-ligand',
     display: { name: 'Polymer & Ligand', group: 'Preset' },
-    params: () => CommonStructureRepresentationParams,
-    async apply(ctx, state, structureCell, params, plugin) {
+    params: () => CommonStructureRepresentationPresetParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        if (!structureCell) return {};
+
         const components = {
             polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
             ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
@@ -86,16 +97,19 @@ const polymerAndLigand = StructureRepresentationProvider({
             coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'polymer-id' })
         };
 
-        await state.updateTree(update, { revertOnError: false }).runInContext(ctx);
+        await plugin.updateDataState(update, { revertOnError: false });
         return { components, representations };
     }
 });
 
-const proteinAndNucleic = StructureRepresentationProvider({
+const proteinAndNucleic = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-protein-and-nucleic',
     display: { name: 'Protein & Nucleic', group: 'Preset' },
-    params: () => CommonStructureRepresentationParams,
-    async apply(ctx, state, structureCell, params, plugin) {
+    params: () => CommonStructureRepresentationPresetParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        if (!structureCell) return {};
+
         const components = {
             protein: await presetSelectionComponent(plugin, structureCell, 'protein'),
             nucleic: await presetSelectionComponent(plugin, structureCell, 'nucleic'),
@@ -107,19 +121,22 @@ const proteinAndNucleic = StructureRepresentationProvider({
             nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams, color })
         };
 
-        await state.updateTree(update, { revertOnError: true }).runInContext(ctx);
+        await plugin.updateDataState(update, { revertOnError: true });
         return { components, representations };
     }
 });
 
-const coarseSurface = StructureRepresentationProvider({
+const coarseSurface = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-coarse-surface',
     display: { name: 'Coarse Surface', group: 'Preset' },
-    params: () => CommonStructureRepresentationParams,
-    async apply(ctx, state, structureCell, params, plugin) {
+    params: () => CommonStructureRepresentationPresetParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        if (!structureCell) return {};
+
         const structure = structureCell.obj!.data;
         const size = Structure.getSize(structure)
-        
+
         const gaussianProps = Object.create(null);
         const components = Object.create(null);
 
@@ -139,22 +156,25 @@ const coarseSurface = StructureRepresentationProvider({
             components.trace = await presetSelectionComponent(plugin, structureCell, 'polymer')
         }
 
-        
+
         const { update, builder, typeParams, color } = reprBuilder(plugin, params);
         const representations = {
             trace: builder.buildRepresentation(update, components.trace, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color })
         };
 
-        await state.updateTree(update, { revertOnError: true }).runInContext(ctx);
+        await plugin.updateDataState(update, { revertOnError: true });
         return { components, representations };
     }
 });
 
-const polymerCartoon = StructureRepresentationProvider({
+const polymerCartoon = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-polymer-cartoon',
     display: { name: 'Polymer Cartoon', group: 'Preset' },
-    params: () => CommonStructureRepresentationParams,
-    async apply(ctx, state, structureCell, params, plugin) {
+    params: () => CommonStructureRepresentationPresetParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        if (!structureCell) return {};
+
         const components = {
             polymer: await presetSelectionComponent(plugin, structureCell, 'polymer'),
         };
@@ -164,16 +184,18 @@ const polymerCartoon = StructureRepresentationProvider({
             polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color })
         };
 
-        await state.updateTree(update, { revertOnError: true }).runInContext(ctx);
+        await plugin.updateDataState(update, { revertOnError: true });
         return { components, representations };
     }
 });
 
-const atomicDetail = StructureRepresentationProvider({
+const atomicDetail = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-atomic-detail',
     display: { name: 'Atomic Detail', group: 'Preset' },
-    params: () => CommonStructureRepresentationParams,
-    async apply(ctx, state, structureCell, params, plugin) {
+    params: () => CommonStructureRepresentationPresetParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        if (!structureCell) return {};
 
         const components = {
             all: await presetSelectionComponent(plugin, structureCell, 'all'),
@@ -184,7 +206,7 @@ const atomicDetail = StructureRepresentationProvider({
             all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color })
         };
 
-        await state.updateTree(update, { revertOnError: true }).runInContext(ctx);
+        await plugin.updateDataState(update, { revertOnError: true });
         return { components, representations };
     }
 });
@@ -193,26 +215,24 @@ export function presetStaticComponent(plugin: PluginContext, structure: StateObj
     return plugin.builders.structure.tryCreateStaticComponent({
         structure,
         type,
-        key: `static-${type}`,
-        tags: [RepresentationProviderTags.Component]
+        key: `static-${type}`
     });
 }
 
 export function presetSelectionComponent(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, query: keyof typeof Q) {
-    return plugin.builders.structure.tryCreateQueryComponent({ 
+    return plugin.builders.structure.tryCreateQueryComponent({
         structure,
         query: Q[query],
-        key: `selection-${query}`,
-        tags: [RepresentationProviderTags.Component]
+        key: `selection-${query}`
     });
 }
 
 export const PresetStructureReprentations = {
     auto,
-    atomicDetail,
-    polymerCartoon,
-    polymerAndLigand,
-    proteinAndNucleic,
-    coarseSurface
+    'atomic-detail': atomicDetail,
+    'polymer-cartoon': polymerCartoon,
+    'polymer-and-ligand': polymerAndLigand,
+    'protein-and-nucleic': proteinAndNucleic,
+    'coarse-surface': coarseSurface
 };
 export type PresetStructureReprentations = typeof PresetStructureReprentations;

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

@@ -5,7 +5,6 @@
  */
 
 import { arrayFind } from '../../../mol-data/util';
-import { Structure } from '../../../mol-model/structure';
 import { PluginContext } from '../../../mol-plugin/context';
 import { StateBuilder, StateObjectRef, StateObjectSelector } from '../../../mol-state';
 import { Task } from '../../../mol-task';
@@ -15,34 +14,37 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { createStructureRepresentationParams, StructureRepresentationBuiltInProps, StructureRepresentationProps } from '../../helpers/structure-representation-params';
 import { PluginStateObject } from '../../objects';
 import { StructureRepresentation3D } from '../../transforms/representation';
-import { PresetStructureReprentations } from './preset';
-import { RepresentationProviderTags, StructureRepresentationProvider } from './provider';
+import { PresetStructureReprentations, StructureRepresentationPresetProvider } from './representation-preset';
 
-export type StructureRepresentationProviderRef = keyof PresetStructureReprentations | StructureRepresentationProvider | string
+export type StructureRepresentationPresetProviderRef = keyof PresetStructureReprentations | StructureRepresentationPresetProvider | string
+
+export const enum StructureRepresentationBuilderTags {
+    Representation = 'structure-representation'
+}
 
 export class StructureRepresentationBuilder {
-    private _providers: StructureRepresentationProvider[] = [];
-    private providerMap: Map<string, StructureRepresentationProvider> = new Map();
+    private _providers: StructureRepresentationPresetProvider[] = [];
+    private providerMap: Map<string, StructureRepresentationPresetProvider> = new Map();
     private get dataState() { return this.plugin.state.data; }
 
     readonly defaultProvider = PresetStructureReprentations.auto;
 
-    private resolveProvider(ref: StructureRepresentationProviderRef) {
+    private resolveProvider(ref: StructureRepresentationPresetProviderRef) {
         return typeof ref === 'string'
             ? PresetStructureReprentations[ref as keyof PresetStructureReprentations] ?? arrayFind(this._providers, p => p.id === ref)
             : ref;
     }
 
-    hasPreset(s: Structure) {
+    hasPreset(s: PluginStateObject.Molecule.Structure) {
         for (const p of this._providers) {
             if (!p.isApplicable || p.isApplicable(s, this.plugin)) return true;
         }
         return false;
     }
 
-    get providers(): ReadonlyArray<StructureRepresentationProvider> { return this._providers; }
+    get providers(): ReadonlyArray<StructureRepresentationPresetProvider> { return this._providers; }
 
-    getPresets(s?: Structure) {
+    getPresets(s?: PluginStateObject.Molecule.Structure) {
         if (!s) return this.providers;
         const ret = [];
         for (const p of this._providers) {
@@ -52,7 +54,7 @@ export class StructureRepresentationBuilder {
         return ret;
     }
 
-    getPresetsWithOptions(s: Structure) {
+    getPresetsWithOptions(s: PluginStateObject.Molecule.Structure) {
         const options: [string, string][] = [];
         const map: { [K in string]: PD.Any } = Object.create(null);
         for (const p of this._providers) {
@@ -65,7 +67,7 @@ export class StructureRepresentationBuilder {
         return PD.MappedStatic(options[0][0], map, { options });
     }
 
-    registerPreset(provider: StructureRepresentationProvider) {
+    registerPreset(provider: StructureRepresentationPresetProvider) {
         if (this.providerMap.has(provider.id)) {
             throw new Error(`Repr. provider with id '${provider.id}' already registered.`);
         }
@@ -73,10 +75,10 @@ export class StructureRepresentationBuilder {
         this.providerMap.set(provider.id, provider);
     }
 
-    applyPreset<K extends keyof PresetStructureReprentations>(parent: StateObjectRef, preset: K, params?: StructureRepresentationProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationProvider.State<PresetStructureReprentations[K]>> | undefined
-    applyPreset<P = any, S = {}>(parent: StateObjectRef, providers: StructureRepresentationProvider<P, S>, params?: P): Promise<S> | undefined
-    applyPreset(parent: StateObjectRef, providerId: string, params?: any): Promise<any> | undefined
-    applyPreset(parent: StateObjectRef, providerRef: string | StructureRepresentationProvider, params?: any): Promise<any> | undefined {
+    applyPreset<K extends keyof PresetStructureReprentations>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, preset: K, params?: StructureRepresentationPresetProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationPresetProvider.State<PresetStructureReprentations[K]>> | undefined
+    applyPreset<P = any, S = {}>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, provider: StructureRepresentationPresetProvider<P, S>, params?: P): Promise<S> | undefined
+    applyPreset(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, providerId: string, params?: any): Promise<any> | undefined
+    applyPreset(parent: StateObjectRef, providerRef: string | StructureRepresentationPresetProvider, params?: any): Promise<any> | undefined {
         const provider = this.resolveProvider(providerRef);
         if (!provider) return;
 
@@ -88,11 +90,11 @@ export class StructureRepresentationBuilder {
         }
 
         const prms = params || (provider.params
-            ? PD.getDefaultValues(provider.params(cell.obj!.data, this.plugin) as PD.Params)
+            ? PD.getDefaultValues(provider.params(cell.obj, this.plugin) as PD.Params)
             : {})
 
 
-        const task = Task.create(`${provider.display.name}`, ctx => provider.apply(ctx, state, cell, prms, this.plugin) as Promise<any>);
+        const task = Task.create(`${provider.display.name}`, () => provider.apply(cell, prms, this.plugin) as Promise<any>);
         return this.plugin.runTask(task);
     }
 
@@ -105,7 +107,7 @@ export class StructureRepresentationBuilder {
         const params = createStructureRepresentationParams(this.plugin, data, props);
         const repr = this.dataState.build()
             .to(structure)
-            .apply(StructureRepresentation3D, params, { tags: RepresentationProviderTags.Representation });
+            .apply(StructureRepresentation3D, params, { tags: StructureRepresentationBuilderTags.Representation });
 
         await this.plugin.updateDataState(repr);
         return  repr.selector;
@@ -121,7 +123,7 @@ export class StructureRepresentationBuilder {
         const params = createStructureRepresentationParams(this.plugin, data, props);
         return builder
             .to(structure)
-            .apply(StructureRepresentation3D, params, { tags: RepresentationProviderTags.Representation })
+            .apply(StructureRepresentation3D, params, { tags: StructureRepresentationBuilderTags.Representation })
             .selector;
     }
 

+ 2 - 2
src/mol-plugin-state/manager/structure/component.ts

@@ -16,7 +16,7 @@ import { UUID } from '../../../mol-util';
 import { arraySetAdd } from '../../../mol-util/array';
 import { ColorNames } from '../../../mol-util/color/names';
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { StructureRepresentationProvider } from '../../builder/structure/provider';
+import { StructureRepresentationPresetProvider } from '../../builder/structure/representation-preset';
 import { PluginComponent } from '../../component';
 import { StructureComponentParams } from '../../helpers/structure-component';
 import { clearStructureOverpaint, setStructureOverpaint } from '../../helpers/structure-overpaint';
@@ -109,7 +109,7 @@ class StructureComponentManager extends PluginComponent<StructureComponentManage
         }
     }
 
-    applyPreset<P = any, S = {}>(structures: ReadonlyArray<StructureRef>, provider: StructureRepresentationProvider<P, S>, params?: P): Promise<any>  {
+    applyPreset<P = any, S = {}>(structures: ReadonlyArray<StructureRef>, provider: StructureRepresentationPresetProvider<P, S>, params?: P): Promise<any>  {
         return this.plugin.dataTransaction(async () => {
             await this.clearComponents(structures);
             for (const s of structures) {

+ 2 - 3
src/mol-plugin-state/manager/structure/hierarchy-state.ts

@@ -7,7 +7,7 @@
 import { PluginStateObject as SO } from '../../objects';
 import { StateObject, StateTransform, State, StateObjectCell, StateTree, StateTransformer } from '../../../mol-state';
 import { StructureBuilderTags } from '../../builder/structure';
-import { RepresentationProviderTags } from '../../builder/structure/provider';
+import { StructureRepresentationBuilderTags } from '../../builder/structure/representation';
 import { StructureRepresentationInteractionTags } from '../../../mol-plugin/behavior/dynamic/selection/structure-representation-interaction';
 import { StateTransforms } from '../../transforms';
 import { VolumeStreaming } from '../../../mol-plugin/behavior/dynamic/volume-streaming/behavior';
@@ -15,7 +15,6 @@ import { CreateVolumeStreamingBehavior } from '../../../mol-plugin/behavior/dyna
 
 export function buildStructureHierarchy(state: State, previous?: StructureHierarchy) {
     const build = BuildState(state, previous || StructureHierarchy());
-    // StateTree.doPreOrder(state.tree, state.tree.root, build, visitCell);
     doPreOrder(state.tree, build);
     if (previous) previous.refs.forEach(isRemoved, build);
     return { hierarchy: build.hierarchy, added: build.added, updated: build.updated, removed: build.removed };
@@ -234,7 +233,7 @@ const tagMap: [string, (state: BuildState, cell: StateObjectCell) => boolean | v
         if (!state.currentStructure) return false;
         state.currentComponent = createOrUpdateRefList(state, cell, state.currentStructure.components, StructureComponentRef, cell, state.currentStructure);
     }, state => state.currentComponent = void 0],
-    [RepresentationProviderTags.Representation, (state, cell) => {
+    [StructureRepresentationBuilderTags.Representation, (state, cell) => {
         if (!state.currentComponent) return false;
         createOrUpdateRefList(state, cell, state.currentComponent.representations, StructureRepresentationRef, cell, state.currentComponent);
     }, state => { }],

+ 15 - 12
src/mol-plugin-state/manager/structure/hierarchy.ts

@@ -9,6 +9,7 @@ import { StructureHierarchy, buildStructureHierarchy, ModelRef, StructureCompone
 import { PluginComponent } from '../../component';
 import { SetUtils } from '../../../mol-util/set';
 import { StateTransform } from '../../../mol-state';
+import { applyTrajectoryHierarchyPreset } from '../../builder/structure/hierarchy-preset';
 
 interface StructureHierarchyManagerState {
     hierarchy: StructureHierarchy,
@@ -156,19 +157,21 @@ export class StructureHierarchyManager extends PluginComponent<StructureHierarch
 
                 const tr = trajectory.cell.obj?.data!;
                 if (kind === 'all' && tr.length > 1) {
-                    for (let i = 0; i < tr.length; i++) {
-                        const model = await this.plugin.builders.structure.createModel(trajectory.cell, { modelIndex: i }, { isCollapsed: true });
-                        const structure = await this.plugin.builders.structure.createStructure(model, { name: 'deposited', params: {} });
-                        this.nextSelection.add(model.ref);
-                        this.nextSelection.add(structure.ref);
-                        await this.plugin.builders.structure.representation.applyPreset(structure, 'auto', { globalThemeName: 'model-index' });
-                    }
+
+                    const { models = [], structures = [] } = (await applyTrajectoryHierarchyPreset(this.plugin, trajectory.cell, 'all-models'))!;
+                    for (const m of models) this.nextSelection.add(m.ref);
+                    for (const s of structures) this.nextSelection.add(s.ref);
                 } else {
-                    const model = await this.plugin.builders.structure.createModel(trajectory.cell, { modelIndex: 0 }, { isCollapsed: true });
-                    const structure = await this.plugin.builders.structure.createStructure(model);
-                    this.nextSelection.add(model.ref);
-                    this.nextSelection.add(structure.ref);
-                    await this.plugin.builders.structure.representation.applyPreset(structure, 'auto');
+
+                    const preset = await applyTrajectoryHierarchyPreset(this.plugin, trajectory.cell, 'first-model');
+
+                    // const model = await this.plugin.builders.structure.createModel(trajectory.cell, { modelIndex: 0 }, { isCollapsed: true });
+                    // const structure = await this.plugin.builders.structure.createStructure(model);
+
+                    if (preset) {
+                        this.nextSelection.add(preset.modelRoot.ref);
+                        this.nextSelection.add(preset.structureRoot.ref);
+                    }
                 }
             }
         });

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

@@ -85,7 +85,7 @@ class ComponentEditorControls extends PurePluginUIComponent<{}, ComponentEditorC
             ActionMenu.Item('Clear', null),
         ];
         const pivot = this.plugin.managers.structure.component.pivotStructure;
-        const providers = this.plugin.builders.structure.representation.getPresets(pivot?.cell.obj?.data)
+        const providers = this.plugin.builders.structure.representation.getPresets(pivot?.cell.obj)
         for (const p of providers) {
             actions.push(ActionMenu.Item(p.display.name, p));
         }

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

@@ -192,7 +192,7 @@ export class StructureSourceControls extends CollapsableControls<{}, StructureSo
         const ret: ActionMenu.Items = [];
 
         const { selection } = this.plugin.managers.structure.hierarchy;
-        if (selection.trajectories.some(t => t.cell.obj?.data.length! > 1)) {
+        if (selection.trajectories.some(t => t.cell.obj?.data.length! > 1 && t.cell.obj?.data.length! !== t.models.length)) {
             ret.push(ActionMenu.Item('Load all models', () => this.plugin.managers.structure.hierarchy.createModels(selection.trajectories, 'all')));
         }
         if (selection.trajectories.some(t => t.models.length > 1)) {

+ 4 - 0
src/mol-state/transformer.ts

@@ -31,6 +31,10 @@ namespace Transformer {
     export type To<T extends Transformer<any, any, any>> = T extends Transformer<any, infer B, any> ? B : unknown;
     export type Cell<T extends Transformer<any, any, any>> = T extends Transformer<any, infer B, any> ? StateObjectCell<B> : unknown;
 
+    export function getParamDefinition<T extends Transformer>(t: T, a: From<T> | undefined, globalCtx: unknown): PD.For<Params<T>> {
+        return t.definition.params ? t.definition.params(a, globalCtx) as any : { } as any;
+    }
+
     export function is(obj: any): obj is Transformer {
         return !!obj && typeof (obj as Transformer).toAction === 'function' && typeof (obj as Transformer).apply === 'function';
     }