Browse Source

adding a transformer to create a single structure for flexible transformations

Yana Rose 4 years ago
parent
commit
69d1765e06
2 changed files with 94 additions and 23 deletions
  1. 35 23
      src/viewer/helpers/preset.ts
  2. 59 0
      src/viewer/helpers/superpose/flexible-structure.ts

+ 35 - 23
src/viewer/helpers/preset.ts

@@ -21,7 +21,8 @@ import { ViewerState } from '../types';
 import { StateSelection, StateObjectSelector, StateObject, StateTransformer } from 'molstar/lib/mol-state';
 import { VolumeStreaming } from 'molstar/lib/mol-plugin/behavior/dynamic/volume-streaming/behavior';
 import { Mat4 } from 'molstar/lib/mol-math/linear-algebra';
-import { StructureSelectionFromExpression, TransformStructureConformation } from 'molstar/lib/mol-plugin-state/transforms/model';
+import { StructureSelectionFromExpression, CustomStructureProperties } from 'molstar/lib/mol-plugin-state/transforms/model';
+import { FlexibleStructureFromModel as FlexibleStructureFromModel } from './superpose/flexible-structure';
 
 type Target = {
     readonly auth_seq_id?: number
@@ -85,7 +86,7 @@ type PropSet = {
     positions: number[]
 };
 
-type PropsetProps = {
+export type PropsetProps = {
     kind: 'prop-set',
     selection?: (Range & {
         matrix?: Mat4
@@ -155,8 +156,10 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({
         let structure: StructureObject | undefined = undefined;
         let structureProperties: StructureObject | undefined = undefined;
 
-        const needLocalStructure = p.kind === 'prop-set' && !!p.selection?.[0]?.matrix;
-        if (!needLocalStructure) {
+        // if flexible transformation is allowed, we may need to create a single structure component
+        // from transformed substructures
+        const allowsFlexTransform = p.kind === 'prop-set';
+        if (!allowsFlexTransform) {
             structure = await builder.createStructure(modelProperties || model, structureParams);
             structureProperties = await builder.insertStructureProperties(structure);
         }
@@ -166,25 +169,23 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({
         let representation: StructureRepresentationPresetProvider.Result | undefined = undefined;
 
         if (p.kind === 'prop-set') {
-            const entryId = model.data?.entryId;
 
+            // this creates a single structure from selections/transformations as specified
+            const _structure = plugin.state.data.build().to(modelProperties)
+                .apply(FlexibleStructureFromModel, { selection: p.selection });
+            structure = await _structure.commit();
+
+            const _structureProperties = plugin.state.data.build().to(structure)
+                .apply(CustomStructureProperties);
+            structureProperties = await _structureProperties.commit();
+
+            const entryId = model.data?.entryId;
             const selections: StructureObject[] = [];
             if (p.selection) {
-                if (needLocalStructure) {
-                    for (const s of p.selection) {
-                        const _structure = await builder.createStructure(modelProperties || model, structureParams);
-                        const _structureProperties = await builder.insertStructureProperties(_structure);
-                        const sele = plugin.state.data.build().to(_structureProperties)
-                            .apply(TransformStructureConformation, {transform: { name: 'matrix', params: { data: s.matrix, transpose: false }}})
-                            .apply(StructureSelectionFromExpression, createSelection(entryId, s));
-                        selections.push(await sele.commit());
-                    }
-                } else {
-                    for (const s of p.selection) {
-                        const sele = plugin.state.data.build().to(structureProperties!)
-                            .apply(StructureSelectionFromExpression, createSelection(entryId, s));
-                        selections.push(await sele.commit());
-                    }
+                for (const s of p.selection) {
+                    const sele = plugin.state.data.build().to(structureProperties!)
+                        .apply(StructureSelectionFromExpression, createSelection(entryId, s));
+                    selections.push(await sele.commit());
                 }
             } else {
                 const sele = plugin.state.data.build().to(structureProperties!)
@@ -192,6 +193,11 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({
                 selections.push(await sele.commit());
             }
 
+            console.log(selections);
+
+            // At this we have a structure that contains only the transformed substructres
+            // The color theme data should be added to `structureProperties.data?.inheritedPropertyData`
+
             const representations = new Array();
             for (const r of p.representation) {
                 for (const sele of selections) {
@@ -213,6 +219,12 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({
                     representations.push(repr);
                 }
             }
+
+            // // this adds a single component
+            // representation = await plugin.builders.structure.representation.applyPreset(structureProperties, 'polymer-cartoon', {
+            //     theme: { globalName: 'superpose', focus: { name: 'superpose' } }
+            // });
+
         } else if (p.kind === 'validation') {
             representation = await plugin.builders.structure.representation.applyPreset(structureProperties!, ValidationReportGeometryQualityPreset);
 
@@ -260,7 +272,7 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({
     }
 });
 
-function createSelection(entryId: string | undefined, range?: Range) {
+export function createSelection(entryId: string | undefined, range?: Range) {
 
     if (!entryId) return {};
 
@@ -280,7 +292,7 @@ function createSelection(entryId: string | undefined, range?: Range) {
     }
 }
 
-const createTest = (asymId: string, residues: number[]) => {
+export const createTest = (asymId: string, residues: number[]) => {
     if (residues.length > 0) {
         return {
             'chain-test': testChain(asymId),
@@ -299,7 +311,7 @@ const testResidues = (residueSet: number[]) => {
     return MS.core.set.has([MS.set(...residueSet), MS.ammp('label_seq_id')]);
 }
 
-const createRange = (start: number, end: number) => [...Array(end - start + 1)].map((_, i) => start + i);
+export const createRange = (start: number, end: number) => [...Array(end - start + 1)].map((_, i) => start + i);
 
 const createLabel = (entryId: string, range: Range) => {
     let label = ''.concat(entryId, '.', range.asymId);

+ 59 - 0
src/viewer/helpers/superpose/flexible-structure.ts

@@ -0,0 +1,59 @@
+import { PluginStateTransform, PluginStateObject as SO } from 'molstar/lib/mol-plugin-state/objects';
+import { RootStructureDefinition } from 'molstar/lib/mol-plugin-state/helpers/root-structure';
+import { PluginContext } from 'molstar/lib/mol-plugin/context';
+import { Task } from 'molstar/lib/mol-task';
+import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition';
+import { PropsetProps, createTest, createRange } from '../preset';
+import { StructureQueryHelper } from 'molstar/lib/mol-plugin-state/helpers/structure-query';
+import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder';
+import { StructureSelection, Structure } from 'molstar/lib/mol-model/structure';
+
+export { FlexibleStructureFromModel as FlexibleStructureFromModel };
+type FlexibleStructureFromModel = typeof FlexibleStructureFromModel
+const FlexibleStructureFromModel = PluginStateTransform.BuiltIn({
+    name: 'flexible-structure-from-model',
+    display: { name: 'Flexible Structure', description: 'Create a molecular structure from independently transformed substructures.' },
+    from: SO.Molecule.Model,
+    to: SO.Molecule.Structure,
+    params(a) {
+        return {
+            selection: PD.Value<PropsetProps['selection']>([])
+        };
+    }
+})({
+    apply({ a, params }, plugin: PluginContext) {
+        return Task.create('Build Flexible Structure', async ctx => {
+            const base = await RootStructureDefinition.create(plugin, ctx, a.data);
+            const { selection } = params;
+            if (!selection?.length) return base;
+
+            const blocks: Structure[] = []
+            for (const p of selection) {
+                const residues: number[] = (p.beg && p.end) ? createRange(p.beg, p.end) : [];
+                const test = createTest(p.asymId, residues);
+                const expression = MS.struct.generator.atomGroups(test);
+                const { selection: sele } = StructureQueryHelper.createAndRun(base.data, expression);
+                const s = StructureSelection.unionStructure(sele);
+                if (!p.matrix) {
+                    blocks.push(s);
+                } else {
+                    const ts = Structure.transform(s, p.matrix);
+                    blocks.push(ts);
+                }
+            }
+
+            const builder = Structure.Builder()
+            for (const b of blocks) {
+                for (const u of b.units) {
+                    builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, u.traits, u.invariantId);
+                }
+            }
+
+            const blockStructure = builder.getStructure();
+            return new SO.Molecule.Structure(blockStructure)
+        });
+    },
+    dispose({ b }) {
+        b?.data.customPropertyDescriptors.dispose();
+    }
+});