Browse Source

Added volume streaming to proteopedia-wrapper example + vol. streaming behavior tweaks

David Sehnal 5 years ago
parent
commit
b636cdf9cc

+ 2 - 0
src/examples/proteopedia-wrapper/helpers.ts

@@ -105,6 +105,8 @@ export enum StateElements {
     ModelProps = 'model-props',
     Assembly = 'assembly',
 
+    VolumeStreaming = 'volume-streaming',
+
     Sequence = 'sequence',
     SequenceVisual = 'sequence-visual',
     Het = 'het',

+ 14 - 1
src/examples/proteopedia-wrapper/index.html

@@ -40,6 +40,13 @@
                 width: 100%;
                 display: block;
             }
+
+            #volume-streaming-wrapper {
+                position: absolute;
+                top: 100px;
+                left: 780px;
+                width: 300px;
+            }
         </style>
         <link rel="stylesheet" type="text/css" href="app.css" />
         <script type="text/javascript" src="./index.js"></script>
@@ -55,6 +62,7 @@
             </select>
         </div>
         <div id="app"></div>
+        <div id="volume-streaming-wrapper"></div>
         <script>
             // it might be a good idea to define these colors in a separate script file 
             var CustomColors = [0x00ff00, 0x0000ff];
@@ -66,7 +74,7 @@
 
             function $(id) { return document.getElementById(id); }
         
-            var pdbId = '1eve', assemblyId= 'preferred';
+            var pdbId = '1cbs', assemblyId= 'preferred';
             var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
             var format = 'cif';
             
@@ -151,6 +159,11 @@
             addHetGroupsContainer();
 
             addSeparator();
+            addHeader('Exp. Data');
+            addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper')));
+            addControl('Remove', () => PluginWrapper.experimentalData.remove());
+
+            addSeparator(); 
             addHeader('State');
 
             var snapshot;

+ 27 - 2
src/examples/proteopedia-wrapper/index.ts

@@ -4,6 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import * as ReactDOM from 'react-dom';
 import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
 import './index.html'
 import { PluginContext } from '../../mol-plugin/context';
@@ -13,11 +14,11 @@ import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transfo
 import { Color } from '../../mol-util/color';
 import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
 import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
-import { StateBuilder, StateObject } from '../../mol-state';
+import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
 import { EvolutionaryConservation } from './annotation';
 import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
 import { RxEventHelper } from '../../mol-util/rx-event-helper';
-import { ControlsWrapper } from './ui/controls';
+import { ControlsWrapper, volumeStreamingControls } from './ui/controls';
 import { PluginState } from '../../mol-plugin/state';
 import { Scheduler } from '../../mol-task';
 import { createProteopediaCustomTheme } from './coloring';
@@ -26,6 +27,8 @@ import { BuiltInStructureRepresentations } from '../../mol-repr/structure/regist
 import { BuiltInColorThemes } from '../../mol-theme/color';
 import { BuiltInSizeThemes } from '../../mol-theme/size';
 import { ColorNames } from '../../mol-util/color/tables';
+import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
+import { ParamDefinition } from '../../mol-util/param-definition';
 // import { Vec3 } from 'mol-math/linear-algebra';
 // import { ParamDefinition } from 'mol-util/param-definition';
 // import { Text } from 'mol-geo/geometry/text/text';
@@ -264,6 +267,28 @@ class MolStarProteopediaWrapper {
         }
     }
 
+    private experimentalDataElement?: Element = void 0;
+    experimentalData = {
+        init: async (parent: Element) => {
+            const asm = this.state.select(StateElements.Assembly)[0].obj!;
+            const params = ParamDefinition.getDefaultValues(InitVolumeStreaming.definition.params!(asm, this.plugin));
+            params.behaviorRef = StateElements.VolumeStreaming;
+            params.defaultView = 'box';
+            await this.plugin.runTask(this.state.applyAction(InitVolumeStreaming, params, StateElements.Assembly));
+            this.experimentalDataElement = parent;
+            volumeStreamingControls(this.plugin, parent);
+        },
+        remove: () => {
+            const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
+            if (!r) return;
+            PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
+            if (this.experimentalDataElement) {
+                ReactDOM.unmountComponentAtNode(this.experimentalDataElement);
+                this.experimentalDataElement = void 0;
+            }
+        }
+    }
+
     hetGroups = {
         reset: () => {
             const update = this.state.build().delete(StateElements.HetGroupFocus);

+ 12 - 1
src/examples/proteopedia-wrapper/ui/controls.tsx

@@ -5,10 +5,14 @@
  */
 
 import * as React from 'react';
+import * as ReactDOM from 'react-dom';
 import { PluginUIComponent } from '../../../mol-plugin/ui/base';
-import { CurrentObject } from '../../../mol-plugin/ui/plugin';
+import { CurrentObject, PluginContextContainer } from '../../../mol-plugin/ui/plugin';
 import { AnimationControls } from '../../../mol-plugin/ui/state/animation';
 import { CameraSnapshots } from '../../../mol-plugin/ui/camera';
+import { PluginContext } from '../../../mol-plugin/context';
+import { TransformUpdaterControl } from '../../../mol-plugin/ui/state/update-transform';
+import { StateElements } from '../helpers';
 
 export class ControlsWrapper extends PluginUIComponent {
     render() {
@@ -18,4 +22,11 @@ export class ControlsWrapper extends PluginUIComponent {
             <CameraSnapshots />
         </div>;
     }
+}
+
+export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
+    ReactDOM.render(<PluginContextContainer plugin={plugin}>
+            <TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
+        </PluginContextContainer>,
+        parent);
 }

+ 4 - 2
src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts

@@ -43,13 +43,13 @@ export namespace VolumeStreaming {
         valuesInfo: [{ mean: 0, min: -1, max: 1, sigma: 0.1 }, { mean: 0, min: -1, max: 1, sigma: 0.1 }]
     };
 
-    export function createParams(data?: VolumeServerInfo.Data) {
+    export function createParams(data?: VolumeServerInfo.Data, defaultView?: ViewTypes) {
         // fake the info
         const info = data || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: VolumeIsoValue.relative(0) };
         const box = (data && data.structure.boundary.box) || Box3D.empty();
 
         return {
-            view: PD.MappedStatic(info.kind === 'em' ? 'cell' : 'selection-box', {
+            view: PD.MappedStatic(defaultView || (info.kind === 'em' ? 'cell' : 'selection-box'), {
                 'box': PD.Group({
                     bottomLeft: PD.Vec3(box.min),
                     topRight: PD.Vec3(box.max),
@@ -76,6 +76,8 @@ export namespace VolumeStreaming {
         };
     }
 
+    export type ViewTypes = 'box' | 'selection-box' | 'cell'
+
     export type ParamDefinition = typeof createParams extends (...args: any[]) => (infer T) ? T : never
     export type Params = ParamDefinition extends PD.Params ? PD.Values<ParamDefinition> : {}
 

+ 7 - 3
src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts

@@ -26,10 +26,13 @@ export const InitVolumeStreaming = StateAction.build({
     display: { name: 'Volume Streaming' },
     from: SO.Molecule.Structure,
     params(a) {
+        const method = getStreamingMethod(a && a.data);
         return {
-            method: PD.Select<VolumeServerInfo.Kind>(getStreamingMethod(a && a.data), [['em', 'EM'], ['x-ray', 'X-Ray']]),
+            method: PD.Select<VolumeServerInfo.Kind>(method, [['em', 'EM'], ['x-ray', 'X-Ray']]),
             id: PD.Text((a && a.data.models.length > 0 && a.data.models[0].label) || ''),
-            serverUrl: PD.Text('https://ds.litemol.org')
+            serverUrl: PD.Text('https://ds.litemol.org'),
+            defaultView: PD.Text<VolumeStreaming.ViewTypes>(method === 'em' ? 'cell' : 'selection-box'),
+            behaviorRef: PD.Text('', { isHidden: true })
         };
     },
     isApplicable: (a) => a.data.models.length === 1
@@ -56,7 +59,8 @@ export const InitVolumeStreaming = StateAction.build({
     const infoObj = await state.updateTree(infoTree).runInContext(taskCtx);
 
     const behTree = state.build().to(infoTree.ref).apply(CreateVolumeStreamingBehavior,
-        PD.getDefaultValues(VolumeStreaming.createParams(infoObj.data)));
+        PD.getDefaultValues(VolumeStreaming.createParams(infoObj.data, params.defaultView)),
+        { ref: params.behaviorRef ? params.behaviorRef : void 0 });
 
     if (params.method === 'em') {
         behTree.apply(VolumeStreamingVisual, { channel: 'em' }, { state: { isGhost: true } });

+ 10 - 0
src/mol-plugin/ui/plugin.tsx

@@ -37,6 +37,16 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
     }
 }
 
+export class PluginContextContainer extends React.Component<{ plugin: PluginContext }> {
+    render() {
+        return <PluginReactContext.Provider value={this.props.plugin}>
+            <div className='msp-plugin'>
+                {this.props.children}
+            </div>
+        </PluginReactContext.Provider>;
+    }
+}
+
 class Layout extends PluginUIComponent {
     componentDidMount() {
         this.subscribe(this.plugin.layout.events.updated, () => this.forceUpdate());

+ 2 - 12
src/mol-plugin/ui/state/update-transform.tsx

@@ -9,8 +9,7 @@ import { memoizeLatest } from '../../../mol-util/memoize';
 import { StateTransformParameters, TransformContolBase } from './common';
 import { Observable } from 'rxjs';
 import * as React from 'react';
-import { PluginUIComponent, PluginReactContext } from '../base';
-import { PluginContext } from '../../context';
+import { PluginUIComponent } from '../base';
 
 export { UpdateTransformContol, TransformUpdaterControl };
 
@@ -86,7 +85,7 @@ class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Pr
     }
 }
 
-class TransformUpdaterControl extends PluginUIComponent<{ plugin?: PluginContext, nodeRef: string, initiallyCollapsed?: boolean, header?: StateTransformer.Definition['display'] }> {
+class TransformUpdaterControl extends PluginUIComponent<{ nodeRef: string, initiallyCollapsed?: boolean, header?: StateTransformer.Definition['display'] }> {
     componentDidMount() {
         this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
             if (this.props.nodeRef !== ref || this.plugin.state.dataState !== state) return;
@@ -102,15 +101,6 @@ class TransformUpdaterControl extends PluginUIComponent<{ plugin?: PluginContext
         if (!cell || (cell.status !== 'ok' && cell.status !== 'error')) return null;
 
         const transform = cell.transform;
-
-        if (this.props.plugin) {
-            return <PluginReactContext.Provider value={this.props.plugin}>
-                <div className='msp-plugin'>
-                    <UpdateTransformContol state={state} transform={transform} initiallyCollapsed={this.props.initiallyCollapsed} customHeader={this.props.header} />
-                </div>
-            </PluginReactContext.Provider>;
-        }
-
         return <UpdateTransformContol state={state} transform={transform} initiallyCollapsed={this.props.initiallyCollapsed} customHeader={this.props.header} />;
     }
 }