Переглянути джерело

mol-plugin: create structure from selection, better element loci to query translation

David Sehnal 6 роки тому
батько
коміт
28fab6a53c

+ 40 - 1
src/mol-model/structure/structure/element.ts

@@ -15,6 +15,8 @@ import Structure from './structure';
 import Unit from './unit';
 import { Boundary } from './util/boundary';
 import { StructureProperties } from '../structure';
+import { sortArray } from 'mol-data/util';
+import Expression from 'mol-script/language/expression';
 
 interface StructureElement<U = Unit> {
     readonly kind: 'element-location',
@@ -249,6 +251,8 @@ namespace StructureElement {
                 console.warn('toScriptExpression is only supported for Structure with single model, returning empty expression.');
                 return MS.struct.generator.empty();
             }
+            if (loci.elements.length === 0) return MS.struct.generator.empty();
+
             const sourceIndices = UniqueArray.create<number, number>();
             const el = StructureElement.create(), p = StructureProperties.atom.sourceIndex;
             for (const e of loci.elements) {
@@ -262,8 +266,43 @@ namespace StructureElement {
                     UniqueArray.add(sourceIndices, idx, idx);
                 }
             }
+
+            const xs = sourceIndices.array;
+            sortArray(xs);
+
+            const ranges: number[] = [];
+            const set: number[] = [];
+
+            let i = 0, len = xs.length;
+            while (i < len) {
+                const start = i;
+                i++;
+                while (i < len && xs[i - 1] + 1 === xs[i]) i++;
+                const end = i;
+                // TODO: is this a good value?
+                if (end - start > 12) {
+                    ranges[ranges.length] = xs[start];
+                    ranges[ranges.length] = xs[end - 1];
+                } else {
+                    for (let j = start; j < end; j++) {
+                        set[set.length] = xs[j];
+                    }
+                }
+            }
+
+            const siProp = MS.struct.atomProperty.core.sourceIndex();
+            const tests: Expression[] = [];
+
+            // TODO: add set.ofRanges constructor to MolQL???
+            if (set.length > 0) {
+                tests[tests.length] = MS.core.set.has([MS.set.apply(null, set), siProp]);
+            }
+            for (let rI = 0, _rI = ranges.length / 2; rI < _rI; rI++) {
+                tests[tests.length] = MS.core.rel.inRange([siProp, ranges[2 * rI], ranges[2 * rI + 1]]);
+            }
+
             return MS.struct.generator.atomGroups({
-                'atom-test': MS.core.set.has([MS.set.apply(null, sourceIndices.array), MS.struct.atomProperty.core.sourceIndex()]),
+                'atom-test': tests.length > 1 ? MS.core.logic.or.apply(null, tests) : tests[0],
                 'group-by': 0
             });
         }

+ 2 - 0
src/mol-plugin/index.ts

@@ -47,6 +47,8 @@ export const DefaultPluginSpec: PluginSpec = {
         PluginSpec.Action(StateTransforms.Representation.StructureRepresentation3D),
         PluginSpec.Action(StateTransforms.Representation.ExplodeStructureRepresentation3D),
         PluginSpec.Action(StateTransforms.Representation.VolumeRepresentation3D),
+
+        PluginSpec.Action(StateActions.Structure.StructureFromSelection),
     ],
     behaviors: [
         PluginSpec.Behavior(PluginBehaviors.Representation.HighlightLoci),

+ 20 - 1
src/mol-plugin/state/actions/structure.ts

@@ -12,10 +12,11 @@ import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
 import { Download } from '../transforms/data';
 import { StructureRepresentation3DHelpers } from '../transforms/representation';
-import { CustomModelProperties } from '../transforms/model';
+import { CustomModelProperties, StructureSelection } from '../transforms/model';
 import { DataFormatProvider } from './data-format';
 import { FileInfo } from 'mol-util/file-info';
 import { Task } from 'mol-task';
+import { StructureElement } from 'mol-model/structure';
 
 export const MmcifProvider: DataFormatProvider<any> = {
     label: 'mmCIF',
@@ -230,3 +231,21 @@ export const EnableModelCustomProps = StateAction.build({
     const root = state.build().to(ref).insert(CustomModelProperties, params);
     return state.updateTree(root);
 });
+
+export const StructureFromSelection = StateAction.build({
+    display: { name: 'Selection Structure', description: 'Create a new Structure from the current selection.' },
+    from: PluginStateObject.Molecule.Structure,
+    params: {
+        label: PD.Text()
+    }
+    // isApplicable(a, t, ctx: PluginContext) {
+    //     return t.transformer !== CustomModelProperties;
+    // }
+})(({ a, ref, params, state }, plugin: PluginContext) => {
+    const sel = plugin.helpers.structureSelection.get(a.data);
+    if (sel.kind === 'empty-loci') return Task.constant('', void 0);
+
+    const query = StructureElement.Loci.toScriptExpression(sel);
+    const root = state.build().to(ref).apply(StructureSelection, { query, label: params.label });
+    return state.updateTree(root);
+});