Browse Source

mol-plugin: UI tweaks

David Sehnal 5 years ago
parent
commit
36d8df1442

+ 161 - 161
src/apps/viewer/extensions/jolecule.ts

@@ -1,168 +1,168 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
-import { StateTransforms } from '../../../mol-plugin/state/transforms';
-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';
-import { PluginCommands } from '../../../mol-plugin/command';
-import { Vec3 } from '../../../mol-math/linear-algebra';
-import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
-import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
-import { Text } from '../../../mol-geo/geometry/text/text';
-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' },
-    params: { id: ParamDefinition.Text('1mbo') },
-    from: PluginStateObject.Root
-})(async ({ ref, state, params }, plugin: PluginContext) => {
-    try {
-        const id = params.id.trim().toLowerCase();
-        const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
-
-        data.sort((a, b) => a.order - b.order);
-
-        await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
-        plugin.state.snapshots.clear();
-
-        const template = createTemplate(plugin, state, id);
-        const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
-        for (const s of snapshots) {
-            plugin.state.snapshots.add(s);
-        }
-
-        PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
-    } catch (e) {
-        plugin.log.error(`Jolecule Failed: ${e}`);
-    }
-});
-
-interface JoleculeSnapshot {
-    order: number,
-    distances: { i_atom1: number, i_atom2: number }[],
-    labels: { i_atom: number, text: string }[],
-    camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
-    selected: number[],
-    text: string
-}
-
-function createTemplate(plugin: PluginContext, state: State, id: string) {
-    const b = new StateBuilder.Root(state.tree);
-    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);
-    createDefaultStructureComplex(plugin, structure);
-    return { tree: b.getTree(), structure: structure.ref };
-}
-
-const labelOptions: ParamDefinition.Values<Text.Params> = {
-    ...ParamDefinition.getDefaultValues(Text.Params),
-    tether: true,
-    sizeFactor: 1.3,
-    attachment: 'bottom-right',
-    offsetZ: 10,
-    background: true,
-    backgroundMargin: 0.2,
-    backgroundColor: ColorNames.skyblue,
-    backgroundOpacity: 0.9
-}
-
-// const distanceLabelOptions = {
+// /**
+//  * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author David Sehnal <david.sehnal@gmail.com>
+//  */
+
+// import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
+// import { StateTransforms } from '../../../mol-plugin/state/transforms';
+// 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';
+// import { PluginCommands } from '../../../mol-plugin/command';
+// import { Vec3 } from '../../../mol-math/linear-algebra';
+// import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
+// import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
+// import { Text } from '../../../mol-geo/geometry/text/text';
+// 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' },
+//     params: { id: ParamDefinition.Text('1mbo') },
+//     from: PluginStateObject.Root
+// })(async ({ ref, state, params }, plugin: PluginContext) => {
+//     try {
+//         const id = params.id.trim().toLowerCase();
+//         const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
+
+//         data.sort((a, b) => a.order - b.order);
+
+//         await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
+//         plugin.state.snapshots.clear();
+
+//         const template = createTemplate(plugin, state, id);
+//         const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
+//         for (const s of snapshots) {
+//             plugin.state.snapshots.add(s);
+//         }
+
+//         PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
+//     } catch (e) {
+//         plugin.log.error(`Jolecule Failed: ${e}`);
+//     }
+// });
+
+// interface JoleculeSnapshot {
+//     order: number,
+//     distances: { i_atom1: number, i_atom2: number }[],
+//     labels: { i_atom: number, text: string }[],
+//     camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
+//     selected: number[],
+//     text: string
+// }
+
+// function createTemplate(plugin: PluginContext, state: State, id: string) {
+//     const b = new StateBuilder.Root(state.tree);
+//     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);
+//     createDefaultStructureComplex(plugin, structure);
+//     return { tree: b.getTree(), structure: structure.ref };
+// }
+
+// const labelOptions: ParamDefinition.Values<Text.Params> = {
 //     ...ParamDefinition.getDefaultValues(Text.Params),
-//     sizeFactor: 1,
-//     offsetX: 0,
-//     offsetY: 0,
+//     tether: true,
+//     sizeFactor: 1.3,
+//     attachment: 'bottom-right',
 //     offsetZ: 10,
 //     background: true,
 //     backgroundMargin: 0.2,
-//     backgroundColor: ColorNames.snow,
+//     backgroundColor: ColorNames.skyblue,
 //     backgroundOpacity: 0.9
 // }
 
-function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
-    const b = new StateBuilder.Root(template.tree);
-
-    let i = 0;
-    for (const l of params.e.labels) {
-        const expression = createExpression([l.i_atom]);
-        const group = b.to(template.structure)
-            .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
-
-        group
-            .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
-            .apply(StateTransforms.Representation.StructureLabels3D, {
-                target: { name: 'static-text', params: { value: l.text || '' } },
-                options: labelOptions
-            });
-
-        group
-            .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', {  }));
-    }
-    if (params.e.selected && params.e.selected.length > 0) {
-        b.to(template.structure)
-            .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
-    }
-    // TODO
-    // for (const l of params.e.distances) {
-    //     b.to('structure')
-    //         .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
-    //         .apply(StateTransforms.Representation.StructureLabels3D, {
-    //             target: { name: 'static-text', params: { value: l. || '' } },
-    //             options: labelOptions
-    //         });
-    // }
-    return PluginStateSnapshotManager.Entry({
-        id: UUID.create22(),
-        data: { tree: StateTree.toJSON(b.getTree()) },
-        camera: {
-            current: getCameraSnapshot(params.e.camera),
-            transitionStyle: 'animate',
-            transitionDurationInMs: 350
-        }
-    }, {
-        name:  params.e.text
-    });
-}
-
-function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
-    const direction = Vec3.sub(Vec3(), e.pos, e.in);
-    Vec3.normalize(direction, direction);
-    const up = Vec3.sub(Vec3(), e.pos, e.up);
-    Vec3.normalize(up, up);
-
-    const s: Camera.Snapshot = {
-        mode: 'perspective',
-        fov: Math.PI / 4,
-        position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
-        target: e.pos,
-        radius: (e.slab.z_back - e.slab.z_front) / 2,
-        fog: 50,
-        up,
-    };
-    return s;
-}
-
-function createExpression(atomIndices: number[]) {
-    if (atomIndices.length === 0) return MS.struct.generator.empty();
-
-    return MS.struct.generator.atomGroups({
-        'atom-test': atomIndices.length === 1
-            ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
-            : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
-        'group-by': 0
-    });
-}
+// // const distanceLabelOptions = {
+// //     ...ParamDefinition.getDefaultValues(Text.Params),
+// //     sizeFactor: 1,
+// //     offsetX: 0,
+// //     offsetY: 0,
+// //     offsetZ: 10,
+// //     background: true,
+// //     backgroundMargin: 0.2,
+// //     backgroundColor: ColorNames.snow,
+// //     backgroundOpacity: 0.9
+// // }
+
+// function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
+//     const b = new StateBuilder.Root(template.tree);
+
+//     let i = 0;
+//     for (const l of params.e.labels) {
+//         const expression = createExpression([l.i_atom]);
+//         const group = b.to(template.structure)
+//             .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
+
+//         group
+//             .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
+//             .apply(StateTransforms.Representation.StructureLabels3D, {
+//                 target: { name: 'static-text', params: { value: l.text || '' } },
+//                 options: labelOptions
+//             });
+
+//         group
+//             .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
+//             .apply(StateTransforms.Representation.StructureRepresentation3D,
+//                 StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', {  }));
+//     }
+//     if (params.e.selected && params.e.selected.length > 0) {
+//         b.to(template.structure)
+//             .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
+//             .apply(StateTransforms.Representation.StructureRepresentation3D,
+//                 StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
+//     }
+//     // TODO
+//     // for (const l of params.e.distances) {
+//     //     b.to('structure')
+//     //         .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
+//     //         .apply(StateTransforms.Representation.StructureLabels3D, {
+//     //             target: { name: 'static-text', params: { value: l. || '' } },
+//     //             options: labelOptions
+//     //         });
+//     // }
+//     return PluginStateSnapshotManager.Entry({
+//         id: UUID.create22(),
+//         data: { tree: StateTree.toJSON(b.getTree()) },
+//         camera: {
+//             current: getCameraSnapshot(params.e.camera),
+//             transitionStyle: 'animate',
+//             transitionDurationInMs: 350
+//         }
+//     }, {
+//         name:  params.e.text
+//     });
+// }
+
+// function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
+//     const direction = Vec3.sub(Vec3(), e.pos, e.in);
+//     Vec3.normalize(direction, direction);
+//     const up = Vec3.sub(Vec3(), e.pos, e.up);
+//     Vec3.normalize(up, up);
+
+//     const s: Camera.Snapshot = {
+//         mode: 'perspective',
+//         fov: Math.PI / 4,
+//         position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
+//         target: e.pos,
+//         radius: (e.slab.z_back - e.slab.z_front) / 2,
+//         fog: 50,
+//         up,
+//     };
+//     return s;
+// }
+
+// function createExpression(atomIndices: number[]) {
+//     if (atomIndices.length === 0) return MS.struct.generator.empty();
+
+//     return MS.struct.generator.atomGroups({
+//         'atom-test': atomIndices.length === 1
+//             ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
+//             : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
+//         'group-by': 0
+//     });
+// }

+ 1 - 2
src/apps/viewer/index.ts

@@ -11,7 +11,6 @@ import './favicon.ico'
 import { PluginContext } from '../../mol-plugin/context';
 import { PluginCommands } from '../../mol-plugin/command';
 import { PluginSpec } from '../../mol-plugin/spec';
-import { CreateJoleculeState } from './extensions/jolecule';
 import { LoadCellPackModel } from './extensions/cellpack/model';
 import { StructureFromCellpack } from './extensions/cellpack/state';
 require('mol-plugin/skin/light.scss')
@@ -27,7 +26,7 @@ function init() {
     const spec: PluginSpec = {
         actions: [
             ...DefaultPluginSpec.actions,
-            PluginSpec.Action(CreateJoleculeState),
+            // PluginSpec.Action(CreateJoleculeState),
             PluginSpec.Action(LoadCellPackModel),
             PluginSpec.Action(StructureFromCellpack),
         ],

+ 2 - 2
src/mol-plugin/skin/base/components/viewport.scss

@@ -47,8 +47,8 @@
 .msp-viewport-controls-buttons {
     text-align: right;
     position: relative;
-    opacity: 0.5;
-    &:hover { opacity: 1.0; }
+    // opacity: 0.5;
+    // &:hover { opacity: 1.0; }
 
     > div {
         position: relative;

+ 16 - 31
src/mol-plugin/ui/left-panel.tsx

@@ -7,7 +7,7 @@
 import * as React from 'react';
 import { PluginUIComponent } from './base';
 import { StateTree } from './state/tree';
-import { IconButton, SectionHeader, ControlGroup } from './controls/common';
+import { IconButton, SectionHeader } from './controls/common';
 import { StateObjectActions } from './state/actions';
 import { StateTransform } from '../../mol-state';
 import { PluginCommands } from '../command';
@@ -15,8 +15,9 @@ import { ParameterControls } from './controls/parameters';
 import { Canvas3DParams } from '../../mol-canvas3d/canvas3d';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { StateSnapshots } from './state/snapshots';
+import { HelpContent } from './viewport/help';
 
-type TabName = 'none' | 'root' | 'data' | 'behavior' | 'states' | 'viewport-settings'
+type TabName = 'none' | 'root' | 'data' | 'states' | 'settings' | 'help'
 
 export class LeftPanelControls extends PluginUIComponent<{}, { tab: TabName }> {
     state = { tab: 'data' as TabName };
@@ -32,10 +33,9 @@ export class LeftPanelControls extends PluginUIComponent<{}, { tab: TabName }> {
             return;
         }
 
-
         switch (tab) {
             case 'data': this.plugin.state.setKind('data'); break;
-            case 'behavior': this.plugin.state.setKind('behavior'); break;
+            case 'settings': this.plugin.state.setKind('behavior'); break;
         }
 
         this.setState({ tab });
@@ -55,13 +55,13 @@ export class LeftPanelControls extends PluginUIComponent<{}, { tab: TabName }> {
             <StateTree state={this.plugin.state.dataState} />
         </>,
         'states': <StateSnapshots />,
-        'behavior': <>
-            <SectionHeader icon='address' title='Plugin Behavior' />
-            <StateTree state={this.plugin.state.behaviorState} />
-        </>,
-        'viewport-settings': <>
+        'settings': <>
             <SectionHeader icon='settings' title='Plugin Settings' />
             <FullSettings />
+        </>,
+        'help': <>
+            <SectionHeader icon='help-circle' title='Help' />
+            <HelpContent />
         </>
     }
 
@@ -73,9 +73,9 @@ export class LeftPanelControls extends PluginUIComponent<{}, { tab: TabName }> {
                 <IconButton icon='home' toggleState={tab === 'root'} onClick={() => this.set('root')} title='Home' />
                 <IconButton icon='flow-tree' toggleState={tab === 'data'} onClick={() => this.set('data')} title='State Tree' />
                 <IconButton icon='floppy' toggleState={tab === 'states'} onClick={() => this.set('states')} title='Plugin State' />
+                <IconButton icon='help-circle' toggleState={tab === 'help'} onClick={() => this.set('help')} title='Help' />
                 <div className='msp-left-panel-controls-buttons-bottom'>
-                    <IconButton icon='address' toggleState={tab === 'behavior'} onClick={() => this.set('behavior')} title='Plugin Behavior' />
-                    <IconButton icon='settings' toggleState={tab === 'viewport-settings'} onClick={() => this.set('viewport-settings')} title='Viewport Settings' />
+                    <IconButton icon='settings' toggleState={tab === 'settings'} onClick={() => this.set('settings')} title='Settings' />
                 </div>
             </div>
             <div className='msp-scrollable-container'>
@@ -90,18 +90,6 @@ class FullSettings extends PluginUIComponent {
         PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { [p.name]: p.value } });
     }
 
-    setLayout = (p: { param: PD.Base<any>, name: string, value: any }) => {
-        PluginCommands.Layout.Update.dispatch(this.plugin, { state: { [p.name]: p.value } });
-    }
-
-    setInteractivityProps = (p: { param: PD.Base<any>, name: string, value: any }) => {
-        PluginCommands.Interactivity.SetProps.dispatch(this.plugin, { props: { [p.name]: p.value } });
-    }
-
-    screenshot = () => {
-        this.plugin.helpers.viewportScreenshot?.download();
-    }
-
     componentDidMount() {
         this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
         this.subscribe(this.plugin.layout.events.updated, () => this.forceUpdate());
@@ -114,15 +102,12 @@ class FullSettings extends PluginUIComponent {
 
     render() {
         return <>
-            {/* <ControlGroup header='Layout' initialExpanded={true}>
-                <ParameterControls params={PluginLayoutStateParams} values={this.plugin.layout.state} onChange={this.setLayout} />
-            </ControlGroup>
-            <ControlGroup header='Interactivity' initialExpanded={true}>
-                <ParameterControls params={Interactivity.Params} values={this.plugin.interactivity.props} onChange={this.setInteractivityProps} />
-            </ControlGroup> */}
-            {this.plugin.canvas3d && <ControlGroup header='Viewport' initialExpanded={true}>
+            {this.plugin.canvas3d && <>
+                <SectionHeader title='Viewport' />
                 <ParameterControls params={Canvas3DParams} values={this.plugin.canvas3d.props} onChange={this.setSettings} />
-            </ControlGroup>}
+            </>}
+            <SectionHeader title='Behavior' />
+            <StateTree state={this.plugin.state.behaviorState} />
         </>
     }
 }

+ 1 - 1
src/mol-plugin/ui/toast.tsx

@@ -2,7 +2,7 @@
  * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * Adapted from LiteMol (c) David Sehnal
- * 
+ *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 

+ 4 - 5
src/mol-plugin/ui/viewport.tsx

@@ -12,7 +12,6 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PluginUIComponent } from './base';
 import { ControlGroup, IconButton } from './controls/common';
 import { SimpleSettingsControl } from './viewport/simple-settings';
-import { HelpContent } from './viewport/help';
 
 interface ViewportControlsState {
     isSettingsExpanded: boolean,
@@ -98,16 +97,16 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
                     {this.icon('expand-layout', this.toggleExpanded, 'Toggle Expanded', this.plugin.layout.state.isExpanded)}
                     {this.icon('settings', this.toggleSettingsExpanded, 'Settings', this.state.isSettingsExpanded)}
                 </div>
-                <div>
+                {/* <div>
                     <div className='msp-semi-transparent-background' />
                     {this.icon('help-circle', this.toggleHelpExpanded, 'Help', this.state.isHelpExpanded)}
-                </div>
+                </div> */}
             </div>
-            {this.state.isHelpExpanded && <div className='msp-viewport-controls-panel'>
+            {/* {this.state.isHelpExpanded && <div className='msp-viewport-controls-panel'>
                 <ControlGroup header='Help' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleHelpExpanded} topRightIcon='off'>
                     <HelpContent />
                 </ControlGroup>
-            </div>}
+            </div>} */}
             {this.state.isSettingsExpanded && <div className='msp-viewport-controls-panel'>
                 <ControlGroup header='Basic Settings' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleSettingsExpanded} topRightIcon='off'>
                     <SimpleSettingsControl />