Ver Fonte

wip, structure tools, repr presets

Alexander Rose há 5 anos atrás
pai
commit
e1b7a5b267

+ 20 - 13
src/mol-plugin/ui/structure/representation.tsx

@@ -13,6 +13,8 @@ import { Color } from '../../../mol-util/color';
 import { ButtonSelect, Options } from '../controls/common'
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { VisualQuality, VisualQualityOptions } from '../../../mol-geo/geometry/base';
+import { StructureRepresentationPresets as P } from '../../util/structure-representation-helper';
+import { camelCaseToWords } from '../../../mol-util/string';
 
 abstract class BaseStructureRepresentationControls extends PluginUIComponent {
     onChange = (value: string) => {
@@ -49,25 +51,19 @@ abstract class BaseStructureRepresentationControls extends PluginUIComponent {
             <span title={this.label}>{this.label}</span>
             <div className='msp-select-row'>
                 <ButtonSelect label='Show' onChange={this.show}>
-                    <optgroup label='Show'>
-                        {Options(types)}
-                    </optgroup>
+                    <optgroup label='Show'>{Options(types)}</optgroup>
                 </ButtonSelect>
                 <ButtonSelect label='Hide' onChange={this.hide}>
                     <optgroup label='Clear'>
                         <option key={'__all__'} value={'__all__'}>All</option>
                     </optgroup>
-                    <optgroup label='Hide'>
-                        {Options(types)}
-                    </optgroup>
+                    <optgroup label='Hide'>{Options(types)}</optgroup>
                 </ButtonSelect>
                 <ButtonSelect label='Color' onChange={this.color}>
                     <optgroup label='Clear'>
                         <option key={-1} value={-1}>Theme</option>
                     </optgroup>
-                    <optgroup label='Color'>
-                        {ColorOptions()}
-                    </optgroup>
+                    <optgroup label='Color'>{ColorOptions()}</optgroup>
                 </ButtonSelect>
             </div>
         </div>
@@ -90,8 +86,11 @@ class SelectionStructureRepresentationControls extends BaseStructureRepresentati
 }
 
 export class StructureRepresentationControls extends PluginUIComponent {
-    preset = async () => {
-        await this.plugin.helpers.structureRepresentation.preset()
+    preset = async (value: string) => {
+        const presetFn = P[value as keyof typeof P]
+        if (presetFn) {
+            await presetFn(this.plugin.helpers.structureRepresentation)
+        }
     }
 
     onChange = async (p: { param: PD.Base<any>, name: string, value: any }) => {
@@ -123,12 +122,20 @@ export class StructureRepresentationControls extends PluginUIComponent {
     }
 
     render() {
+        const presets = Object.keys(P).map(name => {
+            return [name, camelCaseToWords(name)] as [string, string]
+        })
+
         return <div className='msp-transform-wrapper'>
             <div className='msp-transform-header'>
                 <button className='msp-btn msp-btn-block'>Representation</button>
             </div>
-            <div className='msp-btn-row-group'>
-                <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.preset()}>Preset</button>
+            <div className='msp-control-row'>
+                <div className='msp-select-row'>
+                    <ButtonSelect label='Preset' onChange={this.preset}>
+                        <optgroup label='Preset'>{Options(presets)}</optgroup>
+                    </ButtonSelect>
+                </div>
             </div>
             <EverythingStructureRepresentationControls />
             <SelectionStructureRepresentationControls />

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

@@ -15,8 +15,6 @@ import { Interactivity } from '../../util/interactivity';
 import { ParameterControls } from '../controls/parameters';
 import { camelCaseToWords } from '../../../mol-util/string';
 
-type SelectionModifier = 'add' | 'remove' | 'only'
-
 const StructureSelectionParams = {
     granularity: Interactivity.Params.granularity,
 }

+ 69 - 12
src/mol-plugin/util/structure-representation-helper.ts

@@ -49,7 +49,7 @@ export class StructureRepresentationHelper {
         return selections.length > 0 ? selections[0] : undefined
     }
 
-    private async _set(modifier: SelectionModifier, type: string, loci: StructureElement.Loci, structure: StructureTransform) {
+    private async _set(modifier: SelectionModifier, type: string, loci: StructureElement.Loci, structure: StructureTransform, props = {}) {
         const state = this.plugin.state.dataState
         const update = state.build()
         const s = structure.obj!.data
@@ -69,6 +69,7 @@ export class StructureRepresentationHelper {
             const params = StructureRepresentation3DHelpers.getDefaultParams(this.plugin, type as any, s)
 
             const p = params.type.params
+            Object.assign(p, props)
             if (p.ignoreHydrogens !== undefined) p.ignoreHydrogens = this._ignoreHydrogens
             if (p.quality !== undefined) p.quality = this._quality
 
@@ -84,23 +85,23 @@ export class StructureRepresentationHelper {
         await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
     }
 
-    async set(modifier: SelectionModifier, type: string, lociGetter: (structure: Structure) => StructureElement.Loci) {
+    async set(modifier: SelectionModifier, type: string, lociGetter: (structure: Structure) => StructureElement.Loci, props = {}) {
         const state = this.plugin.state.dataState;
         const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
 
         for (const structure of structures) {
             const s = structure.obj!.data
             const loci = lociGetter(s)
-            await this._set(modifier, type, loci, structure)
+            await this._set(modifier, type, loci, structure, props)
         }
     }
 
-    async setFromExpression(modifier: SelectionModifier, type: string, expression: Expression) {
+    async setFromExpression(modifier: SelectionModifier, type: string, expression: Expression, props = {}) {
         return this.set(modifier, type, (structure) => {
             const compiled = compile<StructureSelection>(expression)
             const result = compiled(new QueryContext(structure))
             return StructureSelection.toLoci2(result)
-        })
+        }, props)
     }
 
     async clear() {
@@ -169,16 +170,72 @@ export class StructureRepresentationHelper {
     }
 
     async preset() {
-        // TODO generalize and make configurable
-        await this.clear()
-        await this.setFromExpression('add', 'cartoon', Q.all)
-        await this.setFromExpression('add', 'carbohydrate', Q.all)
-        await this.setFromExpression('add', 'ball-and-stick', MS.struct.modifier.union([
-            MS.struct.combinator.merge([ Q.ligandsPlusConnected, Q.branchedConnectedOnly, Q.water ])
-        ]))
+        // TODO option to limit to specific structure
+        const state = this.plugin.state.dataState;
+        const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
+
+        if (structures.length === 0) return
+        const s = structures[0].obj!.data
+
+        if (s.elementCount < 50000) {
+            polymerAndLigand(this)
+        } else if (s.elementCount < 200000) {
+            proteinAndNucleic(this)
+        } else {
+            if (s.unitSymmetryGroups[0].units.length > 10) {
+                capsid(this)
+            } else {
+                coarseCapsid(this)
+            }
+        }
     }
 
     constructor(private plugin: PluginContext) {
 
     }
+}
+
+//
+
+async function polymerAndLigand(r: StructureRepresentationHelper) {
+    await r.clear()
+    await r.setFromExpression('add', 'cartoon', Q.all)
+    await r.setFromExpression('add', 'carbohydrate', Q.all)
+    await r.setFromExpression('add', 'ball-and-stick', MS.struct.modifier.union([
+        MS.struct.combinator.merge([ Q.ligandPlusConnected, Q.branchedConnectedOnly, Q.water ])
+    ]))
+}
+
+async function proteinAndNucleic(r: StructureRepresentationHelper) {
+    await r.clear()
+    await r.setFromExpression('add', 'cartoon', Q.protein)
+    await r.setFromExpression('add', 'gaussian-surface', Q.nucleic)
+
+    await r.setFromExpression('add', 'carbohydrate', Q.all)
+    await r.setFromExpression('add', 'ball-and-stick', MS.struct.modifier.union([
+        MS.struct.combinator.merge([ Q.ligandPlusConnected, Q.branchedConnectedOnly, Q.water ])
+    ]))
+}
+
+async function capsid(r: StructureRepresentationHelper) {
+    await r.clear()
+    await r.setFromExpression('add', 'gaussian-surface', Q.polymer, {
+        smoothness: 0.5,
+    })
+}
+
+async function coarseCapsid(r: StructureRepresentationHelper) {
+    await r.clear()
+    await r.setFromExpression('add', 'gaussian-surface', Q.trace, {
+        radiusOffset: 1,
+        smoothness: 0.5,
+        visuals: ['structure-gaussian-surface-mesh']
+    })
+}
+
+export const StructureRepresentationPresets = {
+    polymerAndLigand,
+    proteinAndNucleic,
+    capsid,
+    coarseCapsid
 }

+ 62 - 25
src/mol-plugin/util/structure-selection-helper.ts

@@ -15,17 +15,52 @@ import Expression from '../../mol-script/language/expression';
 
 const all = MS.struct.generator.all()
 
-const polymers = MS.struct.modifier.union([
+const polymer = MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
         'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
     })
 ])
 
-const backboneTrace = MS.struct.modifier.union([
+const trace = MS.struct.modifier.union([
+    MS.struct.combinator.merge([
+        MS.struct.modifier.union([
+            MS.struct.generator.atomGroups({
+                'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
+                'chain-test': MS.core.set.has([
+                    MS.set('sphere', 'gaussian'), MS.ammp('objectPrimitive')
+                ])
+            })
+        ]),
+        MS.struct.modifier.union([
+            MS.struct.generator.atomGroups({
+                'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
+                'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
+                'atom-test': MS.core.set.has([MS.set('CA', 'P'), MS.ammp('label_atom_id')])
+            })
+        ])
+    ])
+])
+
+const protein = MS.struct.modifier.union([
+    MS.struct.generator.atomGroups({
+        'entity-test': MS.core.logic.and([
+            MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
+            MS.core.str.match([
+                MS.re('(polypeptide|cyclic-pseudo-peptide)', 'i'),
+                MS.ammp('entitySubtype')
+            ])
+        ])
+    })
+])
+
+const nucleic = MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
-        'atom-test': MS.core.logic.or([
-            MS.core.rel.eq([MS.ammp('label_atom_id'), 'CA']),
-            MS.core.rel.eq([MS.ammp('label_atom_id'), 'P'])
+        'entity-test': MS.core.logic.and([
+            MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
+            MS.core.str.match([
+                MS.re('(nucleotide|peptide nucleic acid)', 'i'),
+                MS.ammp('entitySubtype')
+            ])
         ])
     })
 ])
@@ -37,19 +72,18 @@ const water = MS.struct.modifier.union([
 ])
 
 const branched = MS.struct.modifier.union([
-    MS.struct.combinator.merge([
-        MS.struct.modifier.union([
-            MS.struct.generator.atomGroups({
-                'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'branched'])
-            })
-        ]),
-        MS.struct.modifier.union([
-            MS.struct.generator.atomGroups({
-                'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'non-polymer']),
-                'residue-test': MS.core.str.match([MS.re('saccharide', 'i'), MS.ammp('chemCompType')])
-            })
+    MS.struct.generator.atomGroups({
+        'entity-test': MS.core.logic.or([
+            MS.core.rel.eq([MS.ammp('entityType'), 'branched']),
+            MS.core.logic.and([
+                MS.core.rel.eq([MS.ammp('entityType'), 'non-polymer']),
+                MS.core.str.match([
+                    MS.re('oligosaccharide', 'i'),
+                    MS.ammp('entitySubtype')
+                ])
+            ])
         ])
-    ])
+    })
 ])
 
 const branchedPlusConnected = MS.struct.modifier.union([
@@ -65,21 +99,22 @@ const branchedConnectedOnly = MS.struct.modifier.union([
     })
 ])
 
-const ligands = MS.struct.modifier.union([
+const ligand = MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
         'entity-test': MS.core.logic.and([
             MS.core.rel.neq([MS.ammp('entityType'), 'branched']),
             MS.core.rel.eq([MS.ammp('entityType'), 'non-polymer'])
         ]),
+        'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
         'residue-test': MS.core.logic.not([
             MS.core.str.match([MS.re('saccharide', 'i'), MS.ammp('chemCompType')])
         ])
     })
 ])
 
-const ligandsPlusConnected = MS.struct.modifier.union([
+const ligandPlusConnected = MS.struct.modifier.union([
     MS.struct.modifier.includeConnected({
-        0: ligands, 'layer-count': 1, 'as-whole-residues': true
+        0: ligand, 'layer-count': 1, 'as-whole-residues': true
     })
 ])
 
@@ -93,20 +128,22 @@ const coarse = MS.struct.modifier.union([
 
 export const StructureSelectionQueries = {
     all,
-    polymers,
-    backboneTrace,
+    polymer,
+    trace,
+    protein,
+    nucleic,
     water,
     branched,
     branchedPlusConnected,
     branchedConnectedOnly,
-    ligands,
-    ligandsPlusConnected,
+    ligand,
+    ligandPlusConnected,
     coarse,
 }
 
 //
 
-type SelectionModifier = 'add' | 'remove' | 'only'
+export type SelectionModifier = 'add' | 'remove' | 'only'
 
 export class StructureSelectionHelper {
     private get structures() {