Browse Source

Merge branch 'master' into dev-sb-strucmotifwizard

# Conflicts:
#	package-lock.json
#	package.json
JonStargaryen 4 years ago
parent
commit
a90b729513
6 changed files with 165 additions and 17 deletions
  1. 1 1
      README.md
  2. 2 1
      package.json
  3. 3 0
      src/viewer/assets.ts
  4. 149 12
      src/viewer/index.ts
  5. 3 2
      tsconfig.json
  6. 7 1
      webpack.config.js

+ 1 - 1
README.md

@@ -33,7 +33,7 @@ From the root of the project:
 
     http-server -p PORT-NUMBER
 
-and navigate to `build/dist/structure-viewer/`
+and navigate to `build/dist/viewer/`
 
 ## Prerelease
     npm version prerelease # assumes the current version ends with '-dev.X'

+ 2 - 1
package.json

@@ -31,7 +31,8 @@
     },
     "main": "build/src/index.js",
     "files": [
-        "build/dist/"
+        "build/dist/",
+        "build/src/viewer/"
     ],
     "author": "RCSB PDB and Mol* Contributors",
     "license": "MIT",

+ 3 - 0
src/viewer/assets.ts

@@ -0,0 +1,3 @@
+import './index.html';
+import './favicon.ico';
+require('./skin/rcsb.scss');

+ 149 - 12
src/viewer/index.ts

@@ -7,16 +7,15 @@
 import { BehaviorSubject } from 'rxjs';
 import { DefaultPluginSpec } from 'molstar/lib/mol-plugin';
 import { Plugin } from 'molstar/lib/mol-plugin-ui/plugin';
-import './index.html';
-import './favicon.ico';
 import { PluginContext } from 'molstar/lib/mol-plugin/context';
 import { PluginCommands } from 'molstar/lib/mol-plugin/commands';
 import { ViewerState as ViewerState, CollapsedState, ModelUrlProvider } from './types';
 import { PluginSpec } from 'molstar/lib/mol-plugin/spec';
 
 import { ColorNames } from 'molstar/lib/mol-util/color/names';
-import ReactDOM = require('react-dom');
-import React = require('react');
+import * as React from 'react';
+import * as ReactDOM from 'react-dom';
+
 import { ModelLoader } from './helpers/model';
 import { PresetProps } from './helpers/preset';
 import { ControlsWrapper } from './ui/controls';
@@ -28,15 +27,22 @@ import { PluginState } from 'molstar/lib/mol-plugin/state';
 import { BuiltInTrajectoryFormat } from 'molstar/lib/mol-plugin-state/formats/trajectory';
 import { ObjectKeys } from 'molstar/lib/mol-util/type-helpers';
 import { PluginLayoutControlsDisplay } from 'molstar/lib/mol-plugin/layout';
-require('./skin/rcsb.scss');
+import {Structure} from 'molstar/lib/mol-model/structure/structure';
+import {Script} from 'molstar/lib/mol-script/script';
+import {MolScriptBuilder} from 'molstar/lib/mol-script/language/builder';
+import {SetUtils} from 'molstar/lib/mol-util/set';
+import {Loci} from 'molstar/lib/mol-model/loci';
+import {StructureSelection} from 'molstar/lib/mol-model/structure/query';
+import {StructureRef} from 'molstar/lib/mol-plugin-state/manager/structure/hierarchy-state';
+import {StructureSelectionQuery} from 'molstar/lib/mol-plugin-state/helpers/structure-selection-query';
 
 /** package version, filled in at bundle build time */
 declare const __RCSB_MOLSTAR_VERSION__: string;
-export const RCSB_MOLSTAR_VERSION = __RCSB_MOLSTAR_VERSION__;
+export const RCSB_MOLSTAR_VERSION = typeof __RCSB_MOLSTAR_VERSION__ != 'undefined' ? __RCSB_MOLSTAR_VERSION__ : 'none';
 
 /** unix time stamp, to be filled in at bundle build time */
 declare const __BUILD_TIMESTAMP__: number;
-export const BUILD_TIMESTAMP = __BUILD_TIMESTAMP__;
+export const BUILD_TIMESTAMP = typeof __BUILD_TIMESTAMP__ != 'undefined' ? __BUILD_TIMESTAMP__ : 'none';
 export const BUILD_DATE = new Date(BUILD_TIMESTAMP);
 
 const Extensions = {
@@ -74,7 +80,7 @@ const DefaultViewerProps = {
     backgroundColor: ColorNames.white,
     showWelcomeToast: true
 };
-type ViewerProps = typeof DefaultViewerProps
+export type ViewerProps = typeof DefaultViewerProps
 
 export class Viewer {
     private readonly plugin: PluginContext;
@@ -145,10 +151,25 @@ export class Viewer {
 
         this.plugin.init();
         ReactDOM.render(React.createElement(Plugin, { plugin: this.plugin }), target);
-
-        const renderer = this.plugin.canvas3d!.props.renderer;
-        PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: o.backgroundColor } } });
-
+        // TODO Check why this.plugin.canvas3d can be null
+        // this.plugin.canvas3d can be null. The value is not assigned until React Plugin component is mounted
+        // Next wait Promise guarantees that its value is defined
+        const wait: Promise<null> = new Promise<null>((resolve, reject)=>{
+            const recursive: () => void = () => {
+                if(this.plugin.canvas3d != null){
+                    resolve();
+                }else{
+                    setTimeout(()=>{
+                        recursive();
+                    }, 100);
+                }
+            };
+            recursive();
+        });
+        wait.then(result=>{
+            const renderer = this.plugin.canvas3d!.props.renderer;
+            PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: o.backgroundColor } } });
+        });
         if (o.showWelcomeToast) {
             PluginCommands.Toast.Show(this.plugin, {
                 title: 'Welcome',
@@ -200,4 +221,120 @@ export class Viewer {
     async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, isBinary: boolean, props?: PresetProps & { dataLabel?: string }, matrix?: Mat4) {
         return this.customState.modelLoader.parse({ data, format, isBinary }, props, matrix);
     }
+
+    pluginCall(f: (plugin: PluginContext) => void){
+        f(this.plugin);
+    }
+
+    public getPlugin(): PluginContext {
+        return this.plugin;
+    }
+
+    public select(selection: Array<{modelId: string; asymId: string; position: number;}>, mode: 'select'|'hover'): void;
+    public select(modelId: string, asymId: string, position: number, mode: 'select'|'hover'): void;
+    public select(modelId: string, asymId: string, begin: number, end: number, mode: 'select'|'hover'): void;
+    public select(...args: any[]){
+        if(args.length === 2){
+            this.clearSelection('select');
+            (args[0] as Array<{modelId: string; asymId: string; position: number;}>).forEach(r=>{
+                this.selectSegment(r.modelId, r.asymId, r.position, r.position, args[1], 'add');
+            });
+        }else if(args.length === 4){
+            this.selectSegment(args[0], args[1], args[2], args[2], args[3]);
+        }else if(args.length === 5){
+            this.selectSegment(args[0], args[1], args[2], args[3], args[4]);
+        }
+    }
+    private selectSegment(modelId: string, asymId: string, begin: number, end: number, mode: 'select'|'hover', modifier: 'add'|'set' = 'set'): void {
+        const data: Structure | undefined = getStructureWithModelId(this.plugin.managers.structure.hierarchy.current.structures, modelId);
+        if (data == null) return;
+        const seq_id: Array<number> = new Array<number>();
+        for(let n = begin; n <= end; n++){
+            seq_id.push(n);
+        }
+        const sel: StructureSelection = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
+            'chain-test': Q.core.rel.eq([asymId, MolScriptBuilder.ammp('label_asym_id')]),
+            'residue-test': Q.core.set.has([MolScriptBuilder.set(...SetUtils.toArray(new Set(seq_id))), MolScriptBuilder.ammp('label_seq_id')])
+        }), data);
+        const loci: Loci = StructureSelection.toLociWithSourceUnits(sel);
+        if(mode == null || mode === 'select')
+            this.plugin.managers.structure.selection.fromLoci(modifier, loci);
+        else if(mode === 'hover')
+            this.plugin.managers.interactivity.lociHighlights.highlight({ loci });
+    }
+    public clearSelection(mode: 'select'|'hover'): void {
+        if(mode == null || mode === 'select')
+            this.plugin.managers.interactivity.lociSelects.deselectAll();
+        else if(mode === 'hover')
+            this.plugin.managers.interactivity.lociHighlights.clearHighlights();
+    }
+
+    public async createComponentFromSet(componentId: string, modelId: string, residues: Array<{asymId: string, position: number}>, representationType: 'ball-and-stick'|'spacefill'|'gaussian-surface'|'cartoon'){
+        const structureRef: StructureRef | undefined = getStructureRefWithModelId(this.plugin.managers.structure.hierarchy.current.structures, modelId);
+        if(structureRef == null)
+            return;
+
+        await this.plugin.managers.structure.component.add({
+            selection: StructureSelectionQuery(
+                'innerLabel',
+                MolScriptBuilder.struct.combinator.merge(
+                    residues.map(r=>MolScriptBuilder.struct.generator.atomGroups({
+                        'chain-test': MolScriptBuilder.core.rel.eq([r.asymId, MolScriptBuilder.ammp('label_asym_id')]),
+                        'residue-test': MolScriptBuilder.core.rel.eq([r.position, MolScriptBuilder.ammp('label_seq_id')])
+                    }))
+                )
+            ),
+            options: { checkExisting: true, label: componentId },
+            representation: representationType,
+        }, [structureRef]);
+    }
+
+    public async createComponentFromRange(componentId: string, modelId: string, asymId: string, begin: number, end: number, representationType: 'ball-and-stick'|'spacefill'|'gaussian-surface'|'cartoon'){
+        const structureRef: StructureRef | undefined = getStructureRefWithModelId(this.plugin.managers.structure.hierarchy.current.structures, modelId);
+        if(structureRef == null)
+            return;
+        const seq_id: Array<number> = new Array<number>();
+        for(let n = begin; n <= end; n++){
+            seq_id.push(n);
+        }
+        await this.plugin.managers.structure.component.add({
+            selection: StructureSelectionQuery(
+                'innerLabel',
+                MolScriptBuilder.struct.generator.atomGroups({
+                    'chain-test': MolScriptBuilder.core.rel.eq([asymId, MolScriptBuilder.ammp('label_asym_id')]),
+                    'residue-test': MolScriptBuilder.core.set.has([MolScriptBuilder.set(...SetUtils.toArray(new Set(seq_id))), MolScriptBuilder.ammp('label_seq_id')])
+                })
+            ),
+            options: { checkExisting: true, label: componentId },
+            representation: representationType,
+        }, [structureRef]);
+    }
+
+    public removeComponent(componentId: string): void{
+        this.plugin.managers.structure.hierarchy.currentComponentGroups.forEach(c=>{
+            for(const comp of c){
+                if(comp.cell.obj?.label === componentId) {
+                    this.plugin.managers.structure.hierarchy.remove(c);
+                    break;
+                }
+            }
+        });
+    }
+}
+
+function getStructureRefWithModelId(structures: StructureRef[], modelId: string): StructureRef|undefined{
+    for(const structure of structures){
+        if(!structure.cell?.obj?.data?.units)
+            continue;
+        const unit =  structure.cell.obj.data.units[0];
+        const id: string = unit.model.id;
+        if(id === modelId)
+            return structure;
+    }
+}
+
+function getStructureWithModelId(structures: StructureRef[], modelId: string): Structure|undefined{
+    const structureRef: StructureRef | undefined = getStructureRefWithModelId(structures, modelId);
+    if(structureRef != null)
+        return structureRef.cell?.obj?.data;
 }

+ 3 - 2
tsconfig.json

@@ -14,7 +14,8 @@
         "jsx": "react",
         "lib": [ "es6", "dom", "esnext.asynciterable", "es2016" ],
         "outDir": "build/src",
-        "rootDir": "src"
+        "rootDir": "src",
+        "declaration": true
     },
-    "include": [ "src/**/*" ],
+    "include": [ "src/**/*" ]
 }

+ 7 - 1
webpack.config.js

@@ -60,5 +60,11 @@ module.exports = [
             path: path.resolve(__dirname, `build/dist/viewer`)
         },
         ...sharedConfig
-    },
+    },{
+        entry: path.resolve(__dirname, `build/src/viewer/assets.js`),
+        output: {
+            path: path.resolve(__dirname, `build/dist/viewer`)
+        },
+        ...sharedConfig
+    }
 ]