Browse Source

lighting demo

Alexander Rose 5 years ago
parent
commit
c7b618c246
3 changed files with 254 additions and 0 deletions
  1. 87 0
      src/apps/demos/lighting/index.html
  2. 166 0
      src/apps/demos/lighting/index.ts
  3. 1 0
      webpack.config.js

+ 87 - 0
src/apps/demos/lighting/index.html

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8" />
+        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+        <title>Mol* Lighting Demo</title>
+        <style>
+            * {
+                margin: 0;
+                padding: 0;
+                box-sizing: border-box;
+            }
+            #app {
+                position: absolute;
+                left: 160px;
+                top: 100px;
+                width: 600px;
+                height: 600px;
+                border: 1px solid #ccc;
+            }
+
+            #controls {
+                position: absolute;
+                width: 150px;
+                top: 100px;
+                left: 780px;
+            }
+
+            #controls > button {
+                display: block;
+                width: 100%;
+                text-align: left;
+                margin: 5px 0px;
+            }
+
+            #controls > input, #controls > select {
+                width: 100%;
+                display: block;
+            }
+        </style>
+        <link rel="stylesheet" type="text/css" href="app.css" />
+        <script type="text/javascript" src="./index.js"></script>
+    </head>
+    <body>
+        <div id='controls'></div>
+        <div id="app"></div>
+        <script>      
+            LightingDemo.init('app')
+            LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' })
+            
+            addHeader('Example PDB IDs');
+            addControl('1M07', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' }));
+            addControl('6HY0', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6HY0.cif', assemblyId: '1' }));
+            addControl('6QVK', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6QVK.cif', assemblyId: '1' }));
+            addControl('1RB8', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1RB8.cif', assemblyId: '1' }));
+
+            addSeparator()
+
+            addHeader('Lighting Presets');
+            addControl('Illustrative', () => LightingDemo.setPreset('illustrative'));
+            addControl('Standard', () => LightingDemo.setPreset('standard'));
+            addControl('Ambient Occlusion', () => LightingDemo.setPreset('occlusion'));
+
+            ////////////////////////////////////////////////////////
+
+            function $(id) { return document.getElementById(id); }
+
+            function addControl(label, action) {
+                var btn = document.createElement('button');
+                btn.onclick = action;
+                btn.innerText = label;
+                $('controls').appendChild(btn);
+            }
+
+            function addSeparator() {
+                var hr = document.createElement('br');
+                $('controls').appendChild(hr);
+            }
+
+            function addHeader(header) {
+                var h = document.createElement('h3');
+                h.innerText = header;
+                $('controls').appendChild(h);
+            }
+        </script>
+    </body>
+</html>

+ 166 - 0
src/apps/demos/lighting/index.ts

@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createPlugin, DefaultPluginSpec } from '../../../mol-plugin';
+import './index.html'
+import { PluginContext } from '../../../mol-plugin/context';
+import { PluginCommands } from '../../../mol-plugin/command';
+import { StateTransforms } from '../../../mol-plugin/state/transforms';
+import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
+import { PluginStateObject as PSO } from '../../../mol-plugin/state/objects';
+import { StateBuilder } from '../../../mol-state';
+import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
+require('mol-plugin/skin/light.scss')
+
+type SupportedFormats = 'cif' | 'pdb'
+type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
+
+const Canvas3DPresets = {
+    illustrative: {
+        multiSample: {
+            mode: 'temporal' as Canvas3DProps['multiSample']['mode']
+        },
+        postprocessing: {
+            occlusionEnable: true,
+            occlusionBias: 0.8,
+            occlusionKernelSize: 6,
+            outlineEnable: true,
+        },
+        renderer: {
+            ambientIntensity: 1,
+            lightIntensity: 0,
+        }
+    },
+    occlusion: {
+        multiSample: {
+            mode: 'temporal' as Canvas3DProps['multiSample']['mode']
+        },
+        postprocessing: {
+            occlusionEnable: true,
+            occlusionBias: 0.8,
+            occlusionKernelSize: 6,
+            outlineEnable: false,
+        },
+        renderer: {
+            ambientIntensity: 0.4,
+            lightIntensity: 0.6,
+        }
+    },
+    standard: {
+        multiSample: {
+            mode: 'off' as Canvas3DProps['multiSample']['mode']
+        },
+        postprocessing: {
+            occlusionEnable: false,
+            outlineEnable: false,
+        },
+        renderer: {
+            ambientIntensity: 0.4,
+            lightIntensity: 0.6,
+        }
+    }
+}
+
+type Canvas3DPreset = keyof typeof Canvas3DPresets
+
+function getPreset(preset: Canvas3DPreset) {
+    switch (preset) {
+        case 'illustrative': return Canvas3DPresets['illustrative']
+        case 'standard': return Canvas3DPresets['standard']
+        case 'occlusion': return Canvas3DPresets['occlusion']
+    }
+}
+
+class LightingDemo {
+    plugin: PluginContext;
+
+    init(target: string | HTMLElement) {
+        this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
+            ...DefaultPluginSpec,
+            layout: {
+                initial: {
+                    isExpanded: false,
+                    showControls: false
+                },
+                controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' }
+            }
+        });
+
+        this.setPreset('illustrative');
+    }
+
+    setPreset(preset: Canvas3DPreset) {
+        const props = getPreset(preset)
+        PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
+            ...props,
+            multiSample: {
+                ...this.plugin.canvas3d.props.multiSample,
+                ...props.multiSample
+            },
+            renderer: {
+                ...this.plugin.canvas3d.props.renderer,
+                ...props.renderer
+            },
+            postprocessing: {
+                ...this.plugin.canvas3d.props.postprocessing,
+                ...props.postprocessing
+            },
+        }});
+    }
+
+    private download(b: StateBuilder.To<PSO.Root>, url: string) {
+        return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
+    }
+
+    private parse(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
+        const parsed = format === 'cif'
+            ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
+            : b.apply(StateTransforms.Model.TrajectoryFromPDB);
+
+        return parsed
+            .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
+            .apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
+    }
+
+    private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
+        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
+            .apply(StateTransforms.Representation.StructureRepresentation3D,
+                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill', {}, 'illustrative'), { ref: 'seq-visual' });
+        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
+            .apply(StateTransforms.Representation.StructureRepresentation3D,
+                StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
+        return visualRoot;
+    }
+
+    private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
+    async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
+        let loadType: 'full' | 'update' = 'full';
+
+        const state = this.plugin.state.dataState;
+
+        if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
+            loadType = 'full';
+        } else if (this.loadedParams.url === url) {
+            if (state.select('asm').length > 0) loadType = 'update';
+        }
+
+        let tree: StateBuilder.Root;
+        if (loadType === 'full') {
+            await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
+            tree = state.build();
+            this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
+        } else {
+            tree = state.build();
+            tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
+        }
+
+        await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
+        this.loadedParams = { url, format, assemblyId };
+        PluginCommands.Camera.Reset.dispatch(this.plugin, { });
+    }
+}
+
+(window as any).LightingDemo = new LightingDemo();

+ 1 - 0
webpack.config.js

@@ -93,6 +93,7 @@ module.exports = [
     createApp('viewer'),
     createApp('basic-wrapper'),
     createEntry('examples/proteopedia-wrapper/index', 'examples/proteopedia-wrapper', 'index'),
+    createEntry('apps/demos/lighting/index', 'demos/lighting', 'index'),
     createNodeApp('state-docs'),
     createNodeEntryPoint('preprocess', 'servers/model', 'model-server'),
     createApp('model-server-query'),