Browse Source

added more structure selection queries

- whole residues
- non-standard residues from current structures
- elements from current structures
Alexander Rose 5 years ago
parent
commit
3d1366024d

+ 9 - 0
src/mol-model/structure/model/properties/atomic/types.ts

@@ -14,6 +14,15 @@ export const enum Elements {
     H = 'H', D = 'D', T = 'T', HE = 'HE', LI = 'LI', BE = 'BE', B = 'B', C = 'C', N = 'N', O = 'O', F = 'F', NE = 'NE', NA = 'NA', MG = 'MG', AL = 'AL', SI = 'SI', P = 'P', S = 'S', CL = 'CL', AR = 'AR', K = 'K', CA = 'CA', SC = 'SC', TI = 'TI', V = 'V', CR = 'CR', MN = 'MN', FE = 'FE', CO = 'CO', NI = 'NI', CU = 'CU', ZN = 'ZN', GA = 'GA', GE = 'GE', AS = 'AS', SE = 'SE', BR = 'BR', KR = 'KR', RB = 'RB', SR = 'SR', Y = 'Y', ZR = 'ZR', NB = 'NB', MO = 'MO', TC = 'TC', RU = 'RU', RH = 'RH', PD = 'PD', AG = 'AG', CD = 'CD', IN = 'IN', SN = 'SN', SB = 'SB', TE = 'TE', I = 'I', XE = 'XE', CS = 'CS', BA = 'BA', LA = 'LA', CE = 'CE', PR = 'PR', ND = 'ND', PM = 'PM', SM = 'SM', EU = 'EU', GD = 'GD', TB = 'TB', DY = 'DY', HO = 'HO', ER = 'ER', TM = 'TM', YB = 'YB', LU = 'LU', HF = 'HF', TA = 'TA', W = 'W', RE = 'RE', OS = 'OS', IR = 'IR', PT = 'PT', AU = 'AU', HG = 'HG', TL = 'TL', PB = 'PB', BI = 'BI', PO = 'PO', AT = 'AT', RN = 'RN', FR = 'FR', RA = 'RA', AC = 'AC', TH = 'TH', PA = 'PA', U = 'U', NP = 'NP', PU = 'PU', AM = 'AM', CM = 'CM', BK = 'BK', CF = 'CF', ES = 'ES', FM = 'FM', MD = 'MD', NO = 'NO', LR = 'LR', RF = 'RF', DB = 'DB', SG = 'SG', BH = 'BH', HS = 'HS', MT = 'MT', DS = 'DS', RG = 'RG', CN = 'CN', NH = 'NH', FL = 'FL', MC = 'MC', LV = 'LV', TS = 'TS', OG = 'OG'
 }
 
+export const ElementNames: { [k: string]: string } = {
+    'H': 'Hydrogen',
+    'C': 'Carbon',
+    'N': 'Nitrogen',
+    'O': 'Oxygen',
+    'S': 'Sulfur',
+    'FE': 'Iron',
+};
+
 export const AlkaliMetals = new Set<ElementSymbol>(['LI', 'NA', 'K', 'RB', 'CS', 'FR'] as ElementSymbol[]);
 export function isAlkaliMetal(element: ElementSymbol) { return AlkaliMetals.has(element); }
 

+ 28 - 6
src/mol-plugin-state/helpers/structure-selection-query.ts

@@ -229,14 +229,14 @@ const branchedPlusConnected = StructureSelectionQuery('Carbohydrate with Connect
     MS.struct.modifier.includeConnected({
         0: branched.expression, 'layer-count': 1, 'as-whole-residues': true
     })
-]), { category: StructureSelectionCategory.Internal });
+]), { category: StructureSelectionCategory.Internal, isHidden: true });
 
 const branchedConnectedOnly = StructureSelectionQuery('Connected to Carbohydrate', MS.struct.modifier.union([
     MS.struct.modifier.exceptBy({
         0: branchedPlusConnected.expression,
         by: branched.expression
     })
-]), { category: StructureSelectionCategory.Internal });
+]), { category: StructureSelectionCategory.Internal, isHidden: true });
 
 const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
     MS.struct.combinator.merge([
@@ -290,14 +290,14 @@ const ligandPlusConnected = StructureSelectionQuery('Ligand with Connected', MS.
         ]),
         by: branched.expression
     })
-]), { category: StructureSelectionCategory.Internal });
+]), { category: StructureSelectionCategory.Internal, isHidden: true });
 
 const ligandConnectedOnly = StructureSelectionQuery('Connected to Ligand', MS.struct.modifier.union([
     MS.struct.modifier.exceptBy({
         0: ligandPlusConnected.expression,
         by: ligand.expression
     })
-]), { category: StructureSelectionCategory.Internal });
+]), { category: StructureSelectionCategory.Internal, isHidden: true });
 
 // residues connected to ligands or branched entities
 const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydrate', MS.struct.modifier.union([
@@ -305,7 +305,7 @@ const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydra
         branchedConnectedOnly.expression,
         ligandConnectedOnly.expression
     ]),
-]), { category: StructureSelectionCategory.Internal });
+]), { category: StructureSelectionCategory.Internal, isHidden: true });
 
 const disulfideBridges = StructureSelectionQuery('Disulfide Bridges', MS.struct.modifier.union([
     MS.struct.modifier.wholeResidues([
@@ -380,6 +380,16 @@ const bonded = StructureSelectionQuery('Residues Bonded to Selection', MS.struct
     referencesCurrent: true
 });
 
+const wholeResidues = StructureSelectionQuery('Whole Residues of Selection', MS.struct.modifier.union([
+    MS.struct.modifier.wholeResidues({
+        0: MS.internal.generator.current()
+    })
+]), {
+    description: 'Expand current selection to whole residues.',
+    category: StructureSelectionCategory.Manipulate,
+    referencesCurrent: true
+});
+
 const StandardAminoAcids = [
     [['HIS'], 'HISTIDINE'],
     [['ARG'], 'ARGININE'],
@@ -390,6 +400,7 @@ const StandardAminoAcids = [
     [['TRP'], 'TRYPTOPHAN'],
     [['ALA'], 'ALANINE'],
     [['MET'], 'METHIONINE'],
+    [['PRO'], 'PROLINE'],
     [['CYS'], 'CYSTEINE'],
     [['ASN'], 'ASPARAGINE'],
     [['VAL'], 'VALINE'],
@@ -402,6 +413,7 @@ const StandardAminoAcids = [
     [['THR'], 'THREONINE'],
     [['SEC'], 'SELENOCYSTEINE'],
     [['PYL'], 'PYRROLYSINE'],
+    [['UNK'], 'UNKNOWN'],
 ].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
 
 const StandardNucleicBases = [
@@ -411,9 +423,10 @@ const StandardNucleicBases = [
     [['G', 'DG'], 'GUANOSINE'],
     [['I', 'DI'], 'INOSINE'],
     [['U', 'DU'], 'URIDINE'],
+    [['N', 'DN'], 'UNKNOWN'],
 ].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) {
+export 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')])
@@ -421,6 +434,14 @@ function ResidueQuery([names, label]: [string[], string], category: string) {
     ]), { category });
 }
 
+export function ElementSymbolQuery([names, label]: [string[], string], category: string) {
+    return StructureSelectionQuery(`${stringToWords(label)} (${names.join(', ')})`, MS.struct.modifier.union([
+        MS.struct.generator.atomGroups({
+            'atom-test': MS.core.set.has([MS.set(...names), MS.acp('elementSymbol')])
+        })
+    ]), { category });
+}
+
 export const StructureSelectionQueries = {
     all,
     current,
@@ -447,6 +468,7 @@ export const StructureSelectionQueries = {
     surroundings,
     complement,
     bonded,
+    wholeResidues,
 };
 
 export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {

+ 61 - 5
src/mol-plugin-ui/structure/selection.tsx

@@ -11,7 +11,7 @@ import Brush from '@material-ui/icons/Brush';
 import Restore from '@material-ui/icons/Restore';
 import Remove from '@material-ui/icons/Remove';
 import * as React from 'react';
-import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
+import { StructureSelectionQueries, StructureSelectionQuery, ResidueQuery, ElementSymbolQuery } 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, StructureComponentRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
@@ -25,6 +25,9 @@ import { Button, ControlGroup, IconButton, ToggleButton } from '../controls/comm
 import { ParameterControls, ParamOnChange, PureSelectControl } from '../controls/parameters';
 import { Union, Subtract, Intersect, SetSvg as SetSvg, CubeSvg } from '../controls/icons';
 import { AddComponentControls } from './components';
+import { SetUtils } from '../../mol-util/set';
+import { AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol } from '../../mol-model/structure/model/types';
+import { ElementNames } from '../../mol-model/structure/model/properties/atomic/types';
 
 const StructureSelectionParams = {
     granularity: InteractivityManager.Params.granularity,
@@ -45,6 +48,10 @@ const ActionHeader = new Map<StructureSelectionModifier, string>([
     ['set', 'Set Selection']
 ] as const);
 
+const StandardResidues = SetUtils.unionMany(
+    AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames
+);
+
 export class StructureSelectionActionsControls extends PluginUIComponent<{}, StructureSelectionActionsControlsState> {
     state = {
         action: void 0 as StructureSelectionActionsControlsState['action'],
@@ -60,6 +67,9 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
             if (this.state.isEmpty !== isEmpty) {
                 this.setState({ isEmpty });
             }
+            // trigger elementQueries and nonStandardResidueQueries recalculation
+            this.queriesVersion = -1;
+            this.forceUpdate();
         });
 
         this.subscribe(this.plugin.behaviors.state.isBusy, v => {
@@ -83,15 +93,60 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
         this.plugin.managers.structure.selection.fromSelectionQuery(modifier, selectionQuery, false);
     }
 
-    selectQuery: ActionMenu.OnSelect = item => {
+    selectQuery: ActionMenu.OnSelect = (item, e) => {
         if (!item || !this.state.action) {
             this.setState({ action: void 0 });
             return;
         }
         const q = this.state.action! as StructureSelectionModifier;
-        this.setState({ action: void 0 }, () => {
+        if (e?.shiftKey) {
             this.set(q, item.value as StructureSelectionQuery);
+        } else {
+            this.setState({ action: void 0 }, () => {
+                this.set(q, item.value as StructureSelectionQuery);
+            });
+        }
+    }
+
+    get elementQueries () {
+        const uniqueElements = new Set<ElementSymbol>();
+        for (const s of this.plugin.managers.structure.hierarchy.selection.structures) {
+            const structure = s.cell.obj?.data;
+            if (!structure) continue;
+
+            structure.uniqueElementSymbols.forEach(e => uniqueElements.add(e));
+        }
+
+        const queries: StructureSelectionQuery[] = [];
+        uniqueElements.forEach(e => {
+            const label = ElementNames[e] || e;
+            queries.push(ElementSymbolQuery([[e], label], 'Element Symbol'));
+        });
+        return queries;
+    }
+
+    get nonStandardResidueQueries () {
+        const residueLabels = new Map<string, string>();
+        const uniqueResidues = new Set<string>();
+        for (const s of this.plugin.managers.structure.hierarchy.selection.structures) {
+            const structure = s.cell.obj?.data;
+            if (!structure) continue;
+
+            structure.uniqueResidueNames.forEach(r => uniqueResidues.add(r));
+            for (const m of structure.models) {
+                structure.uniqueResidueNames.forEach(r => {
+                    const comp = m.properties.chemicalComponentMap.get(r);
+                    if (comp) residueLabels.set(r, comp.name);
+                });
+            }
+        }
+
+        const queries: StructureSelectionQuery[] = [];
+        SetUtils.difference(uniqueResidues, StandardResidues).forEach(r => {
+            const label = residueLabels.get(r) || r;
+            queries.push(ResidueQuery([[r], label], 'Ligand/Non-standard Residue'));
         });
+        return queries;
     }
 
     private queriesItems: ActionMenu.Items[] = []
@@ -99,8 +154,9 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
     get queries () {
         const { registry } = this.plugin.query.structure;
         if (registry.version !== this.queriesVersion) {
-            this.queriesItems = ActionMenu.createItems(registry.list, {
-                filter: q => q !== StructureSelectionQueries.current,
+            const queries = [...registry.list, ...this.nonStandardResidueQueries, ...this.elementQueries];
+            this.queriesItems = ActionMenu.createItems(queries, {
+                filter: q => q !== StructureSelectionQueries.current && !q.isHidden,
                 label: q => q.label,
                 category: q => q.category,
                 description: q => q.description