소스 검색

Issue #8: viewer-backend integration

* Colorization of ranges in chains.
  The blade template renders JavaScript objects for our viewer implementation.

* The viewer gets TRPDB from the pdbtm controller.

* Irrelevant codes are removed from the viewer app.
cycle20 3 년 전
부모
커밋
da64861d6f
4개의 변경된 파일106개의 추가작업 그리고 276개의 파일을 삭제
  1. 9 30
      src/apps/viewer/index.html
  2. 86 243
      src/apps/viewer/index.ts
  3. 10 2
      src/extensions/anvil/algorithm.ts
  4. 1 1
      tsconfig.json

+ 9 - 30
src/apps/viewer/index.html

@@ -39,6 +39,7 @@
     <body>
         <div id="app"></div>
         <script type="text/javascript" src="./molstar.js"></script>
+        <script type="text/javascript" src="http://localhost:8080/valami/data.js"></script>
         <script type="text/javascript">
             function getParam(name, regex) {
                 var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i');
@@ -48,38 +49,16 @@
             var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
             if (debugMode) molstar.setDebugMode(debugMode, debugMode);
 
-            var hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
-            var collapseLeftPanel = getParam('collapse-left-panel', '[^&]+').trim() === '1';
-            var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase();
-            var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase();
             var viewer = new molstar.Viewer('app', {
-                layoutShowControls: !hideControls,
-                viewportShowExpand: false,
-                collapseLeftPanel: collapseLeftPanel,
-                pdbProvider: pdbProvider || 'pdbe',
-                emdbProvider: emdbProvider || 'pdbe',
+                layoutShowControls: true,
+                viewportShowExpand: true,
+                collapseLeftPanel: false
             });
 
-            var snapshotId = getParam('snapshot-id', '[^&]+').trim();
-            if (snapshotId) viewer.setRemoteSnapshot(snapshotId);
-
-            var snapshotUrl = getParam('snapshot-url', '[^&]+').trim();
-            var snapshotUrlType = getParam('snapshot-url-type', '[^&]+').toLowerCase().trim() || 'molj';
-            if (snapshotUrl && snapshotUrlType) viewer.loadSnapshotFromUrl(snapshotUrl, snapshotUrlType);
-
-            var structureUrl = getParam('structure-url', '[^&]+').trim();
-            var structureUrlFormat = getParam('structure-url-format', '[a-z]+').toLowerCase().trim();
-            var structureUrlIsBinary = getParam('structure-url-is-binary', '[^&]+').trim() === '1';
-            if (structureUrl) viewer.loadStructureFromUrl(structureUrl, structureUrlFormat, structureUrlIsBinary);
-
-            var pdb = getParam('pdb', '[^&]+').trim();
-            if (pdb) viewer.loadPdb(pdb);
-
-            var pdbDev = getParam('pdb-dev', '[^&]+').trim();
-            if (pdbDev) viewer.loadPdbDev(pdbDev);
-
-            var emdb = getParam('emdb', '[^&]+').trim();
-            if (emdb) viewer.loadEmdb(emdb);
+            viewer.loadWithUNITMPMembraneRepresentation(
+                'http://localhost:8080/valami/1a0s.trpdb.cif',
+                pdb
+            );
         </script>
     </body>
-</html>
+</html>

+ 86 - 243
src/apps/viewer/index.ts

@@ -5,63 +5,36 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ANVILMembraneOrientation, MembraneOrientation3D, tryCreateMembraneOrientation } from '../../extensions/anvil/behavior';
-import { CellPack } from '../../extensions/cellpack';
-import { DnatcoConfalPyramids } from '../../extensions/dnatco';
-import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
-import { Mp4Export } from '../../extensions/mp4-export';
-import { GeometryExport } from '../../extensions/geo-export';
-import { PDBeStructureQualityReport } from '../../extensions/pdbe';
-import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
-import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
-import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
-import { StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
-import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
-import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
-import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
-import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
-import { PluginStateObject } from '../../mol-plugin-state/objects';
+import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
 import { createPlugin } from '../../mol-plugin-ui';
 import { PluginUIContext } from '../../mol-plugin-ui/context';
 import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
 import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
-import { PluginCommands } from '../../mol-plugin/commands';
 import { PluginConfig } from '../../mol-plugin/config';
 import { PluginSpec } from '../../mol-plugin/spec';
-import { PluginState } from '../../mol-plugin/state';
-import { StateObjectRef, StateObjectSelector } from '../../mol-state';
-import { Asset } from '../../mol-util/assets';
+import { StateBuilder, StateObjectRef } from '../../mol-state';
 import { Color } from '../../mol-util/color';
 import '../../mol-util/polyfill';
 import { ObjectKeys } from '../../mol-util/type-helpers';
 import './embedded.html';
 import './favicon.ico';
 import './index.html';
+import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
+import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
+import { Expression } from '../../mol-script/language/expression';
+import { PluginStateObject } from '../../mol-plugin-state/objects';
 
 require('mol-plugin-ui/skin/light.scss');
 
 export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
 export { setDebugMode, setProductionMode } from '../../mol-util/debug';
 
-const CustomFormats = [
-    ['g3d', G3dProvider] as const
-];
-
 const Extensions = {
-    'cellpack': PluginSpec.Behavior(CellPack),
-    'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
-    'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
-    'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
-    'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
-    'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
-    'g3d': PluginSpec.Behavior(G3DFormat),
-    'mp4-export': PluginSpec.Behavior(Mp4Export),
-    'geo-export': PluginSpec.Behavior(GeometryExport)
+    'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation)
 };
 
 const DefaultViewerOptions = {
-    customFormats: CustomFormats as [string, DataFormatProvider][],
     extensions: ObjectKeys(Extensions),
     layoutIsExpanded: true,
     layoutShowControls: true,
@@ -91,6 +64,85 @@ type ViewerOptions = typeof DefaultViewerOptions;
 export class Viewer {
     plugin: PluginUIContext
 
+
+
+
+
+    ////////////////////////////// UNITMP VIEWER PROTOTYPING SECTION
+
+    async loadWithUNITMPMembraneRepresentation(url: string, selectors: any[] = [], format: any = 'mmcif') {
+        const isBinary = false;
+
+        // David Sehnal's version
+        const data = await this.plugin.builders.data.download({ url, isBinary }); //, { state: { isGhost: true } });
+        const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
+        await this.plugin.builders.structure.hierarchy.applyPreset(
+            trajectory, 'default', { representationPreset: 'preset-membrane-orientation' as any });
+
+        const structure: StateObjectRef<PluginStateObject.Molecule.Structure> =
+            this.plugin.managers.structure.hierarchy.current.models[0].structures[0].cell;
+        const components = {
+            polymer: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer'),
+            ligand: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'ligand'),
+            water: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'water'),
+        };
+
+        const builder = this.plugin.builders.structure.representation;
+        const update = this.plugin.build();
+        if (components.polymer) builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { alpha: 0.5 } }, { tag: 'polymer' });
+        if (components.ligand) builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick' }, { tag: 'ligand' });
+        if (components.water) builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { alpha: 0.6 } }, { tag: 'water' });
+        await update.commit();
+
+        selectors.forEach(selector => {
+            const chain = selector;
+
+            for(let regionKey in chain.regions) {
+              const update = this.plugin.build();
+              const region = chain.regions[regionKey];
+              let color: Color = Color.fromArray(region.color, 0);
+              this.applyColor(chain.chain_id, region.auth_ids, update.to(structure), color);
+              update.commit();
+            }
+        });
+
+        //
+        // reset the camera because the membranes render 1st and the structure might not be fully visible
+        //
+        requestAnimationFrame(() => this.plugin.canvas3d?.requestCameraReset());
+    }
+
+    private applyColor(chain: string, auth_seq_ids: number[], update: StateBuilder.To<any, any>, color: Color) {
+        const label: string = `${chain}.${auth_seq_ids.length}`;
+        console.log(label);
+
+        const query: Expression = this.getQuery(chain, auth_seq_ids);
+
+        // based on https://github.com/molstar/molstar/issues/209
+        update
+            .apply(StateTransforms.Model.StructureSelectionFromExpression, { label: label, expression: query })
+            .apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, update.selector.data, {
+                color: 'uniform',
+                colorParams: { value: color }
+            }));
+    }
+
+    private getQuery(chainId: string, auth_array: number[]): Expression {
+        const query: Expression =
+            MS.struct.generator.atomGroups({
+                'residue-test': MS.core.set.has([MS.set( ...auth_array ), MS.ammp('auth_seq_id')]),
+                'chain-test': MS.core.rel.eq([chainId, MS.ammp('label_asym_id')])
+            });
+        return query;
+    }
+
+    ////////////////////////////// END OF PROTOTYPING SECTION
+
+
+
+
+
+
     constructor(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
         const o = { ...DefaultViewerOptions, ...options };
         const defaultSpec = DefaultPluginUISpec();
@@ -103,7 +155,6 @@ export class Viewer {
             ],
             animations: [...defaultSpec.animations || []],
             customParamEditors: defaultSpec.customParamEditors,
-            customFormats: o?.customFormats,
             layout: {
                 initial: {
                     isExpanded: o.layoutIsExpanded,
@@ -152,215 +203,7 @@ export class Viewer {
         this.plugin = createPlugin(element, spec);
     }
 
-    // Csongor's version
-    applyMembrane() {
-        console.log("under investigation...");
-        console.log(MembraneOrientation3D.id);
-
-        const hier = this.plugin.managers.structure.hierarchy;
-        const stateObj: StateObjectRef<PluginStateObject.Molecule.Structure> =
-            hier.selection.structures[0].components[0].structure.cell;
-
-        // TODO (?): it doesn't work: ApplyAction.apply(MembraneOrientation3D.toAction());
-
-        tryCreateMembraneOrientation(this.plugin, stateObj);
-    }
-
-    setRemoteSnapshot(id: string) {
-        const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
-        return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
-    }
-
-    loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
-        return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
-    }
-
-    loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
-        const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
-        return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
-            source: {
-                name: 'url',
-                params: {
-                    url: Asset.Url(url),
-                    format: format as any,
-                    isBinary,
-                    options: { ...params.source.params.options, representationParams: options?.representationParams as any },
-                }
-            }
-        }));
-    }
-
-    // David Sehnal's version
-    async loadStructureWithMembraneRepresentation(url: string, format: BuiltInTrajectoryFormat, isBinary: boolean) {
-        const data = await this.plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
-        const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
-        await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default', { representationPreset: 'preset-membrane-orientation' as any });
-
-        // reset the camera because the membranes render 1st and the structure might not be fully visible
-        requestAnimationFrame(() => this.plugin.canvas3d?.requestCameraReset());
-    }
-
-    async loadAllModelsOrAssemblyFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
-        const plugin = this.plugin;
-
-        const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
-        const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
-
-        await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'all-models', { useDefaultIfSingleModel: true, representationPresetParams: options?.representationParams });
-    }
-
-    async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) {
-        const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel });
-        const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format);
-        await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default');
-    }
-
-    loadPdb(pdb: string, options?: LoadStructureOptions) {
-        const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
-        const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!;
-        return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
-            source: {
-                name: 'pdb' as const,
-                params: {
-                    provider: {
-                        id: pdb,
-                        server: {
-                            name: provider,
-                            params: PdbDownloadProvider[provider].defaultValue as any
-                        }
-                    },
-                    options: { ...params.source.params.options, representationParams: options?.representationParams as any },
-                }
-            }
-        }));
-    }
-
-    loadPdbDev(pdbDev: string) {
-        const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
-        return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
-            source: {
-                name: 'pdb-dev' as const,
-                params: {
-                    provider: {
-                        id: pdbDev,
-                        encoding: 'bcif',
-                    },
-                    options: params.source.params.options,
-                }
-            }
-        }));
-    }
-
-    loadEmdb(emdb: string, options?: { detail?: number }) {
-        const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!;
-        return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
-            source: {
-                name: 'pdb-emd-ds' as const,
-                params: {
-                    provider: {
-                        id: emdb,
-                        server: provider,
-                    },
-                    detail: options?.detail ?? 3,
-                }
-            }
-        }));
-    }
-
-    /**
-     * @example Load X-ray density from volume server
-        viewer.loadVolumeFromUrl({
-            url: 'https://www.ebi.ac.uk/pdbe/densities/x-ray/1tqn/cell?detail=3',
-            format: 'dscif',
-            isBinary: true
-        }, [{
-            type: 'relative',
-            value: 1.5,
-            color: 0x3362B2
-        }, {
-            type: 'relative',
-            value: 3,
-            color: 0x33BB33,
-            volumeIndex: 1
-        }, {
-            type: 'relative',
-            value: -3,
-            color: 0xBB3333,
-            volumeIndex: 1
-        }], {
-            entryId: ['2FO-FC', 'FO-FC'],
-            isLazy: true
-        });
-     * *********************
-     * @example Load EM density from volume server
-        viewer.loadVolumeFromUrl({
-            url: 'https://maps.rcsb.org/em/emd-30210/cell?detail=6',
-            format: 'dscif',
-            isBinary: true
-        }, [{
-            type: 'relative',
-            value: 1,
-            color: 0x3377aa
-        }], {
-            entryId: 'EMD-30210',
-            isLazy: true
-        });
-     */
-    async loadVolumeFromUrl({ url, format, isBinary }: { url: string, format: BuildInVolumeFormat, isBinary: boolean }, isovalues: VolumeIsovalueInfo[], options?: { entryId?: string | string[], isLazy?: boolean }) {
-        const plugin = this.plugin;
-
-        if (!plugin.dataFormats.get(format)) {
-            throw new Error(`Unknown density format: ${format}`);
-        }
-
-        if (options?.isLazy) {
-            const update = this.plugin.build();
-            update.toRoot().apply(StateTransforms.Data.LazyVolume, {
-                url,
-                format,
-                entryId: options?.entryId,
-                isBinary,
-                isovalues: isovalues.map(v => ({ alpha: 1, volumeIndex: 0, ...v }))
-            });
-            return update.commit();
-        }
-
-        return plugin.dataTransaction(async () => {
-            const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
-
-            const parsed = await plugin.dataFormats.get(format)!.parse(plugin, data, { entryId: options?.entryId });
-            const firstVolume = (parsed.volume || parsed.volumes[0]) as StateObjectSelector<PluginStateObject.Volume.Data>;
-            if (!firstVolume?.isOk) throw new Error('Failed to parse any volume.');
-
-            const repr = plugin.build();
-            for (const iso of isovalues) {
-                repr
-                    .to(parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume)
-                    .apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, firstVolume.data!, {
-                        type: 'isosurface',
-                        typeParams: { alpha: iso.alpha ?? 1, isoValue: iso.type === 'absolute' ? { kind: 'absolute', absoluteValue: iso.value } : { kind: 'relative', relativeValue: iso.value } },
-                        color: 'uniform',
-                        colorParams: { value: iso.color }
-                    }));
-            }
-
-            await repr.commit();
-        });
-    }
-
     handleResize() {
         this.plugin.layout.events.updated.next();
     }
 }
-
-export interface LoadStructureOptions {
-    representationParams?: StructureRepresentationPresetProvider.CommonParams
-}
-
-export interface VolumeIsovalueInfo {
-    type: 'absolute' | 'relative',
-    value: number,
-    color: Color,
-    alpha?: number,
-    volumeIndex?: number
-}

+ 10 - 2
src/extensions/anvil/algorithm.ts

@@ -62,7 +62,7 @@ export function computeANVIL(structure: Structure, props: ANVILProps) {
     });
 }
 
-// avoiding namespace lookup improved performance in Chrome (Aug 2020)
+// avoiding namespace lookup improved performance in Chrome (Aug 2020) <<<<<<<<<<<<<<<<< WTH????
 const v3add = Vec3.add;
 const v3clone = Vec3.clone;
 const v3create = Vec3.create;
@@ -163,15 +163,23 @@ export async function calculate(runtime: RuntimeContext, structure: Structure, p
 
     if (ctx.adjust && !ctx.large) {
         membrane = await adjustThickness(runtime, 'Adjusting membrane thickness...', ctx, membrane, initialHphobHphil);
+        membrane.planePoint1[0] = 0;
+        membrane.planePoint1[1] = 0;
+        membrane.planePoint1[2] = 17.75 / 2;
+        membrane.planePoint2[0] = 0;
+        membrane.planePoint2[1] = 0;
+        membrane.planePoint2[2] = -17.75 / 2;
     }
 
     const normalVector = v3zero();
     const center =  v3zero();
+
     v3sub(normalVector, membrane.planePoint1, membrane.planePoint2);
     v3normalize(normalVector, normalVector);
 
     v3add(center, membrane.planePoint1, membrane.planePoint2);
     v3scale(center, center, 0.5);
+
     const extent = adjustExtent(ctx, membrane, center);
 
     return {
@@ -602,4 +610,4 @@ function setLocation(l: StructureElement.Location, structure: Structure, serialI
     l.unit = structure.units[structure.serialMapping.unitIndices[serialIndex]];
     l.element = structure.serialMapping.elementIndices[serialIndex];
     return l;
-}
+}

+ 1 - 1
tsconfig.json

@@ -21,4 +21,4 @@
     },
     "include": [ "src/**/*" ],
     // "exclude": [ "src/servers/**/*", "src/perf-tests/*", "src/cli/**/*" ]
-}
+}