Browse Source

StructureSelectionQuery improvements

- added categories
- StructureSelectionQueryList with standard residues
Alexander Rose 5 years ago
parent
commit
d103cda0c8
2 changed files with 151 additions and 126 deletions
  1. 20 91
      src/mol-plugin-ui/structure/selection.tsx
  2. 131 35
      src/mol-plugin/util/structure-selection-helper.ts

+ 20 - 91
src/mol-plugin-ui/structure/selection.tsx

@@ -7,107 +7,36 @@
 
 import * as React from 'react';
 import { CollapsableControls, CollapsableState } from '../base';
-import { StructureSelectionQueries, StructureSelectionQuery, SelectionModifier } from '../../mol-plugin/util/structure-selection-helper';
+import { StructureSelectionQuery, SelectionModifier, StructureSelectionQueryList } from '../../mol-plugin/util/structure-selection-helper';
 import { PluginCommands } from '../../mol-plugin/command';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Interactivity } from '../../mol-plugin/util/interactivity';
 import { ParameterControls } from '../controls/parameters';
-import { stripTags, stringToWords } from '../../mol-util/string';
+import { stripTags } from '../../mol-util/string';
 import { StructureElement } from '../../mol-model/structure';
 import { ActionMenu } from '../controls/action-menu';
-import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
 import { ToggleButton } from '../controls/common';
 
-const SSQ = StructureSelectionQueries
-
-function SSQItem(name: keyof typeof SSQ) {
-    return ActionMenu.Item(SSQ[name].label, SSQ[name])
-}
-
-const StandardAminoAcids = [
-    [['HIS'], 'HISTIDINE'],
-    [['ARG'], 'ARGININE'],
-    [['LYS'], 'LYSINE'],
-    [['ILE'], 'ISOLEUCINE'],
-    [['PHE'], 'PHENYLALANINE'],
-    [['LEU'], 'LEUCINE'],
-    [['TRP'], 'TRYPTOPHAN'],
-    [['ALA'], 'ALANINE'],
-    [['MET'], 'METHIONINE'],
-    [['CYS'], 'CYSTEINE'],
-    [['ASN'], 'ASPARAGINE'],
-    [['VAL'], 'VALINE'],
-    [['GLY'], 'GLYCINE'],
-    [['SER'], 'SERINE'],
-    [['GLN'], 'GLUTAMINE'],
-    [['TYR'], 'TYROSINE'],
-    [['ASP'], 'ASPARTIC ACID'],
-    [['GLU'], 'GLUTAMIC ACID'],
-    [['THR'], 'THREONINE'],
-    [['SEC'], 'SELENOCYSTEINE'],
-    [['PYL'], 'PYRROLYSINE'],
-].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][]
-
-const StandardNucleicBases = [
-    [['A', 'DA'], 'ADENOSINE'],
-    [['C', 'DC'], 'CYTIDINE'],
-    [['T', 'DT'], 'THYMIDINE'],
-    [['G', 'DG'], 'GUANOSINE'],
-    [['I', 'DI'], 'INOSINE'],
-    [['U', 'DU'], 'URIDINE'],
-].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][]
-
-function ResidueItem([names, label]: [string[], string]) {
-    const query = StructureSelectionQuery(names.join(', '), MS.struct.modifier.union([
-        MS.struct.generator.atomGroups({
-            'residue-test': MS.core.set.has([MS.set(...names), MS.ammp('auth_comp_id')])
-        })
-    ]))
-    return ActionMenu.Item(`${names.join(', ')} (${stringToWords(label)})`, query)
+function createDefaultQueries() {
+    const cats = new Map<string, (ActionMenu.Item | string)[]>();
+    const items: (ActionMenu.Item | (ActionMenu.Item | string)[] | string)[] = [];
+    for (const q of StructureSelectionQueryList) {
+        if (!!q.category) {
+            let cat = cats.get(q.category);
+            if (!cat) {
+                cat = [q.category];
+                cats.set(q.category, cat);
+                items.push(cat);
+            }
+            cat.push(ActionMenu.Item(q.label, q));
+        } else {
+            items.push(ActionMenu.Item(q.label, q));
+        }
+    }
+    return items as ActionMenu.Items;
 }
 
-const DefaultQueries = [
-    SSQItem('all'),
-    [
-        'Type',
-        SSQItem('polymer'),
-        SSQItem('protein'),
-        SSQItem('nucleic'),
-        SSQItem('branched'),
-        SSQItem('ligand'),
-        SSQItem('water'),
-    ],
-    [
-        'Structure',
-        SSQItem('trace'),
-        SSQItem('backbone'),
-        SSQItem('helix'),
-        SSQItem('beta'),
-        SSQItem('ring'),
-        SSQItem('aromaticRing'),
-        SSQItem('nonStandardPolymer'),
-    ],
-    [
-        'Amino Acids',
-        SSQItem('isBuried'),
-        SSQItem('isAccessible'),
-        ...StandardAminoAcids.map(v => ResidueItem(v)),
-    ],
-    [
-        'Nucleic Bases',
-        ...StandardNucleicBases.map(v => ResidueItem(v)),
-    ],
-    [
-        'Manipulate',
-        SSQItem('surroundings'),
-        SSQItem('complement'),
-        SSQItem('bonded'),
-    ],
-    [
-        'Validation',
-        SSQItem('hasClash'),
-    ]
-] as unknown as ActionMenu.Items
+export const DefaultQueries = createDefaultQueries()
 
 const StructureSelectionParams = {
     granularity: Interactivity.Params.granularity,

+ 131 - 35
src/mol-plugin/util/structure-selection-helper.ts

@@ -20,36 +20,64 @@ import { ValidationReport, ValidationReportProvider } from '../../mol-model-prop
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
 import { Task } from '../../mol-task';
 import { AccessibleSurfaceAreaSymbols, AccessibleSurfaceAreaProvider } from '../../mol-model-props/computed/accessible-surface-area';
+import { stringToWords } from '../../mol-util/string';
+
+export enum StructureSelectionCategory {
+    Type = 'Type',
+    Structure = 'Structure Property',
+    Atom = 'Atom Property',
+    Bond = 'Bond Property',
+    Residue = 'Residue Property',
+    AminoAcid = 'Amino Acid',
+    NucleicBase = 'Nucleic Base',
+    Manipulate = 'Manipulate Selection',
+    Validation = 'Validation',
+    Misc = 'Miscellaneous',
+    Internal = 'Internal',
+}
+
+export { StructureSelectionQuery }
 
-export interface StructureSelectionQuery {
+interface StructureSelectionQuery {
     readonly label: string
     readonly expression: Expression
     readonly description: string
+    readonly category: string
+    readonly isHidden: boolean
     readonly query: StructureQuery
     readonly ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
 }
 
-export function StructureSelectionQuery(label: string, expression: Expression, description = '', ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>): StructureSelectionQuery {
+interface StructureSelectionQueryProps {
+    description?: string,
+    category?: string
+    isHidden?: boolean
+    ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
+}
+
+function StructureSelectionQuery(label: string, expression: Expression, props: StructureSelectionQueryProps = {}): StructureSelectionQuery {
     let _query: StructureQuery
     return {
         label,
         expression,
-        description,
+        description: props.description || '',
+        category: props.category ?? StructureSelectionCategory.Misc,
+        isHidden: !!props.isHidden,
         get query() {
             if (!_query) _query = compile<StructureSelection>(expression)
             return _query
         },
-        ensureCustomProperties
+        ensureCustomProperties: props.ensureCustomProperties
     }
 }
 
-const all = StructureSelectionQuery('All', MS.struct.generator.all())
+const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '' })
 
 const polymer = StructureSelectionQuery('Polymer', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
         'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
     })
-]))
+]), { category: StructureSelectionCategory.Type })
 
 const trace = StructureSelectionQuery('Trace', MS.struct.modifier.union([
     MS.struct.combinator.merge([
@@ -69,7 +97,7 @@ const trace = StructureSelectionQuery('Trace', MS.struct.modifier.union([
             })
         ])
     ])
-]))
+]), { category: StructureSelectionCategory.Structure })
 
 // TODO maybe pre-calculate atom properties like backbone/sidechain
 const backbone = StructureSelectionQuery('Backbone', MS.struct.modifier.union([
@@ -101,7 +129,7 @@ const backbone = StructureSelectionQuery('Backbone', MS.struct.modifier.union([
             })
         ])
     ])
-]))
+]), { category: StructureSelectionCategory.Structure })
 
 const protein = StructureSelectionQuery('Protein', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -113,7 +141,7 @@ const protein = StructureSelectionQuery('Protein', MS.struct.modifier.union([
             ])
         ])
     })
-]))
+]), { category: StructureSelectionCategory.Type })
 
 const nucleic = StructureSelectionQuery('Nucleic', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -125,7 +153,7 @@ const nucleic = StructureSelectionQuery('Nucleic', MS.struct.modifier.union([
             ])
         ])
     })
-]))
+]), { category: StructureSelectionCategory.Type })
 
 const proteinOrNucleic = StructureSelectionQuery('Protein or Nucleic', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -137,7 +165,7 @@ const proteinOrNucleic = StructureSelectionQuery('Protein or Nucleic', MS.struct
             ])
         ])
     })
-]))
+]), { category: StructureSelectionCategory.Type })
 
 const helix = StructureSelectionQuery('Helix', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -153,7 +181,7 @@ const helix = StructureSelectionQuery('Helix', MS.struct.modifier.union([
             MS.core.type.bitflags([SecondaryStructureType.Flag.Helix])
         ])
     })
-]))
+]), { category: StructureSelectionCategory.Residue })
 
 const beta = StructureSelectionQuery('Beta Strand/Sheet', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -169,13 +197,13 @@ const beta = StructureSelectionQuery('Beta Strand/Sheet', MS.struct.modifier.uni
             MS.core.type.bitflags([SecondaryStructureType.Flag.Beta])
         ])
     })
-]))
+]), { category: StructureSelectionCategory.Residue })
 
 const water = StructureSelectionQuery('Water', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
         'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'water'])
     })
-]))
+]), { category: StructureSelectionCategory.Type })
 
 const branched = StructureSelectionQuery('Carbohydrate', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -190,20 +218,20 @@ const branched = StructureSelectionQuery('Carbohydrate', MS.struct.modifier.unio
             ])
         ])
     })
-]))
+]), { category: StructureSelectionCategory.Type })
 
 const branchedPlusConnected = StructureSelectionQuery('Carbohydrate with Connected', MS.struct.modifier.union([
     MS.struct.modifier.includeConnected({
         0: branched.expression, 'layer-count': 1, 'as-whole-residues': true
     })
-]))
+]), { category: StructureSelectionCategory.Internal })
 
 const branchedConnectedOnly = StructureSelectionQuery('Connected to Carbohydrate', MS.struct.modifier.union([
     MS.struct.modifier.exceptBy({
         0: branchedPlusConnected.expression,
         by: branched.expression
     })
-]))
+]), { category: StructureSelectionCategory.Internal })
 
 const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
     MS.struct.combinator.merge([
@@ -237,7 +265,7 @@ const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
             })
         ])
     ]),
-]))
+]), { category: StructureSelectionCategory.Residue })
 
 // don't include branched entities as they have their own link representation
 const ligandPlusConnected = StructureSelectionQuery('Ligand with Connected', MS.struct.modifier.union([
@@ -257,14 +285,14 @@ const ligandPlusConnected = StructureSelectionQuery('Ligand with Connected', MS.
         ]),
         by: branched.expression
     })
-]))
+]), { category: StructureSelectionCategory.Internal })
 
 const ligandConnectedOnly = StructureSelectionQuery('Connected to Ligand', MS.struct.modifier.union([
     MS.struct.modifier.exceptBy({
         0: ligandPlusConnected.expression,
         by: ligand.expression
     })
-]))
+]), { category: StructureSelectionCategory.Internal })
 
 // residues connected to ligands or branched entities
 const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydrate', MS.struct.modifier.union([
@@ -272,7 +300,7 @@ const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydra
         branchedConnectedOnly.expression,
         ligandConnectedOnly.expression
     ]),
-]))
+]), { category: StructureSelectionCategory.Internal })
 
 const disulfideBridges = StructureSelectionQuery('Disulfide Bridges', MS.struct.modifier.union([
     MS.struct.modifier.wholeResidues([
@@ -285,7 +313,7 @@ const disulfideBridges = StructureSelectionQuery('Disulfide Bridges', MS.struct.
             })
         ])
     ])
-]))
+]), { category: StructureSelectionCategory.Bond })
 
 const nonStandardPolymer = StructureSelectionQuery('Non-standard Residues in Polymers', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -293,7 +321,7 @@ const nonStandardPolymer = StructureSelectionQuery('Non-standard Residues in Pol
         'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
         'residue-test': MS.ammp('isNonStandard')
     })
-]))
+]), { category: StructureSelectionCategory.Residue })
 
 const coarse = StructureSelectionQuery('Coarse Elements', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
@@ -301,15 +329,15 @@ const coarse = StructureSelectionQuery('Coarse Elements', MS.struct.modifier.uni
             MS.set('sphere', 'gaussian'), MS.ammp('objectPrimitive')
         ])
     })
-]))
+]), { category: StructureSelectionCategory.Residue })
 
 const ring = StructureSelectionQuery('Rings in Residues', MS.struct.modifier.union([
     MS.struct.generator.rings()
-]))
+]), { category: StructureSelectionCategory.Residue })
 
 const aromaticRing = StructureSelectionQuery('Aromatic Rings in Residues', MS.struct.modifier.union([
     MS.struct.generator.rings({ 'only-aromatic': true })
-]))
+]), { category: StructureSelectionCategory.Residue })
 
 const surroundings = StructureSelectionQuery('Surrounding Residues (5 \u212B) of Selection', MS.struct.modifier.union([
     MS.struct.modifier.exceptBy({
@@ -320,20 +348,29 @@ const surroundings = StructureSelectionQuery('Surrounding Residues (5 \u212B) of
         }),
         by: MS.internal.generator.current()
     })
-]), 'Select residues within 5 \u212B of the current selection.')
+]), {
+    description: 'Select residues within 5 \u212B of the current selection.',
+    category: StructureSelectionCategory.Manipulate
+})
 
 const complement = StructureSelectionQuery('Inverse / Complement of Selection', MS.struct.modifier.union([
     MS.struct.modifier.exceptBy({
         0: MS.struct.generator.all(),
         by: MS.internal.generator.current()
     })
-]), 'Select everything not in the current selection.')
+]), {
+    description: 'Select everything not in the current selection.',
+    category: StructureSelectionCategory.Manipulate
+})
 
 const bonded = StructureSelectionQuery('Residues Bonded to Selection', MS.struct.modifier.union([
     MS.struct.modifier.includeConnected({
         0: MS.internal.generator.current(), 'layer-count': 1, 'as-whole-residues': true
     })
-]), 'Select residues covalently bonded to current selection.')
+]), {
+    description: 'Select residues covalently bonded to current selection.',
+    category: StructureSelectionCategory.Manipulate
+})
 
 const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modifier.union([
     MS.struct.modifier.wholeResidues([
@@ -344,8 +381,12 @@ const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modi
             })
         ])
     ])
-]), 'Select residues with clashes in the wwPDB validation report.', (ctx, structure) => {
-    return ValidationReportProvider.attach(ctx, structure.models[0])
+]), {
+    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([
@@ -357,8 +398,12 @@ const isBuried = StructureSelectionQuery('Buried Protein Residues', MS.struct.mo
             })
         ])
     ])
-]), 'Select buried protein residues.', (ctx, structure) => {
-    return AccessibleSurfaceAreaProvider.attach(ctx, structure)
+]), {
+    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([
@@ -370,10 +415,55 @@ const isAccessible = StructureSelectionQuery('Accessible Protein Residues', MS.s
             })
         ])
     ])
-]), 'Select accessible protein residues.', (ctx, structure) => {
-    return AccessibleSurfaceAreaProvider.attach(ctx, structure)
+]), {
+    description: 'Select accessible protein residues.',
+    category: StructureSelectionCategory.Residue,
+    ensureCustomProperties: (ctx, structure) => {
+        return AccessibleSurfaceAreaProvider.attach(ctx, structure)
+    }
 })
 
+const StandardAminoAcids = [
+    [['HIS'], 'HISTIDINE'],
+    [['ARG'], 'ARGININE'],
+    [['LYS'], 'LYSINE'],
+    [['ILE'], 'ISOLEUCINE'],
+    [['PHE'], 'PHENYLALANINE'],
+    [['LEU'], 'LEUCINE'],
+    [['TRP'], 'TRYPTOPHAN'],
+    [['ALA'], 'ALANINE'],
+    [['MET'], 'METHIONINE'],
+    [['CYS'], 'CYSTEINE'],
+    [['ASN'], 'ASPARAGINE'],
+    [['VAL'], 'VALINE'],
+    [['GLY'], 'GLYCINE'],
+    [['SER'], 'SERINE'],
+    [['GLN'], 'GLUTAMINE'],
+    [['TYR'], 'TYROSINE'],
+    [['ASP'], 'ASPARTIC ACID'],
+    [['GLU'], 'GLUTAMIC ACID'],
+    [['THR'], 'THREONINE'],
+    [['SEC'], 'SELENOCYSTEINE'],
+    [['PYL'], 'PYRROLYSINE'],
+].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][]
+
+const StandardNucleicBases = [
+    [['A', 'DA'], 'ADENOSINE'],
+    [['C', 'DC'], 'CYTIDINE'],
+    [['T', 'DT'], 'THYMIDINE'],
+    [['G', 'DG'], 'GUANOSINE'],
+    [['I', 'DI'], 'INOSINE'],
+    [['U', 'DU'], 'URIDINE'],
+].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][]
+
+function ResidueQuery([names, label]: [string[], string], category: string) {
+    return StructureSelectionQuery(`${stringToWords(label)} (${names.join(', ')})`, MS.struct.modifier.union([
+        MS.struct.generator.atomGroups({
+            'residue-test': MS.core.set.has([MS.set(...names), MS.ammp('auth_comp_id')])
+        })
+    ]), { category })
+}
+
 export const StructureSelectionQueries = {
     all,
     polymer,
@@ -406,6 +496,12 @@ export const StructureSelectionQueries = {
     isAccessible
 }
 
+export const StructureSelectionQueryList = [
+    ...Object.values(StructureSelectionQueries),
+    ...StandardAminoAcids.map(v => ResidueQuery(v, StructureSelectionCategory.AminoAcid)),
+    ...StandardNucleicBases.map(v => ResidueQuery(v, StructureSelectionCategory.NucleicBase)),
+]
+
 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 },