Browse Source

plugin-state: preset improvements

Alexander Rose 5 years ago
parent
commit
8b3df4b373

+ 56 - 0
src/mol-model/structure/structure/structure.ts

@@ -991,6 +991,62 @@ namespace Structure {
             }
         }
     }
+
+    //
+
+    const DefaultSizeThresholds = {
+        smallResidueCount: 10,
+        mediumResidueCount: 1500,
+        largeResidueCount: 12000,
+        highSymmetryUnitCount: 10,
+        fiberResidueCount: 15
+    }
+    type SizeThresholds = typeof DefaultSizeThresholds
+
+    function getPolymerSymmetryGroups(structure: Structure) {
+        return structure.unitSymmetryGroups.filter(ug => ug.units[0].polymerElements.length > 0)
+    }
+
+    /**
+     * Try to match fiber-like structures like 6nk4
+     */
+    function isFiberLike(structure: Structure, thresholds: SizeThresholds) {
+        const polymerSymmetryGroups = getPolymerSymmetryGroups(structure)
+        return (
+            polymerSymmetryGroups.length === 1 &&
+            polymerSymmetryGroups[0].units.length > 2 &&
+            polymerSymmetryGroups[0].units[0].polymerElements.length < thresholds.fiberResidueCount
+        )
+    }
+
+    function hasHighSymmetry(structure: Structure, thresholds: SizeThresholds) {
+        const polymerSymmetryGroups = getPolymerSymmetryGroups(structure)
+        return (
+            polymerSymmetryGroups.length > 1 &&
+            polymerSymmetryGroups[0].units.length > thresholds.highSymmetryUnitCount
+        )
+    }
+
+    export enum Size { Small, Medium, Large, Huge, Gigantic }
+
+    export function getSize(structure: Structure, thresholds: Partial<SizeThresholds> = {}): Size {
+        const t = { ...DefaultSizeThresholds, thresholds }
+        if (structure.polymerResidueCount >= t.largeResidueCount) {
+            if (hasHighSymmetry(structure, t)) {
+                return Size.Huge
+            } else {
+                return Size.Gigantic
+            }
+        } else if (isFiberLike(structure, t)) {
+            return Size.Small
+        } else if (structure.polymerResidueCount < t.smallResidueCount) {
+            return Size.Small
+        } else if (structure.polymerResidueCount < t.mediumResidueCount) {
+            return Size.Medium
+        } else {
+            return Size.Large
+        }
+    }
 }
 
 export default Structure

+ 2 - 2
src/mol-plugin-state/builder/representation.ts

@@ -93,7 +93,7 @@ export class RepresentationBuilder {
     removePreset(providerRef: RepresentationProviderRef, structureRoot?: StateObjectRef) {
         const id = this.resolveProvider(providerRef)?.id;
         if (!id) return;
-        
+
         const state = this.plugin.state.dataState;
         const root = StateObjectRef.resolveRef(state, structureRoot) || StateTransform.RootRef;
         const reprs = StateSelection.findWithAllTags(state.tree, root, new Set([id, RepresentationProviderTags.Representation]));
@@ -113,7 +113,7 @@ export class RepresentationBuilder {
         if (builder.editInfo.count === 0) return;
         return this.plugin.runTask(state.updateTree(builder));
     }
-    
+
     structurePreset<K extends keyof PresetStructureReprentations>(parent: StateObjectRef, preset: K, params?: StructureRepresentationProvider.Params<PresetStructureReprentations[K]>): Promise<StructureRepresentationProvider.State<PresetStructureReprentations[K]>> | undefined
     structurePreset<P = any, S = {}>(parent: StateObjectRef, providers: StructureRepresentationProvider<P, S>, params?: P): Promise<S> | undefined
     structurePreset(parent: StateObjectRef, providerId: string, params?: any): Promise<any> | undefined

+ 88 - 53
src/mol-plugin-state/builder/structure/preset.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { StateTransforms } from '../../transforms';
@@ -13,49 +14,49 @@ import { StateObjectRef } from '../../../mol-state';
 import { PluginStateObject } from '../../objects';
 import { StaticStructureComponentType } from '../../helpers/structure-component';
 import { PluginContext } from '../../../mol-plugin/context';
+import { Structure } from '../../../mol-model/structure';
 
 const auto = StructureRepresentationProvider({
     id: 'preset-structure-representation-auto',
-    display: { name: 'Automaic', group: 'Preset' },
+    display: { name: 'Automatic', group: 'Preset' },
     apply(ctx, state, structureCell, _, plugin) {
-        const s = structureCell.obj!.data;
-
-        // TODO: a way to improve this?
-
-        if (s.elementCount < 50000) {
-            return defaultPreset.apply(ctx, state, structureCell, void 0, plugin);
-        } else if (s.elementCount < 200000) {
-            return proteinAndNucleic.apply(ctx, state, structureCell, void 0, plugin);
-        } else {
-            if (s.unitSymmetryGroups[0].units.length > 10) {
-                return capsid.apply(ctx, state, structureCell, void 0, plugin);
-            } else {
-                return coarseCapsid.apply(ctx, state, structureCell, void 0, plugin);
-            }
+        const structure = structureCell.obj!.data;
+        const size = Structure.getSize(structure)
+
+        switch (size) {
+            case Structure.Size.Gigantic:
+            case Structure.Size.Huge:
+                return coarseSurface.apply(ctx, state, structureCell, void 0, plugin);
+            case Structure.Size.Large:
+                return polymerCartoon.apply(ctx, state, structureCell, void 0, plugin);
+            case Structure.Size.Medium:
+                return polymerAndLigand.apply(ctx, state, structureCell, void 0, plugin);
+            case Structure.Size.Small:
+                return atomicDetail.apply(ctx, state, structureCell, void 0, plugin);
         }
     }
 });
 
-const defaultPreset = StructureRepresentationProvider({
-    id: 'preset-structure-representation-default',
-    display: { name: 'Default', group: 'Preset' },
+const polymerAndLigand = StructureRepresentationProvider({
+    id: 'preset-structure-representation-polymer-and-ligand',
+    display: { name: 'Polymer & Ligand', group: 'Preset' },
     async apply(ctx, state, structureCell, _, plugin) {
         const structure = structureCell.obj?.data!;
         const reprTags = [this.id, RepresentationProviderTags.Representation];
-        
+
         const components = {
-            proteinOrNucleic: await staticComponent(plugin, structureCell, 'protein-or-nucleic'),
+            polymer: await staticComponent(plugin, structureCell, 'polymer'),
             ligand: await staticComponent(plugin, structureCell, 'ligand'),
             nonStandard: await staticComponent(plugin, structureCell, 'non-standard'),
             branched: await staticComponent(plugin, structureCell, 'branched'),
             water: await staticComponent(plugin, structureCell, 'water'),
             coarse: await staticComponent(plugin, structureCell, 'coarse')
         };
-                
+
         const builder = state.build();
         const representations = {
-            proteinOrNucleic: components.proteinOrNucleic && builder
-                .to(components.proteinOrNucleic)
+            polymer: components.polymer && builder
+                .to(components.polymer)
                 .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, StructureRepresentation3DHelpers.getDefaultParams(plugin, 'cartoon', structure)).selector,
             ligand: components.ligand && builder
                 .to(components.ligand)
@@ -74,7 +75,7 @@ const defaultPreset = StructureRepresentationProvider({
                 .to(components.coarse)
                 .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, StructureRepresentation3DHelpers.getDefaultParamsWithTheme(plugin, 'spacefill', 'polymer-id', structure, {}))
         };
-        
+
         await state.updateTree(builder, { revertOnError: false }).runInContext(ctx);
         return { components, representations };
     }
@@ -101,33 +102,75 @@ const proteinAndNucleic = StructureRepresentationProvider({
                 .to(components.nucleic)
                 .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, StructureRepresentation3DHelpers.getDefaultParams(plugin, 'gaussian-surface', structure)).selector,
         };
-      
+
         await state.updateTree(builder, { revertOnError: true }).runInContext(ctx);
         return { components, representations };
     }
 });
 
-const capsid = StructureRepresentationProvider({
-    id: 'preset-structure-representation-capsid',
-    display: { name: 'Capsid', group: 'Preset' },
+const coarseSurface = StructureRepresentationProvider({
+    id: 'preset-structure-representation-coarse-surface',
+    display: { name: 'Coarse Surface', group: 'Preset' },
     async apply(ctx, state, structureCell, _, plugin) {
         const structure = structureCell.obj!.data;
+        const size = Structure.getSize(structure)
+
+        const gaussianProps = Object.create(null);
+        const components = Object.create(null);
+
+        if (size === Structure.Size.Gigantic) {
+            Object.assign(gaussianProps, {
+                radiusOffset: 1,
+                smoothness: 0.5,
+                visuals: ['structure-gaussian-surface-mesh']
+            })
+            components.trace = await selectionComponent(plugin, structureCell, 'trace')
+        } else if(size === Structure.Size.Huge) {
+            Object.assign(gaussianProps, {
+                smoothness: 0.5,
+            })
+            components.trace = await selectionComponent(plugin, structureCell, 'polymer')
+        } else {
+            components.trace = await selectionComponent(plugin, structureCell, 'polymer')
+        }
 
         const params = StructureRepresentation3DHelpers.createParams(plugin, structure, {
-            repr: [BuiltInStructureRepresentations['gaussian-surface'], () => ({ smoothness: 1 })]
+            repr: [
+                BuiltInStructureRepresentations['gaussian-surface'],
+                () => gaussianProps
+            ]
         });
 
         const reprTags = [this.id, RepresentationProviderTags.Representation];
 
+        const builder = state.build();
+        const representations = {
+            trace: components.trace && builder
+                .to(components.trace)
+                .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, params).selector,
+        };
+
+        await state.updateTree(builder, { revertOnError: true }).runInContext(ctx);
+        return { components, representations };
+    }
+});
+
+const polymerCartoon = StructureRepresentationProvider({
+    id: 'preset-structure-representation-polymer-cartoon',
+    display: { name: 'Polymer Cartoon', group: 'Preset' },
+    async apply(ctx, state, structureCell, _, plugin) {
+        const structure = structureCell.obj!.data;
+        const reprTags = [this.id, RepresentationProviderTags.Representation];
+
         const components = {
-            polymer: await selectionComponent(plugin, structureCell, 'polymer')
+            polymer: await selectionComponent(plugin, structureCell, 'polymer'),
         };
 
         const builder = state.build();
         const representations = {
             polymer: components.polymer && builder
                 .to(components.polymer)
-                .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, params).selector,
+                .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, StructureRepresentation3DHelpers.getDefaultParams(plugin, 'cartoon', structure)).selector,
         };
 
         await state.updateTree(builder, { revertOnError: true }).runInContext(ctx);
@@ -135,40 +178,31 @@ const capsid = StructureRepresentationProvider({
     }
 });
 
-const coarseCapsid = StructureRepresentationProvider({
-    id: 'preset-structure-representation-coarse-capsid',
-    display: { name: 'Coarse Capsid', group: 'Preset' },
+const atomicDetail = StructureRepresentationProvider({
+    id: 'preset-structure-representation-atomic-detail',
+    display: { name: 'Atomic Detail', group: 'Preset' },
     async apply(ctx, state, structureCell, _, plugin) {
         const structure = structureCell.obj!.data;
-
-        const params = StructureRepresentation3DHelpers.createParams(plugin, structure, {
-            repr: [
-                BuiltInStructureRepresentations['gaussian-surface'],
-                () => ({ smoothness: 0.5, radiusOffset: 1, /* visuals: ['gaussian-surface-mesh']*/ })
-            ]
-        });
-
         const reprTags = [this.id, RepresentationProviderTags.Representation];
 
         const components = {
-            trace: await selectionComponent(plugin, structureCell, 'trace')
+            all: await selectionComponent(plugin, structureCell, 'all'),
         };
 
         const builder = state.build();
         const representations = {
-            trace: components.trace && builder
-                .to(components.trace)
-                .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, params).selector,
+            all: components.all && builder
+                .to(components.all)
+                .applyOrUpdateTagged(reprTags, StateTransforms.Representation.StructureRepresentation3D, StructureRepresentation3DHelpers.getDefaultParams(plugin, 'ball-and-stick', structure)).selector,
         };
-        
-        await state.updateTree(builder, { revertOnError: true }).runInContext(ctx);
 
+        await state.updateTree(builder, { revertOnError: true }).runInContext(ctx);
         return { components, representations };
     }
 });
 
 function staticComponent(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, type: StaticStructureComponentType) {
-    return plugin.builders.structure.tryCreateComponent(structure, { 
+    return plugin.builders.structure.tryCreateComponent(structure, {
         type: { name: 'static', params: type },
         nullIfEmpty: true,
         label: ''
@@ -176,7 +210,7 @@ function staticComponent(plugin: PluginContext, structure: StateObjectRef<Plugin
 }
 
 function selectionComponent(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, query: keyof typeof Q) {
-    return plugin.builders.structure.tryCreateComponent(structure, { 
+    return plugin.builders.structure.tryCreateComponent(structure, {
         type: { name: 'expression', params: Q[query].expression },
         nullIfEmpty: true,
         label: Q[query].label
@@ -185,9 +219,10 @@ function selectionComponent(plugin: PluginContext, structure: StateObjectRef<Plu
 
 export const PresetStructureReprentations = {
     auto,
-    default: defaultPreset,
+    atomicDetail,
+    polymerCartoon,
+    polymerAndLigand,
     proteinAndNucleic,
-    capsid,
-    coarseCapsid
+    coarseSurface
 };
 export type PresetStructureReprentations = typeof PresetStructureReprentations;

+ 10 - 10
src/mol-plugin-state/helpers/structure-component.ts

@@ -17,7 +17,7 @@ import { Script } from '../../mol-script/script';
 export const StaticStructureComponentTypes = [
     'all',
 
-    'protein-or-nucleic',
+    'polymer',
 
     'protein',
     'nucleic',
@@ -34,7 +34,7 @@ export type StaticStructureComponentType = (typeof StaticStructureComponentTypes
 
 export const StructureComponentParams = {
     type: PD.MappedStatic('static', {
-        static: PD.Text<StaticStructureComponentType>('protein-or-nucleic'),
+        static: PD.Text<StaticStructureComponentType>('polymer'),
         expression: PD.Value<Expression>(MolScriptBuilder.struct.generator.all),
         bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
         script: PD.Script({ language: 'mol-script', expression: '(sel.atom.all)' }),
@@ -53,21 +53,21 @@ export function createStructureComponent(a: Structure, params: StructureComponen
         case 'static': {
             let query: StructureQuery;
             switch (params.type.params) {
-                case 'all': query = StructureSelectionQueries.all.query; label = 'Sequence'; break;
+                case 'all': query = StructureSelectionQueries.all.query; label = 'All'; break;
+
+                case 'polymer': query = StructureSelectionQueries.polymer.query; label = 'Polymer'; break;
 
-                case 'protein-or-nucleic': query = StructureSelectionQueries.proteinOrNucleic.query; label = 'Sequence'; break;
-    
                 case 'protein': query = StructureSelectionQueries.protein.query; label = 'Protein'; break;
                 case 'nucleic': query = StructureSelectionQueries.nucleic.query; label = 'Nucleic'; break;
                 case 'water': query = Queries.internal.water(); label = 'Water'; break;
-    
+
                 case 'branched': query = StructureSelectionQueries.branchedPlusConnected.query; label = 'Branched'; break;
                 case 'ligand': query = StructureSelectionQueries.ligandPlusConnected.query; label = 'Ligand'; break;
-    
+
                 case 'non-standard': query = StructureSelectionQueries.nonStandardPolymer.query; label = 'Non-standard'; break;
-    
+
                 case 'coarse': query = StructureSelectionQueries.coarse.query; label = 'Coarse'; break;
-        
+
                 default: throw new Error(`${params.type} is a not valid complex element.`);
             }
             const result = query(new QueryContext(a));
@@ -96,7 +96,7 @@ export function createStructureComponent(a: Structure, params: StructureComponen
 
 export function updateStructureComponent(a: Structure, b: SO.Molecule.Structure, oldParams: StructureComponentParams, newParams: StructureComponentParams, cache: { source: Structure, entry?: StructureQueryHelper.CacheEntry }) {
     if (oldParams.type.name !== newParams.type.name) return StateTransformer.UpdateResult.Recreate;
-    
+
     let updated = false;
 
     switch (newParams.type.name) {

+ 2 - 2
src/mol-plugin-state/transforms/model.ts

@@ -608,7 +608,7 @@ const StructureSelectionFromBundle = PluginStateTransform.BuiltIn({
 });
 
 export const StructureComplexElementTypes = {
-    'protein-or-nucleic': 'protein-or-nucleic',
+    'polymer': 'polymer',
 
     'protein': 'protein',
     'nucleic': 'nucleic',
@@ -642,7 +642,7 @@ const StructureComplexElement = PluginStateTransform.BuiltIn({
 
         let query: StructureQuery, label: string;
         switch (params.type) {
-            case 'protein-or-nucleic': query = StructureSelectionQueries.proteinOrNucleic.query; label = 'Sequence'; break;
+            case 'polymer': query = StructureSelectionQueries.polymer.query; label = 'Polymer'; break;
 
             case 'protein': query = StructureSelectionQueries.protein.query; label = 'Protein'; break;
             case 'nucleic': query = StructureSelectionQueries.nucleic.query; label = 'Nucleic'; break;

+ 1 - 1
src/mol-plugin/util/structure-complex-helper.ts

@@ -14,7 +14,7 @@ import { StructureComplexElementTypes } from '../../mol-plugin-state/transforms/
 export function createDefaultStructureComplex(
     ctx: PluginContext, root: StateBuilder.To<PluginStateObject.Molecule.Structure>
 ) {
-    root.apply(StateTransforms.Model.StructureComplexElement, { type: 'protein-or-nucleic' }, { tags: StructureComplexElementTypes['protein-or-nucleic'] })
+    root.apply(StateTransforms.Model.StructureComplexElement, { type: 'polymer' }, { tags: StructureComplexElementTypes['polymer'] })
         .apply(StateTransforms.Representation.StructureRepresentation3D,
             StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'));
 

+ 9 - 16
src/mol-plugin/util/structure-selection-helper.ts

@@ -75,7 +75,13 @@ 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'])
+        'entity-test': MS.core.logic.and([
+            MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
+            MS.core.str.match([
+                MS.re('(polypeptide|cyclic-pseudo-peptide|nucleotide|peptide nucleic acid)', 'i'),
+                MS.ammp('entitySubtype')
+            ])
+        ])
     })
 ]), { category: StructureSelectionCategory.Type })
 
@@ -155,18 +161,6 @@ 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({
-        'entity-test': MS.core.logic.and([
-            MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
-            MS.core.str.match([
-                MS.re('(polypeptide|cyclic-pseudo-peptide|nucleotide|peptide nucleic acid)', 'i'),
-                MS.ammp('entitySubtype')
-            ])
-        ])
-    })
-]), { category: StructureSelectionCategory.Type })
-
 const helix = StructureSelectionQuery('Helix', MS.struct.modifier.union([
     MS.struct.generator.atomGroups({
         'entity-test': MS.core.logic.and([
@@ -265,7 +259,7 @@ const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
             })
         ])
     ]),
-]), { category: StructureSelectionCategory.Residue })
+]), { category: StructureSelectionCategory.Type })
 
 // don't include branched entities as they have their own link representation
 const ligandPlusConnected = StructureSelectionQuery('Ligand with Connected', MS.struct.modifier.union([
@@ -329,7 +323,7 @@ const coarse = StructureSelectionQuery('Coarse Elements', MS.struct.modifier.uni
             MS.set('sphere', 'gaussian'), MS.ammp('objectPrimitive')
         ])
     })
-]), { category: StructureSelectionCategory.Residue })
+]), { category: StructureSelectionCategory.Type })
 
 const ring = StructureSelectionQuery('Rings in Residues', MS.struct.modifier.union([
     MS.struct.generator.rings()
@@ -471,7 +465,6 @@ export const StructureSelectionQueries = {
     backbone,
     protein,
     nucleic,
-    proteinOrNucleic,
     helix,
     beta,
     water,

+ 1 - 1
src/mol-repr/structure/visual/util/common.ts

@@ -109,7 +109,7 @@ export function getConformation(unit: Unit) {
 
 export const CommonSurfaceParams = {
     ignoreHydrogens: PD.Boolean(false),
-    includeParent: PD.Boolean(true, { description: 'Include elements of the parent structure in surface calculation to get a surface patch of the current structure.' }),
+    includeParent: PD.Boolean(false, { description: 'Include elements of the parent structure in surface calculation to get a surface patch of the current structure.' }),
 }
 export const DefaultCommonSurfaceProps = PD.getDefaultValues(CommonSurfaceParams)
 export type CommonSurfaceProps = typeof DefaultCommonSurfaceProps