فهرست منبع

mol-plugin: CreateComplex -> Create3DRepresentationPreset

David Sehnal 5 سال پیش
والد
کامیت
8fd8eb703b

+ 3 - 2
src/apps/viewer/extensions/jolecule.ts

@@ -6,7 +6,7 @@
 
 import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
 import { StateTransforms } from '../../../mol-plugin/state/transforms';
-import { createModelTree, complexRepresentation } from '../../../mol-plugin/state/actions/structure';
+import { createModelTree } from '../../../mol-plugin/state/actions/structure';
 import { PluginContext } from '../../../mol-plugin/context';
 import { PluginStateObject } from '../../../mol-plugin/state/objects';
 import { ParamDefinition } from '../../../mol-util/param-definition';
@@ -19,6 +19,7 @@ import { UUID } from '../../../mol-util';
 import { ColorNames } from '../../../mol-util/color/names';
 import { Camera } from '../../../mol-canvas3d/camera';
 import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
+import { createDefaultStructureComplex } from '../../../mol-plugin/util/structure-complex-helper';
 
 export const CreateJoleculeState = StateAction.build({
     display: { name: 'Jolecule State Import' },
@@ -60,7 +61,7 @@ function createTemplate(plugin: PluginContext, state: State, id: string) {
     const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
     const model = createModelTree(data, 'cif');
     const structure = model.apply(StateTransforms.Model.StructureFromModel);
-    complexRepresentation(plugin, structure, { hideWater: true });
+    createDefaultStructureComplex(plugin, structure);
     return { tree: b.getTree(), structure: structure.ref };
 }
 

+ 7 - 2
src/mol-plugin/context.ts

@@ -43,6 +43,7 @@ import { StructureOverpaintHelper } from './util/structure-overpaint-helper';
 import { PluginToastManager } from './state/toast';
 import { StructureMeasurementManager } from './util/structure-measurement';
 import { ViewportScreenshotWrapper } from './util/viewport-screenshot';
+import { StructureRepresentationManager } from './state/representation/structure';
 
 interface Log {
     entries: List<LogEntry>
@@ -111,7 +112,8 @@ export class PluginContext {
 
     readonly structureRepresentation = {
         registry: new StructureRepresentationRegistry(),
-        themeCtx: { colorThemeRegistry: ColorTheme.createRegistry(), sizeThemeRegistry: SizeTheme.createRegistry() } as ThemeRegistryContext
+        themeCtx: { colorThemeRegistry: ColorTheme.createRegistry(), sizeThemeRegistry: SizeTheme.createRegistry() } as ThemeRegistryContext,
+        manager: void 0 as any as StructureRepresentationManager
     } as const
 
     readonly volumeRepresentation = {
@@ -128,12 +130,12 @@ export class PluginContext {
     readonly customParamEditors = new Map<string, StateTransformParameters.Class>();
 
     readonly helpers = {
+        measurement: new StructureMeasurementManager(this),
         structureSelectionManager: new StructureElementSelectionManager(this),
         structureSelection: new StructureSelectionHelper(this),
         structureRepresentation: new StructureRepresentationHelper(this),
         structureOverpaint: new StructureOverpaintHelper(this),
         substructureParent: new SubstructureParentHelper(this),
-        measurement: new StructureMeasurementManager(this),
         viewportScreenshot: void 0 as ViewportScreenshotWrapper | undefined
     } as const;
 
@@ -271,8 +273,11 @@ export class PluginContext {
         this.interactivity = new Interactivity(this);
         this.lociLabels = new LociLabelManager(this);
 
+        (this.structureRepresentation.manager as StructureRepresentationManager)= new StructureRepresentationManager(this);
+
         this.log.message(`Mol* Plugin ${PLUGIN_VERSION} [${PLUGIN_VERSION_DATE.toLocaleString()}]`);
         if (!isProductionMode) this.log.message(`Development mode enabled`);
         if (isDebugMode) this.log.message(`Debug mode enabled`);
+
     }
 }

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

@@ -24,7 +24,7 @@ export const DefaultPluginSpec: PluginSpec = {
         PluginSpec.Action(StateActions.Structure.DownloadStructure),
         PluginSpec.Action(StateActions.Volume.DownloadDensity),
         PluginSpec.Action(StateActions.DataFormat.OpenFile),
-        PluginSpec.Action(StateActions.Structure.CreateComplexRepresentation),
+        PluginSpec.Action(StateActions.Structure.Create3DRepresentationPreset),
         PluginSpec.Action(StateActions.Structure.EnableModelCustomProps),
         PluginSpec.Action(StateActions.Structure.EnableStructureCustomProps),
 

+ 12 - 35
src/mol-plugin/state/actions/structure.ts

@@ -11,7 +11,6 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { Download } from '../transforms/data';
-import { StructureRepresentation3DHelpers } from '../transforms/representation';
 import { CustomModelProperties, StructureSelectionFromExpression, CustomStructureProperties } from '../transforms/model';
 import { DataFormatProvider, guessCifVariant, DataFormatBuilderOptions } from './data-format';
 import { FileInfo } from '../../../mol-util/file-info';
@@ -91,7 +90,7 @@ type StructureFormat = 'pdb' | 'cif' | 'gro' | '3dg'
 
 //
 
-const DownloadModelRepresentationOptions = ModelStructureRepresentation.getParams(void 0, 'assembly').kind;
+const DownloadModelRepresentationOptions = ModelStructureRepresentation.getParams(void 0, 'assembly').type;
 
 const DownloadStructurePdbIdSourceOptions = PD.Group({
     structure: DownloadModelRepresentationOptions,
@@ -257,45 +256,23 @@ function createStructureTree(ctx: PluginContext, b: StateBuilder.To<PluginStateO
     if (supportProps) {
         root = root.apply(StateTransforms.Model.CustomModelProperties);
     }
-    const structure = root.apply(StateTransforms.Model.StructureFromModel, { kind: params || { name: 'assembly', params: { } } });
+    const structure = root.apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } });
     createDefaultStructureComplex(ctx, structure);
 
     return root;
 }
 
-export function complexRepresentation(
-    ctx: PluginContext, root: StateBuilder.To<PluginStateObject.Molecule.Structure>,
-    params?: { hideSequence?: boolean, hideHET?: boolean, hideWater?: boolean, hideCoarse?: boolean; }
-) {
-    if (!params || !params.hideSequence) {
-        root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'));
-    }
-    if (!params || !params.hideHET) {
-        root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'));
-    }
-    if (!params || !params.hideWater) {
-        root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }));
-    }
-    if (!params || !params.hideCoarse) {
-        root.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'spacefill', {}, 'polymer-id'));
+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.structureRepresentation.manager.hasProvider(a.data); },
+    params(a, plugin: PluginContext) {
+        return {
+            type: plugin.structureRepresentation.manager.getOptions(a.data)
+        };
     }
-}
-
-export const CreateComplexRepresentation = StateAction.build({
-    display: { name: 'Create Complex', description: 'Split the structure into Sequence/Water/Ligands/... ' },
-    from: PluginStateObject.Molecule.Structure
-})(({ ref, state }, ctx: PluginContext) => {
-    const root = state.build().to(ref);
-    createDefaultStructureComplex(ctx, root);
-    return state.updateTree(root);
+})(({ ref, params }, plugin: PluginContext) => {
+    plugin.structureRepresentation.manager.apply(ref, params.type.name, params.type.params);
 });
 
 export const UpdateTrajectory = StateAction.build({

+ 2 - 2
src/mol-plugin/state/representation/model.ts

@@ -50,11 +50,11 @@ export namespace ModelStructureRepresentation {
         }
 
         return {
-            kind: PD.MappedStatic(defaultValue || 'deposited', modes, { options })
+            type: PD.MappedStatic(defaultValue || 'deposited', modes, { options })
         };
     }
 
-    export type Params = PD.Values<ReturnType<typeof getParams>>['kind']
+    export type Params = PD.Values<ReturnType<typeof getParams>>['type']
 
     async function buildAssembly(plugin: PluginContext, ctx: RuntimeContext, model: Model, id?: string) {
         let asm: Assembly | undefined = void 0;

+ 76 - 0
src/mol-plugin/state/representation/structure.ts

@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { arrayFind } from '../../../mol-data/util';
+import { Structure } from '../../../mol-model/structure';
+import { StateTransform } from '../../../mol-state';
+import { Task } from '../../../mol-task';
+import { isProductionMode } from '../../../mol-util/debug';
+import { objectForEach } from '../../../mol-util/object';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { PluginContext } from '../../context';
+import { PresetStructureReprentations } from './structure/preset';
+import { StructureRepresentationProvider } from './structure/providers';
+
+export class StructureRepresentationManager {
+    private providers: StructureRepresentationProvider[] = [];
+
+    hasProvider(s: Structure) {
+        for (const p of this.providers) {
+            if (!p.isApplicable || p.isApplicable(s, this.plugin)) return true;
+        }
+        return false;
+    }
+
+    getOptions(s: Structure) {
+        const options: [string, string][] = [];
+        const map: { [K in string]: PD.Any } = Object.create(null);
+        for (const p of this.providers) {
+            if (p.isApplicable && !p.isApplicable(s, this.plugin)) continue;
+
+            options.push([p.id, p.display.name]);
+            map[p.id] = p.params ? PD.Group(p.params(s, this.plugin)) : PD.EmptyGroup()
+        }
+        if (options.length === 0) return PD.MappedStatic('', { '': PD.EmptyGroup() });
+        return PD.MappedStatic(options[0][0], map, { options });
+    }
+
+    register(provider: StructureRepresentationProvider) {
+        // TODO: sort by group
+        this.providers.push(provider);
+    }
+
+    apply<P>(ref: StateTransform.Ref, providerOrId: StructureRepresentationProvider<P> | string, params?: P) {
+        const provider = typeof providerOrId === 'string'
+            ? arrayFind(this.providers, p => p.id === providerOrId)
+            : providerOrId;
+        if (!provider) return;
+
+        const state = this.plugin.state.dataState;
+        const cell = state.cells.get(ref);
+        if (!cell || !cell.obj || cell.status !== 'ok') {
+            if (!isProductionMode) console.warn(`Applying structure repr. provider to bad cell.`);
+            return;
+        }
+
+        const prms = params || (provider.params
+            ? PD.getDefaultValues(provider.params(cell.obj.data, this.plugin))
+            : {})
+
+        const apply = provider.apply(state, cell, prms, this.plugin);
+
+        if (Task.is(apply)) return this.plugin.runTask(apply);
+        return apply;
+    }
+
+    // init() {
+    //     objectForEach(PresetStructureReprentations, r => this.register(r));
+    // }
+
+    constructor(public plugin: PluginContext) {
+        objectForEach(PresetStructureReprentations, r => this.register(r));
+    }
+}

+ 71 - 0
src/mol-plugin/state/representation/structure/preset.ts

@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { StateTransforms } from '../../transforms';
+import { StructureComplexElementTypes } from '../../transforms/model';
+import { StructureRepresentation3DHelpers } from '../../transforms/representation';
+import { applyBuiltInSelection } from '../../../util/structure-selection-helper';
+import { BuiltInStructureRepresentations } from '../../../../mol-repr/structure/registry';
+import { StructureRepresentationProvider } from './providers';
+
+export const PresetStructureReprentations = {
+    default: StructureRepresentationProvider({
+        id: 'preset-structure-representation-default',
+        display: { name: 'Default', group: 'Preset' },
+        apply(state, structureCell, _, plugin) {
+            const root = state.build().to(structureCell.transform.ref);
+            const structure = structureCell.obj!.data;
+
+            const tags = this.id;
+
+            root.apply(StateTransforms.Model.StructureComplexElement, { type: 'protein-or-nucleic' }, { tags: [this.id, StructureComplexElementTypes['protein-or-nucleic']] })
+                .apply(StateTransforms.Representation.StructureRepresentation3D,
+                    StructureRepresentation3DHelpers.getDefaultParams(plugin, 'cartoon', structure), { tags });
+
+            root.apply(StateTransforms.Model.StructureComplexElement, { type: 'ligand' }, { tags: [this.id, StructureComplexElementTypes.ligand] })
+                .apply(StateTransforms.Representation.StructureRepresentation3D,
+                    StructureRepresentation3DHelpers.getDefaultParams(plugin, 'ball-and-stick', structure), { tags });
+
+            root.apply(StateTransforms.Model.StructureComplexElement, { type: 'modified' }, { tags: [this.id, StructureComplexElementTypes.modified] })
+                .apply(StateTransforms.Representation.StructureRepresentation3D,
+                    StructureRepresentation3DHelpers.getDefaultParamsWithTheme(plugin, 'ball-and-stick', 'polymer-id', structure, void 0), { tags });
+
+            const branched = root.apply(StateTransforms.Model.StructureComplexElement, { type: 'branched' }, { tags: [this.id, StructureComplexElementTypes.branched] })
+
+            branched.apply(StateTransforms.Representation.StructureRepresentation3D,
+                StructureRepresentation3DHelpers.getDefaultParams(plugin, 'ball-and-stick', structure, { alpha: 0.15 }), { tags });
+            branched.apply(StateTransforms.Representation.StructureRepresentation3D,
+                StructureRepresentation3DHelpers.getDefaultParams(plugin, 'carbohydrate', structure), { tags });
+
+            root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }, { tags: [this.id, StructureComplexElementTypes.water] })
+                .apply(StateTransforms.Representation.StructureRepresentation3D,
+                    StructureRepresentation3DHelpers.getDefaultParams(plugin, 'ball-and-stick', structure, { alpha: 0.51 }), { tags });
+
+            root.apply(StateTransforms.Model.StructureComplexElement, { type: 'coarse' }, { tags: [this.id, StructureComplexElementTypes.coarse] })
+                .apply(StateTransforms.Representation.StructureRepresentation3D,
+                    StructureRepresentation3DHelpers.getDefaultParamsWithTheme(plugin, 'spacefill', 'polymer-id', structure, {}), { tags });
+
+            return state.updateTree(root, { revertIfAborted: true });
+        }
+    }),
+    capsid: StructureRepresentationProvider({
+        id: 'preset-structure-representation-capsid',
+        display: { name: 'Capsid', group: 'Preset' },
+        apply(state, structureCell, _, plugin) {
+            const root = plugin.state.dataState.build().to(structureCell.transform.ref);
+            const structure = structureCell.obj!.data;
+
+            const params = StructureRepresentation3DHelpers.createParams(plugin, structure, {
+                repr: [BuiltInStructureRepresentations['gaussian-surface'], () => ({ smoothness: 1 })]
+            });
+
+            applyBuiltInSelection(root, 'polymer', this.id)
+                .apply(StateTransforms.Representation.StructureRepresentation3D, params, { tags: this.id });
+
+            return state.updateTree(root, { revertIfAborted: true });
+        }
+    })
+};

+ 23 - 0
src/mol-plugin/state/representation/structure/providers.ts

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { PluginContext } from '../../../context';
+import { State, StateObjectCell } from '../../../../mol-state';
+import { Task } 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> {
+    id: string,
+    display: { name: string, group: string, description?: string },
+    isApplicable?(structure: Structure, plugin: PluginContext): boolean,
+    params?(structure: Structure | undefined, plugin: PluginContext): PD.Def<P>,
+    // TODO: have create return a "representation structure object" that allows modifications
+    apply(state: State, structure: StateObjectCell<PluginStateObject.Molecule.Structure>, params: P, plugin: PluginContext): Task<any> | Promise<void> | void
+}
+
+export function StructureRepresentationProvider<P>(repr: StructureRepresentationProvider<P>) { return repr; }

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

@@ -208,7 +208,7 @@ const StructureFromModel = PluginStateTransform.BuiltIn({
 })({
     apply({ a, params }, plugin: PluginContext) {
         return Task.create('Build Structure', async ctx => {
-            return ModelStructureRepresentation.create(plugin, ctx, a.data, params && params.kind);
+            return ModelStructureRepresentation.create(plugin, ctx, a.data, params && params.type);
         })
     }
 });

+ 8 - 1
src/mol-plugin/util/structure-selection-helper.ts

@@ -6,7 +6,7 @@
  */
 
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
-import { StateSelection } from '../../mol-state';
+import { StateSelection, StateBuilder } from '../../mol-state';
 import { PluginStateObject } from '../state/objects';
 import { QueryContext, StructureSelection, StructureQuery, StructureElement } from '../../mol-model/structure';
 import { compile } from '../../mol-script/runtime/query/compiler';
@@ -14,6 +14,7 @@ import { Loci } from '../../mol-model/loci';
 import { PluginContext } from '../context';
 import Expression from '../../mol-script/language/expression';
 import { LinkType } from '../../mol-model/structure/model/types';
+import { StateTransforms } from '../state/transforms';
 
 export interface StructureSelectionQuery {
     label: string
@@ -277,6 +278,12 @@ export const StructureSelectionQueries = {
     bonded,
 }
 
+export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
+    return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
+        { expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
+        { tags: customTag ? [query, customTag] : [query] });
+}
+
 //
 
 export type SelectionModifier = 'add' | 'remove' | 'only'

+ 5 - 0
src/mol-task/task.ts

@@ -54,6 +54,11 @@ namespace Task {
         }
     }
 
+    export function is<T = any>(t: any): t is Task<T> {
+        const _t = t as Task<any>;
+        return !!t && typeof _t.id === 'number' && typeof _t.name === 'string' && !!_t.run;
+    }
+
     export interface Aborted { isAborted: true, reason: string, toString(): string }
     export function isAbort(e: any): e is Aborted { return !!e && !!e.isAborted; }
     export function Aborted(reason: string): Aborted { return { isAborted: true, reason, toString() { return `Aborted${reason ? ': ' + reason : ''}`; } }; }

+ 7 - 1
src/mol-util/object.ts

@@ -84,10 +84,16 @@ export function deepClone<T>(source: T): T {
     throw new Error(`Can't clone, type "${typeof source}" unsupported`);
 }
 
-export function mapObjectMap<O extends { [k: string]: T }, T, S>(o: O, f: (v: T) => S): { [k: string]: S } {
+export function mapObjectMap<T, S>(o: { [k: string]: T }, f: (v: T) => S): { [k: string]: S } {
     const ret: any = { };
     for (const k of Object.keys(o)) {
         ret[k] = f((o as any)[k]);
     }
     return ret;
+}
+
+export function objectForEach<T>(o: { [k: string]: T }, f: (v: T, k: string) => void) {
+    for (const k of Object.keys(o)) {
+        f((o as any)[k], k);
+    }
 }

+ 1 - 0
src/mol-util/param-definition.ts

@@ -265,6 +265,7 @@ export namespace ParamDefinition {
     type NonOptionals<P> = { [K in keyof P]-?: undefined extends P[K] ? never: K }[keyof P]
     export type Normalize<P> = Pick<P, NonOptionals<P>> & Partial<Pick<P, Optionals<P>>>
     export type For<P> = { [K in keyof P]-?: Base<P[K]> }
+    export type Def<P> = { [K in keyof P]: Any }
 
     export function getDefaultValues<T extends Params>(params: T) {
         const d: { [k: string]: any } = {}