Browse Source

Various tweaks and fixes
- allow to hide expand button in Viewport
- principal axes fix for single element
- fixes to preset syncing
- CSS fixes
- picking level grouping

David Sehnal 5 years ago
parent
commit
2008f8538c

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

@@ -15,6 +15,7 @@ import { PluginSpec } from '../../mol-plugin/spec';
 import { LoadCellPackModel } from './extensions/cellpack/model';
 import { StructureFromCellpack } from './extensions/cellpack/state';
 import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
+import { PluginConfig } from '../../mol-plugin/config';
 require('mol-plugin-ui/skin/light.scss')
 
 function getParam(name: string, regex: string): string {
@@ -46,6 +47,7 @@ function init() {
         },
         config: DefaultPluginSpec.config
     };
+    spec.config?.set(PluginConfig.Viewport.ShowExpand, false);
     const plugin = createPlugin(document.getElementById('app')!, spec);
     trySetSnapshot(plugin);
     tryLoadFromUrl(plugin);

+ 1 - 1
src/mol-math/linear-algebra/matrix/principal-axes.ts

@@ -26,7 +26,7 @@ namespace PrincipalAxes {
 
     export function calculateMomentsAxes(positions: NumberArray): Axes3D {
         if (positions.length === 3) {
-            return Axes3D.create(Vec3.fromArray(Vec3(), positions, 0), Vec3(), Vec3(), Vec3())
+            return Axes3D.create(Vec3.fromArray(Vec3(), positions, 0), Vec3.create(1, 0, 0), Vec3.create(0, 1, 0), Vec3.create(0, 1, 0))
         }
 
         const points = Matrix.fromArray(positions, 3, positions.length / 3)

+ 16 - 15
src/mol-model/loci.ts

@@ -192,21 +192,6 @@ namespace Loci {
                 ? StructureElement.Loci.extendToWholeChains(loci)
                 : loci
         },
-        'elementInstances': (loci: Loci) => {
-            return StructureElement.Loci.is(loci)
-                ? StructureElement.Loci.extendToAllInstances(loci)
-                : loci
-        },
-        'residueInstances': (loci: Loci) => {
-            return StructureElement.Loci.is(loci)
-                ? StructureElement.Loci.extendToAllInstances(StructureElement.Loci.extendToWholeResidues(loci, true))
-                : loci
-        },
-        'chainInstances': (loci: Loci) => {
-            return StructureElement.Loci.is(loci)
-                ? StructureElement.Loci.extendToAllInstances(StructureElement.Loci.extendToWholeChains(loci))
-                : loci
-        },
         'entity': (loci: Loci) => {
             return StructureElement.Loci.is(loci)
                 ? StructureElement.Loci.extendToWholeEntities(loci)
@@ -224,9 +209,25 @@ namespace Loci {
                     ? Shape.Loci(loci.shape)
                     : loci
         },
+        'elementInstances': (loci: Loci) => {
+            return StructureElement.Loci.is(loci)
+                ? StructureElement.Loci.extendToAllInstances(loci)
+                : loci
+        },
+        'residueInstances': (loci: Loci) => {
+            return StructureElement.Loci.is(loci)
+                ? StructureElement.Loci.extendToAllInstances(StructureElement.Loci.extendToWholeResidues(loci, true))
+                : loci
+        },
+        'chainInstances': (loci: Loci) => {
+            return StructureElement.Loci.is(loci)
+                ? StructureElement.Loci.extendToAllInstances(StructureElement.Loci.extendToWholeChains(loci))
+                : loci
+        },
     }
     export type Granularity = keyof typeof Granularity
     export const GranularityOptions = ParamDefinition.objectToOptions(Granularity, k => {
+        if (k.indexOf('Instances') > 0) return [stringToWords(k), 'With Symmetry'];
         switch (k) {
             case 'element': return'Atom/Coarse Element'
             case 'structure': return'Structure/Shape'

+ 6 - 0
src/mol-plugin-state/manager/structure/component.ts

@@ -147,6 +147,12 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
             }
         }
 
+        if (root.genericRepresentations) {
+            for (const r of root.genericRepresentations) {
+                sync(r);
+            }
+        }
+
         if (changed) return this.plugin.updateDataState(update);
     }
 

+ 2 - 2
src/mol-plugin-ui/plugin.tsx

@@ -101,7 +101,7 @@ class Layout extends PluginUIComponent {
     render() {
         const layout = this.plugin.layout.state;
         const controls = this.plugin.spec.layout?.controls || { };
-        const viewport = this.plugin.spec.components?.viewport || DefaultViewport;
+        const viewport = this.plugin.spec.components?.viewport?.view || DefaultViewport;
 
         return <div className='msp-plugin'>
             <div className={this.layoutClassName}>
@@ -129,7 +129,7 @@ export class ControlsWrapper extends PluginUIComponent {
 
 export class DefaultViewport extends PluginUIComponent {
     render() {
-        const VPControls = this.plugin.spec.components?.viewportControls || ViewportControls;
+        const VPControls = this.plugin.spec.components?.viewport?.controls || ViewportControls;
 
         return <>
             <Viewport />

+ 1 - 1
src/mol-plugin-ui/skin/base/icons.scss

@@ -115,7 +115,7 @@
 .msp-icon-layout:before { content: "\e810"; }
 .msp-icon-menu:before { content: "\e811"; }
 .msp-icon-check:before { content: "\e812"; }
-.msp-icon-cancel:before { content: "\e813"; margin: 0; }
+.msp-icon-cancel:before { content: "\e813"; }
 .msp-icon-cancel-circled:before { content: "\e814"; }
 .msp-icon-cancel-squared:before { content: "\e815"; }
 .msp-icon-plus-circled:before { content: "\e817"; }

+ 1 - 1
src/mol-plugin-ui/state/common.tsx

@@ -249,7 +249,7 @@ abstract class TransformControlBase<P, S extends TransformControlBase.ComponentS
                 <Icon name={this.props.simpleApply?.icon} />
                 {this.props.simpleApply?.header}
             </button>
-            {!info.isEmpty && <ToggleButton icon='cog' label='' title='Options' toggle={this.toggleExpanded} isSelected={!this.state.isCollapsed} disabled={this.state.busy} style={{ flex: '0 0 40px' }} />}
+            {!info.isEmpty && <ToggleButton icon='cog' label='' title='Options' toggle={this.toggleExpanded} isSelected={!this.state.isCollapsed} disabled={this.state.busy} style={{ flex: '0 0 40px', padding: 0 }} />}
         </div>
 
         if (this.state.isCollapsed) return apply;

+ 5 - 5
src/mol-plugin-ui/structure/components.tsx

@@ -119,8 +119,8 @@ class ComponentEditorControls extends PurePluginUIComponent<{}, ComponentEditorC
             <div className='msp-control-row msp-select-row'>
                 <ToggleButton icon='bookmarks' label='Preset' toggle={this.togglePreset} isSelected={this.state.action === 'preset'} disabled={this.isDisabled} />
                 <ToggleButton icon='plus' label='Add' toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
-                <ToggleButton icon='cog' label='' title='Options' style={{ flex: '0 0 40px' }} toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.isDisabled} />
-                <IconButton customClass='msp-flex-item' style={{ flex: '0 0 40px' }} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} icon='back' title={undoTitle} />
+                <ToggleButton icon='cog' label='' title='Options' style={{ flex: '0 0 40px', padding: 0 }} toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.isDisabled} />
+                <IconButton customClass='msp-flex-item' style={{ flex: '0 0 40px', padding: 0 }} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} icon='back' title={undoTitle} />
             </div>
             {this.state.action === 'preset' && this.presetControls}
             {this.state.action === 'add' && <div className='msp-control-offset'>
@@ -347,9 +347,9 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
                     {label}
                     {/* <small className='msp-25-lower-contrast-text' style={{ float: 'right' }}>{reprLabel}</small> */}
                 </button>
-                <IconButton onClick={this.toggleVisible} icon='visual-visibility' toggleState={!cell.state.isHidden} title={`${cell.state.isHidden ? 'Show' : 'Hide'} component`} small customClass='msp-form-control' style={{ flex: '0 0 32px' }} />
-                <IconButton onClick={this.toggleRemove} icon='remove' title='Remove' small toggleState={this.state.action === 'remove'} customClass='msp-form-control' style={{ flex: '0 0 32px' }} />
-                <IconButton onClick={this.toggleAction} icon='dot-3' title='Actions' toggleState={this.state.action === 'action'} customClass='msp-form-control' style={{ flex: '0 0 32px', padding: '0px' }} />
+                <IconButton onClick={this.toggleVisible} icon='visual-visibility' toggleState={!cell.state.isHidden} title={`${cell.state.isHidden ? 'Show' : 'Hide'} component`} small customClass='msp-form-control' style={{ flex: '0 0 32px', padding: 0 }} />
+                <IconButton onClick={this.toggleRemove} icon='remove' title='Remove' small toggleState={this.state.action === 'remove'} customClass='msp-form-control' style={{ flex: '0 0 32px', padding: 0 }} />
+                <IconButton onClick={this.toggleAction} icon='dot-3' title='Actions' toggleState={this.state.action === 'action'} customClass='msp-form-control' style={{ flex: '0 0 32px', padding: 0 }} />
             </div>
             {this.state.action === 'remove' && <div style={{ marginBottom: '6px' }}>
                 <ActionMenu items={this.removeActions} onSelect={this.selectRemoveAction} />

+ 6 - 3
src/mol-plugin-ui/structure/focus.tsx

@@ -149,7 +149,10 @@ export class StructureFocusControls extends PluginUIComponent<{}, StructureFocus
         if (current) this.plugin.managers.camera.focusLoci(current.loci);
     }
 
-    clear = () => this.plugin.managers.structure.focus.clear()
+    clear = () => {
+        this.plugin.managers.structure.focus.clear();
+        this.plugin.managers.camera.reset();
+    }
 
     highlightCurrent = () => {
         const { current } = this.plugin.managers.structure.focus
@@ -187,8 +190,8 @@ export class StructureFocusControls extends PluginUIComponent<{}, StructureFocus
                     style={{ textAlignLast: current ? 'left' : void 0 }}>
                     {label}
                 </button>
-                {current && <IconButton onClick={this.clear} icon='cancel' title='Clear' customClass='msp-form-control' style={{ flex: '0 0 32px' }} disabled={this.isDisabled} />}
-                <ToggleButton icon='book-open' title='Select Target' toggle={this.toggleAction} isSelected={this.state.showAction} disabled={this.isDisabled} style={{ flex: '0 0 40px' }} />
+                {current && <IconButton onClick={this.clear} icon='cancel' title='Clear' customClass='msp-form-control' style={{ flex: '0 0 32px', padding: 0 }} disabled={this.isDisabled} />}
+                <ToggleButton icon='book-open' title='Select Target' toggle={this.toggleAction} isSelected={this.state.showAction} disabled={this.isDisabled} style={{ flex: '0 0 40px', padding: 0 }} />
             </div>
             {this.state.showAction && <ActionMenu items={this.actionItems} onSelect={this.selectAction} />}
         </>;

+ 2 - 2
src/mol-plugin-ui/structure/generic.tsx

@@ -139,8 +139,8 @@ export class GenericEntry<T extends HierarchyRef> extends PurePluginUIComponent<
                 <button className='msp-form-control msp-control-button-label' title={`${label}. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
                     {label} <small>{description}</small>
                 </button>
-                <IconButton customClass='msp-form-control' onClick={this.toggleVisibility} icon='visual-visibility' toggleState={!pivot.cell.state.isHidden} title={`${pivot.cell.state.isHidden ? 'Show' : 'Hide'}`} small style={{ flex: '0 0 32px' }} />
-                {refs.length === 1 && <IconButton customClass='msp-form-control' onClick={this.toggleOptions} icon='dot-3' title='Options' toggleState={this.state.showOptions} style={{ flex: '0 0 32px', padding: '0px' }} />}
+                <IconButton customClass='msp-form-control' onClick={this.toggleVisibility} icon='visual-visibility' toggleState={!pivot.cell.state.isHidden} title={`${pivot.cell.state.isHidden ? 'Show' : 'Hide'}`} small style={{ flex: '0 0 32px', padding: 0 }} />
+                {refs.length === 1 && <IconButton customClass='msp-form-control' onClick={this.toggleOptions} icon='dot-3' title='Options' toggleState={this.state.showOptions} style={{ flex: '0 0 32px', padding: 0 }} />}
             </div>
             {(refs.length === 1 && this.state.showOptions) && <>
                 <div className='msp-control-offset'>

+ 5 - 5
src/mol-plugin-ui/structure/measurements.tsx

@@ -143,7 +143,7 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
             </button>
             {history.length > 1 && <IconButton small={true} customClass='msp-form-control' onClick={() => this.moveHistory(e, 'up')} icon='up-thin' style={{ flex: '0 0 20px', maxWidth: '20px', padding: 0 }} title={'Move up'} />}
             {history.length > 1 && <IconButton small={true} customClass='msp-form-control' onClick={() => this.moveHistory(e, 'down')} icon='down-thin' style={{ flex: '0 0 20px', maxWidth: '20px', padding: 0 }} title={'Move down'} />}
-            <IconButton small={true} customClass='msp-form-control' onClick={() => this.plugin.managers.structure.selection.modifyHistory(e, 'remove')} icon='remove' style={{ flex: '0 0 32px' }} title={'Remove'} />
+            <IconButton small={true} customClass='msp-form-control' onClick={() => this.plugin.managers.structure.selection.modifyHistory(e, 'remove')} icon='remove' style={{ flex: '0 0 32px', padding: 0 }} title={'Remove'} />
         </div>;
     }
 
@@ -170,7 +170,7 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
         return <>
             <div className='msp-control-row msp-select-row'>
                 <ToggleButton icon='plus' label='Add' toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.state.isBusy} />
-                <ToggleButton icon='cog' label='' title='Options' toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.state.isBusy} style={{ flex: '0 0 40px' }} />
+                <ToggleButton icon='cog' label='' title='Options' toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.state.isBusy} style={{ flex: '0 0 40px', padding: 0 }} />
             </div>
             {this.state.action === 'add' && this.add()}
             {this.state.action === 'options' && <MeasurementsOptions />}
@@ -286,9 +286,9 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
                 <button className='msp-form-control msp-control-button-label msp-no-overflow' title='Click to focus. Hover to highlight.' onClick={this.focus} style={{ width: 'auto', textAlign: 'left' }}>
                     <span dangerouslySetInnerHTML={{ __html: this.label }} />
                 </button>
-                <IconButton small customClass='msp-form-control' onClick={this.toggleVisibility} icon='eye' style={{ flex: '0 0 32px' }} title={cell.state.isHidden ? 'Show' : 'Hide'} toggleState={!cell.state.isHidden} />
-                <IconButton small customClass='msp-form-control' onClick={this.delete} icon='remove' style={{ flex: '0 0 32px' }} title='Delete' />
-                <IconButton customClass='msp-form-control' onClick={this.toggleUpdate} icon='dot-3' style={{ flex: '0 0 32px', padding: '0px' }} title='Actions' toggleState={this.state.showUpdate} />
+                <IconButton small customClass='msp-form-control' onClick={this.toggleVisibility} icon='eye' style={{ flex: '0 0 32px', padding: 0 }} title={cell.state.isHidden ? 'Show' : 'Hide'} toggleState={!cell.state.isHidden} />
+                <IconButton small customClass='msp-form-control' onClick={this.delete} icon='remove' style={{ flex: '0 0 32px', padding: 0 }} title='Delete' />
+                <IconButton customClass='msp-form-control' onClick={this.toggleUpdate} icon='dot-3' style={{ flex: '0 0 32px', padding: 0 }} title='Actions' toggleState={this.state.showUpdate} />
             </div>
             {this.state.showUpdate && <>
                 <ActionMenu items={this.actions} onSelect={this.selectAction} />

+ 2 - 2
src/mol-plugin-ui/structure/selection.tsx

@@ -79,7 +79,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
     focus = () => {
         if (this.plugin.managers.structure.selection.stats.elementCount === 0) return;
         const principalAxes = this.plugin.managers.structure.selection.getPrincipalAxes();
-        const { sphere } = this.plugin.managers.structure.selection.getBoundary()
+        const { sphere } = this.plugin.managers.structure.selection.getBoundary();
         this.plugin.managers.camera.focusSphere(sphere, { principalAxes });
     }
 
@@ -173,7 +173,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
                     style={{ textAlignLast: !empty ? 'left' : void 0 }}>
                     {this.stats}
                 </button>
-                {!empty && <IconButton onClick={this.clear} icon='cancel' title='Clear' customClass='msp-form-control' style={{ flex: '0 0 32px' }} />}
+                {!empty && <IconButton onClick={this.clear} icon='cancel' title='Clear' customClass='msp-form-control' style={{ flex: '0 0 32px', padding: 0 }} />}
             </div>
             <StructureMeasurementsControls />
         </>

+ 1 - 1
src/mol-plugin-ui/structure/source.tsx

@@ -253,7 +253,7 @@ export class StructureSourceControls extends CollapsableControls<{}, StructureSo
                 <button className='msp-btn msp-form-control msp-flex-item msp-no-overflow' onClick={this.toggleHierarchy} style={{ overflow: 'hidden', textOverflow: 'ellipsis' }} disabled={disabled} title={label}>
                     {label}
                 </button>
-                {presets.length > 0 && <IconButton customClass='msp-form-control' style={{ flex: '0 0 32px' }} onClick={this.togglePreset} icon='bookmarks' title='Presets' toggleState={this.state.show === 'presets'} disabled={disabled} />}
+                {presets.length > 0 && <IconButton customClass='msp-form-control' style={{ flex: '0 0 32px', padding: 0 }} onClick={this.togglePreset} icon='bookmarks' title='Presets' toggleState={this.state.show === 'presets'} disabled={disabled} />}
             </div>
             {this.state.show === 'hierarchy' && <ActionMenu items={this.hierarchyItems} onSelect={this.selectHierarchy} multiselect />}
             {this.state.show === 'presets' && <ActionMenu items={presets} onSelect={this.applyPreset} />}

+ 2 - 1
src/mol-plugin-ui/viewport.tsx

@@ -14,6 +14,7 @@ import { ControlGroup, IconButton } from './controls/common';
 import { SimpleSettingsControl } from './viewport/simple-settings';
 import { DownloadScreenshotControls } from './viewport/screenshot';
 import { IconName } from './controls/icons';
+import { PluginConfig } from '../mol-plugin/config';
 
 interface ViewportControlsState {
     isSettingsExpanded: boolean,
@@ -93,7 +94,7 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
                 <div>
                     <div className='msp-semi-transparent-background' />
                     {this.icon('tools', this.toggleControls, 'Toggle Controls', this.plugin.layout.state.showControls)}
-                    {this.icon('expand-layout', this.toggleExpanded, 'Toggle Expanded', this.plugin.layout.state.isExpanded)}
+                    {this.plugin.config.get(PluginConfig.Viewport.ShowExpand) && this.icon('expand-layout', this.toggleExpanded, 'Toggle Expanded', this.plugin.layout.state.isExpanded)}
                     {this.icon('settings', this.toggleSettingsExpanded, 'Settings / Controls Info', this.state.isSettingsExpanded)}
                 </div>
             </div>

+ 2 - 2
src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts

@@ -154,9 +154,9 @@ const assemblySymmetryPreset = StructureRepresentationPresetProvider({
         if (!structureCell || !model) return {};
 
         const assemblySymmetry = await tryCreateAssemblySymmetry(plugin, structureCell);
-        const repr = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: Tag.Cluster as any }, plugin);
+        const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: Tag.Cluster as any }, plugin);
 
-        return { components: repr.components, representations: { ...repr.components, assemblySymmetry } };
+        return { components: preset.components, representations: { ...preset.representations, assemblySymmetry } };
     }
 });
 

+ 3 - 0
src/mol-plugin/config.ts

@@ -29,6 +29,9 @@ export const PluginConfig = {
                 // the following test is to include e.g. 'updated' files from PDBe
                 || (!Model.isFromPdbArchive(s.models[0]) && s.models[0].entryId.length === 4))
         })
+    },
+    Viewport: {
+        ShowExpand: item('viewer.show-expand-button', true)
     }
 }
 

+ 4 - 2
src/mol-plugin/spec.ts

@@ -25,8 +25,10 @@ interface PluginSpec {
     components?: {
         remoteState?: 'none' | 'default',
         structureTools?: React.ComponentClass,
-        viewport?: React.ComponentClass,
-        viewportControls?: React.ComponentClass
+        viewport?: {
+            view?: React.ComponentClass,
+            controls?: React.ComponentClass
+        }
     },
     config?: Map<PluginConfigItem, unknown>
 }

+ 5 - 4
src/mol-util/param-definition.ts

@@ -441,17 +441,18 @@ export namespace ParamDefinition {
      *
      * if options is { [string]: string } and mapping is not provided, use the Value.
      */
-    export function objectToOptions<K extends string, V>(options: { [k in K]: V }, f?: null | ((k: K, v: V) => string)): [K, string][] {
-        const ret: [K, string][] = [];
+    export function objectToOptions<K extends string, V>(options: { [k in K]: V }, f?: null | ((k: K, v: V) => string | [string, string])): [K, string][] {
+        const ret: ([K, string] | [K, string, string])[] = [];
         for (const k of Object.keys(options) as K[]) {
             if (!f) {
                 if (typeof options[k as K] === 'string') ret.push([k as K, options[k as K] as any]);
                 else ret.push([k as K, f === null ? k : stringToWords(k)]);
             } else {
-                ret.push([k, f(k as K, options[k as K])])
+                const o = f(k as K, options[k as K]);
+                ret.push(typeof o === 'string' ? [k, o] : [k, o[0], o[1]]);
             }
         }
-        return ret;
+        return ret as [K, string][];
     }
 
     /**