Browse Source

added StructureSelectionQueryRegistry

- register hasClashes, isAccessible, isBuried from their custom props
Alexander Rose 5 years ago
parent
commit
4a96b45b04

+ 27 - 91
src/mol-plugin-state/helpers/structure-selection-query.ts

@@ -6,8 +6,6 @@
  */
 
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
-import { AccessibleSurfaceAreaProvider, AccessibleSurfaceAreaSymbols } from '../../mol-model-props/computed/accessible-surface-area';
-import { ValidationReport, ValidationReportProvider } from '../../mol-model-props/rcsb/validation-report';
 import { QueryContext, Structure, StructureQuery, StructureSelection } from '../../mol-model/structure';
 import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType } from '../../mol-model/structure/model/types';
 import { PluginContext } from '../../mol-plugin/context';
@@ -382,57 +380,6 @@ const bonded = StructureSelectionQuery('Residues Bonded to Selection', MS.struct
     referencesCurrent: true
 })
 
-const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modifier.union([
-    MS.struct.modifier.wholeResidues([
-        MS.struct.modifier.union([
-            MS.struct.generator.atomGroups({
-                'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
-                'atom-test': ValidationReport.symbols.hasClash.symbol(),
-            })
-        ])
-    ])
-]), {
-    description: 'Select residues with clashes in the wwPDB validation report.',
-    category: StructureSelectionCategory.Residue,
-    ensureCustomProperties: (ctx, structure) => {
-        return ValidationReportProvider.attach(ctx, structure.models[0])
-    }
-})
-
-const isBuried = StructureSelectionQuery('Buried Protein Residues', MS.struct.modifier.union([
-    MS.struct.modifier.wholeResidues([
-        MS.struct.modifier.union([
-            MS.struct.generator.atomGroups({
-                'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
-                'residue-test': AccessibleSurfaceAreaSymbols.isBuried.symbol(),
-            })
-        ])
-    ])
-]), {
-    description: 'Select buried protein residues.',
-    category: StructureSelectionCategory.Residue,
-    ensureCustomProperties: (ctx, structure) => {
-        return AccessibleSurfaceAreaProvider.attach(ctx, structure)
-    }
-})
-
-const isAccessible = StructureSelectionQuery('Accessible Protein Residues', MS.struct.modifier.union([
-    MS.struct.modifier.wholeResidues([
-        MS.struct.modifier.union([
-            MS.struct.generator.atomGroups({
-                'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
-                'residue-test': AccessibleSurfaceAreaSymbols.isAccessible.symbol(),
-            })
-        ])
-    ])
-]), {
-    description: 'Select accessible protein residues.',
-    category: StructureSelectionCategory.Residue,
-    ensureCustomProperties: (ctx, structure) => {
-        return AccessibleSurfaceAreaProvider.attach(ctx, structure)
-    }
-})
-
 const StandardAminoAcids = [
     [['HIS'], 'HISTIDINE'],
     [['ARG'], 'ARGININE'],
@@ -500,52 +447,41 @@ export const StructureSelectionQueries = {
     surroundings,
     complement,
     bonded,
-
-    hasClash,
-    isBuried,
-    isAccessible
 }
 
-export const StructureSelectionQueryList = [
-    ...Object.values(StructureSelectionQueries),
-    ...StandardAminoAcids.map(v => ResidueQuery(v, StructureSelectionCategory.AminoAcid)),
-    ...StandardNucleicBases.map(v => ResidueQuery(v, StructureSelectionCategory.NucleicBase)),
-]
-
-export const StructureSelectionQueryOptions: [StructureSelectionQuery, string, string][] = StructureSelectionQueryList.map(q => [q, q.label, q.category])
-
 export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
     return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
         { expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
         { tags: customTag ? [query, customTag] : [query] });
 }
 
-// namespace StructureSelectionQuery {
-//     // export async function getStructure(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
-//     //     const current = plugin.managers.structure.selection.getStructure(structure)
-//     //     const currentSelection = current ? StructureSelection.Singletons(structure, current) : StructureSelection.Empty(structure);
-
-//     //     if (selectionQuery.ensureCustomProperties) {
-//     //         await selectionQuery.ensureCustomProperties({ fetch: plugin.fetch, runtime }, structure)
-//     //     }
-
-//     //     const result = selectionQuery.query(new QueryContext(structure, { currentSelection }))
-//     //     return StructureSelection.unionStructure(result)
-//     // }
+export class StructureSelectionQueryRegistry {
+    list: StructureSelectionQuery[] = []
+    options: [StructureSelectionQuery, string, string][] = []
+    version = 1
 
-//     // export async function getSelection(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
-//     //     const current = plugin.managers.structure.selection.getStructure(structure)
-//     //     const currentSelection = current ? StructureSelection.Singletons(structure, current) : StructureSelection.Empty(structure);
-
-//     //     if (selectionQuery.ensureCustomProperties) {
-//     //         await selectionQuery.ensureCustomProperties({ fetch: plugin.fetch, runtime }, structure)
-//     //     }
+    add(q: StructureSelectionQuery) {
+        this.list.push(q)
+        this.options.push([q, q.label, q.category])
+        this.version += 1
+    }
 
-//     //     return selectionQuery.query(new QueryContext(structure, { currentSelection }));
-//     // }
+    remove(q: StructureSelectionQuery) {
+        const idx = this.list.indexOf(q)
+        if (idx !== -1) {
+            this.list.splice(idx, 1)
+            this.options.splice(idx, 1)
+            this.version += 1
+        }
+    }
 
-//     // export async function getBundle(plugin: PluginContext, runtime: RuntimeContext, selectionQuery: StructureSelectionQuery, structure: Structure) {
-//     //     const loci = await getLoci(plugin, runtime, selectionQuery, structure);
-//     //     return StructureElement.Bundle.fromLoci(loci);
-//     // }
-// }
+    constructor() {
+        // add built-in
+        this.list.push(
+            ...Object.values(StructureSelectionQueries),
+            ...StandardAminoAcids.map(v => ResidueQuery(v, StructureSelectionCategory.AminoAcid)),
+            ...StandardNucleicBases.map(v => ResidueQuery(v, StructureSelectionCategory.NucleicBase))
+        )
+        this.options.push(...this.list.map(q => [q, q.label, q.category] as [StructureSelectionQuery, string, string]))
+    }
+}

+ 4 - 5
src/mol-plugin-state/manager/structure/component.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
@@ -19,7 +19,7 @@ import { StructureRepresentationPresetProvider } from '../../builder/structure/r
 import { StatefulPluginComponent } from '../../component';
 import { StructureComponentParams } from '../../helpers/structure-component';
 import { clearStructureOverpaint, setStructureOverpaint } from '../../helpers/structure-overpaint';
-import { StructureSelectionQueries, StructureSelectionQuery, StructureSelectionQueryOptions } from '../../helpers/structure-selection-query';
+import { StructureSelectionQueries, StructureSelectionQuery } from '../../helpers/structure-selection-query';
 import { StructureRepresentation3D } from '../../transforms/representation';
 import { HierarchyRef, StructureComponentRef, StructureRef, StructureRepresentationRef } from './hierarchy-state';
 import { createStructureColorThemeParams, createStructureSizeThemeParams } from '../../helpers/structure-representation-params';
@@ -351,11 +351,10 @@ namespace StructureComponentManager {
     }
     export type Options = PD.Values<typeof OptionsParams>
 
-    const SelectionParam = PD.Select(StructureSelectionQueryOptions[1][0], StructureSelectionQueryOptions)
-
     export function getAddParams(plugin: PluginContext) {
+        const { options } = plugin.query.structure.registry
         return {
-            selection: SelectionParam,
+            selection: PD.Select(options[1][0], options),
             representation: getRepresentationTypesSelect(plugin, plugin.managers.structure.component.pivotStructure, [['none', '< Create Later >']]),
             label: PD.Text('')
         };

+ 15 - 9
src/mol-plugin-ui/structure/selection.tsx

@@ -6,7 +6,7 @@
  */
 
 import * as React from 'react';
-import { StructureSelectionQueries, StructureSelectionQuery, StructureSelectionQueryList } from '../../mol-plugin-state/helpers/structure-selection-query';
+import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
 import { InteractivityManager } from '../../mol-plugin-state/manager/interactivity';
 import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component';
 import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
@@ -21,12 +21,6 @@ import { Icon } from '../controls/icons';
 import { ParameterControls } from '../controls/parameters';
 import { StructureMeasurementsControls } from './measurements';
 
-export const DefaultQueries = ActionMenu.createItems(StructureSelectionQueryList, {
-    filter: q => q !== StructureSelectionQueries.current,
-    label: q => q.label,
-    category: q => q.category
-});
-
 const StructureSelectionParams = {
     granularity: InteractivityManager.Params.granularity,
 }
@@ -112,7 +106,20 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
         })
     }
 
-    queries = DefaultQueries
+    private queriesItems: ActionMenu.Items[] = []
+    private queriesVersion = -1
+    get queries () {
+        const { registry } = this.plugin.query.structure
+        if (registry.version !== this.queriesVersion) {
+            this.queriesItems = ActionMenu.createItems(registry.list, {
+                filter: q => q !== StructureSelectionQueries.current,
+                label: q => q.label,
+                category: q => q.category
+            });
+            this.queriesVersion = registry.version
+        }
+        return this.queriesItems
+    }
 
     private showAction(q: StructureSelectionControlsState['action']) {
         return () => this.setState({ action: this.state.action === q ? void 0 : q });
@@ -161,7 +168,6 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
             {this.controls}
             <div className='msp-control-row msp-row-text' style={{ margin: '6px 0' }}>
                 <button className='msp-btn msp-btn-block msp-no-overflow' onClick={this.focus} title='Click to Focus Selection' disabled={empty}>
-                    <Icon name='focus-on-visual' style={{ position: 'absolute', left: '5px' }} />
                     {this.stats}
                 </button>
             </div>

+ 47 - 3
src/mol-plugin/behavior/dynamic/custom-props/computed/accessible-surface-area.ts

@@ -1,17 +1,19 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { PluginBehavior } from '../../../behavior';
 import { ParamDefinition as PD } from '../../../../../mol-util/param-definition';
-import { AccessibleSurfaceAreaProvider } from '../../../../../mol-model-props/computed/accessible-surface-area';
+import { AccessibleSurfaceAreaProvider, AccessibleSurfaceAreaSymbols } from '../../../../../mol-model-props/computed/accessible-surface-area';
 import { Loci } from '../../../../../mol-model/loci';
 import { AccessibleSurfaceAreaColorThemeProvider } from '../../../../../mol-model-props/computed/themes/accessible-surface-area';
 import { OrderedSet } from '../../../../../mol-data/int';
 import { arraySum } from '../../../../../mol-util/array';
 import { DefaultQueryRuntimeTable } from '../../../../../mol-script/runtime/query/compiler';
+import { StructureSelectionQuery, StructureSelectionCategory } from '../../../../../mol-plugin-state/helpers/structure-selection-query';
+import { MolScriptBuilder as MS } from '../../../../../mol-script/language/builder';
 
 export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
     name: 'computed-accessible-surface-area-prop',
@@ -42,6 +44,8 @@ export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean
             this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
             this.ctx.representation.structure.themes.colorThemeRegistry.add(AccessibleSurfaceAreaColorThemeProvider)
             this.ctx.managers.lociLabels.addProvider(this.label);
+            this.ctx.query.structure.registry.add(isBuried)
+            this.ctx.query.structure.registry.add(isAccessible)
         }
 
         unregister() {
@@ -51,6 +55,8 @@ export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean
             this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
             this.ctx.representation.structure.themes.colorThemeRegistry.remove(AccessibleSurfaceAreaColorThemeProvider)
             this.ctx.managers.lociLabels.removeProvider(this.label);
+            this.ctx.query.structure.registry.remove(isBuried)
+            this.ctx.query.structure.registry.remove(isAccessible)
         }
     },
     params: () => ({
@@ -59,6 +65,8 @@ export const AccessibleSurfaceArea = PluginBehavior.create<{ autoAttach: boolean
     })
 });
 
+//
+
 function accessibleSurfaceAreaLabel(loci: Loci): string | undefined {
     if(loci.kind === 'element-loci') {
         if (loci.elements.length === 0) return;
@@ -93,4 +101,40 @@ function accessibleSurfaceAreaLabel(loci: Loci): string | undefined {
 
         return `Accessible Surface Area <small>(Whole Structure)</small>: ${arraySum(accessibleSurfaceArea.area).toFixed(2)} \u212B<sup>2</sup>`;
     }
-}
+}
+
+//
+
+const isBuried = StructureSelectionQuery('Buried Protein Residues', MS.struct.modifier.union([
+    MS.struct.modifier.wholeResidues([
+        MS.struct.modifier.union([
+            MS.struct.generator.atomGroups({
+                'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
+                'residue-test': AccessibleSurfaceAreaSymbols.isBuried.symbol(),
+            })
+        ])
+    ])
+]), {
+    description: 'Select buried protein residues.',
+    category: StructureSelectionCategory.Residue,
+    ensureCustomProperties: (ctx, structure) => {
+        return AccessibleSurfaceAreaProvider.attach(ctx, structure)
+    }
+})
+
+const isAccessible = StructureSelectionQuery('Accessible Protein Residues', MS.struct.modifier.union([
+    MS.struct.modifier.wholeResidues([
+        MS.struct.modifier.union([
+            MS.struct.generator.atomGroups({
+                'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
+                'residue-test': AccessibleSurfaceAreaSymbols.isAccessible.symbol(),
+            })
+        ])
+    ])
+]), {
+    description: 'Select accessible protein residues.',
+    category: StructureSelectionCategory.Residue,
+    ensureCustomProperties: (ctx, structure) => {
+        return AccessibleSurfaceAreaProvider.attach(ctx, structure)
+    }
+})

+ 26 - 1
src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts

@@ -15,6 +15,8 @@ import { ClashesRepresentationProvider } from '../../../../../mol-model-props/rc
 import { DensityFitColorThemeProvider } from '../../../../../mol-model-props/rcsb/themes/density-fit';
 import { cantorPairing } from '../../../../../mol-data/util';
 import { DefaultQueryRuntimeTable } from '../../../../../mol-script/runtime/query/compiler';
+import { StructureSelectionQuery, StructureSelectionCategory } from '../../../../../mol-plugin-state/helpers/structure-selection-query';
+import { MolScriptBuilder as MS } from '../../../../../mol-script/language/builder';
 
 export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
     name: 'rcsb-validation-report-prop',
@@ -47,6 +49,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
             this.ctx.representation.structure.themes.colorThemeRegistry.add(RandomCoilIndexColorThemeProvider)
 
             this.ctx.representation.structure.registry.add(ClashesRepresentationProvider)
+            this.ctx.query.structure.registry.add(hasClash)
         }
 
         update(p: { autoAttach: boolean, showTooltip: boolean }) {
@@ -70,6 +73,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
             this.ctx.representation.structure.themes.colorThemeRegistry.remove(RandomCoilIndexColorThemeProvider)
 
             this.ctx.representation.structure.registry.remove(ClashesRepresentationProvider)
+            this.ctx.query.structure.registry.remove(hasClash)
         }
     },
     params: () => ({
@@ -79,6 +83,8 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
     })
 });
 
+//
+
 function geometryQualityLabel(loci: Loci): string | undefined {
     if (loci.kind === 'element-loci') {
         if (loci.elements.length === 0) return
@@ -254,4 +260,23 @@ function randomCoilIndexLabel(loci: Loci): string | undefined {
 
         return `Random Coil Index ${residueCount}: ${rciAvg.toFixed(2)}`
     }
-}
+}
+
+//
+
+const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modifier.union([
+    MS.struct.modifier.wholeResidues([
+        MS.struct.modifier.union([
+            MS.struct.generator.atomGroups({
+                'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
+                'atom-test': ValidationReport.symbols.hasClash.symbol(),
+            })
+        ])
+    ])
+]), {
+    description: 'Select residues with clashes in the wwPDB validation report.',
+    category: StructureSelectionCategory.Residue,
+    ensureCustomProperties: (ctx, structure) => {
+        return ValidationReportProvider.attach(ctx, structure.models[0])
+    }
+})

+ 7 - 0
src/mol-plugin/context.ts

@@ -53,6 +53,7 @@ import { Representation } from '../mol-repr/representation';
 import { HierarchyRef } from '../mol-plugin-state/manager/structure/hierarchy-state';
 import { PluginUIComponent } from '../mol-plugin-ui/base';
 import { StructureFocusManager } from '../mol-plugin-state/manager/structure/focus';
+import { StructureSelectionQueryRegistry } from '../mol-plugin-state/helpers/structure-selection-query';
 
 export class PluginContext {
     private disposed = false;
@@ -119,6 +120,12 @@ export class PluginContext {
         }
     } as const;
 
+    readonly query = {
+        structure: {
+            registry: new StructureSelectionQueryRegistry()
+        }
+    } as const;
+
     readonly dataFormat = {
         trajectory: TrajectoryFormatRegistry(),
         // TODO: separate registries for format catgories