Browse Source

mol-script: added linkedAtomicPairs generator + fixed assembly-symmetry-axes isApplicable for empty structures

David Sehnal 5 years ago
parent
commit
14c22147d5

+ 1 - 1
src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts

@@ -55,7 +55,7 @@ export const AssemblySymmetryAxesRepresentationProvider: StructureRepresentation
     defaultValues: PD.getDefaultValues(AssemblySymmetryAxesParams),
     defaultColorTheme: 'uniform',
     defaultSizeTheme: 'uniform',
-    isApplicable: (structure: Structure) => AssemblySymmetry.get(structure.models[0]) !== undefined
+    isApplicable: (structure: Structure) => structure.models.length > 0 && AssemblySymmetry.get(structure.models[0]) !== undefined
 }
 
 //

+ 14 - 0
src/mol-model/structure/query/context.ts

@@ -119,6 +119,20 @@ export class QueryContextLinkInfo<U extends Unit = Unit> {
     type: LinkType = LinkType.Flag.None;
     order: number = 0;
 
+    swap() {
+        const idxA = this.aIndex;
+        this.aIndex = this.bIndex;
+        this.bIndex = idxA;
+
+        const unitA = this.a.unit;
+        this.a.unit = this.b.unit;
+        this.b.unit = unitA;
+
+        const eA = this.a.element;
+        this.a.element = this.b.element;
+        this.b.element = eA;
+    }
+
     get length() {
         return StructureElement.Location.distance(this.a, this. b);
     }

+ 67 - 2
src/mol-model/structure/query/queries/generators.ts

@@ -7,7 +7,7 @@
 
 import { StructureQuery } from '../query'
 import { StructureSelection } from '../selection'
-import { Unit, StructureProperties as P } from '../../structure'
+import { Unit, StructureProperties as P, StructureElement } from '../../structure'
 import { Segmentation, SortedArray } from '../../../../mol-data/int'
 import { LinearGroupingBuilder } from '../utils/builders';
 import { QueryPredicate, QueryFn, QueryContextView } from '../context';
@@ -226,7 +226,7 @@ function getRingStructure(unit: Unit.Atomic, ring: UnitRing, inputStructure: Str
 export function rings(fingerprints?: ArrayLike<UnitRing.Fingerprint>): StructureQuery {
     return function query_rings(ctx) {
         const { units } = ctx.inputStructure;
-        let ret = StructureSelection.LinearBuilder(ctx.inputStructure);
+        const ret = StructureSelection.LinearBuilder(ctx.inputStructure);
 
         if (!fingerprints || fingerprints.length === 0) {
             for (const u of units) {
@@ -274,4 +274,69 @@ export function querySelection(selection: StructureQuery, query: StructureQuery,
         ctx.popInputStructure();
         return StructureSelection.withInputStructure(result, ctx.inputStructure);
     }
+}
+
+export function linkedAtomicPairs(linkTest?: QueryPredicate): StructureQuery {
+    const test = linkTest || ((ctx: any) => true);
+    return function query_linkedAtomicPairs(ctx) {
+        const structure = ctx.inputStructure;
+
+        const interLinks = structure.links;
+        // Note: each link is called twice, that's why we need the unique builder.
+        const ret = StructureSelection.UniqueBuilder(ctx.inputStructure);
+
+        ctx.pushCurrentLink();
+        const atomicLink = ctx.atomicLink;
+
+        // Process intra unit links
+        for (const unit of structure.units) {
+            if (unit.kind !== Unit.Kind.Atomic) continue;
+
+            const { offset: intraLinkOffset, b: intraLinkB, edgeProps: { flags, order } } = unit.links;
+            atomicLink.a.unit = unit;
+            atomicLink.b.unit = unit;
+            for (let i = 0 as StructureElement.UnitIndex, _i = unit.elements.length; i < _i; i++) {
+                atomicLink.aIndex = i as StructureElement.UnitIndex;
+                atomicLink.a.element = unit.elements[i];
+
+                // check intra unit links
+                for (let lI = intraLinkOffset[i], _lI = intraLinkOffset[i + 1]; lI < _lI; lI++) {
+                    atomicLink.bIndex = intraLinkB[lI] as StructureElement.UnitIndex;
+                    atomicLink.b.element = unit.elements[intraLinkB[lI]];
+                    atomicLink.type = flags[lI];
+                    atomicLink.order = order[lI];
+                    if (test(ctx)) {
+                        const b = structure.subsetBuilder(false);
+                        b.beginUnit(unit.id);
+                        b.addElement(atomicLink.a.element);
+                        b.addElement(atomicLink.b.element);
+                        b.commitUnit();
+                        ret.add(b.getStructure());
+                    }
+                }
+            }
+        }
+
+        // Process inter unit links
+        for (const bond of interLinks.bonds) {
+            atomicLink.a.unit = bond.unitA;
+            atomicLink.a.element = bond.unitA.elements[bond.indexA];
+            atomicLink.aIndex = bond.indexA;
+            atomicLink.b.unit = bond.unitB;
+            atomicLink.b.element = bond.unitB.elements[bond.indexB];
+            atomicLink.bIndex = bond.indexB;
+            atomicLink.order = bond.order;
+            atomicLink.type = bond.flag;
+
+            if (test(ctx)) {
+                const b = structure.subsetBuilder(false);
+                b.addToUnit(atomicLink.a.unit.id, atomicLink.a.element);
+                b.addToUnit(atomicLink.b.unit.id, atomicLink.b.element);
+                ret.add(b.getStructure());
+            }
+        }
+
+        ctx.popCurrentLink();
+        return ret.getSelection();
+    };
 }

+ 6 - 0
src/mol-script/language/symbol-table/structure-query.ts

@@ -93,6 +93,12 @@ const generator = {
         'group-by': Argument(Type.Any, { isOptional: true, defaultValue: `atom-key`, description: 'Group atoms to sets based on this property. Default: each atom has its own set' }),
     }), Types.ElementSelectionQuery, 'Return all atoms for which the tests are satisfied, grouped into sets.'),
 
+    linkedAtomicPairs: symbol(Arguments.Dictionary({
+        0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test each link with this predicate. Each link is visited twice with swapped atom order.' }),
+        // TODO: shoud we support this or just use queryEach to get similar behavior
+        // 'group-by': Argument(Type.Any, { isOptional: true, defaultValue: ``, description: 'Group the links using the privided value' }),
+    }), Types.ElementSelectionQuery, 'Return all pairs of atoms for which the test is satisfied.'),
+
     rings: symbol(Arguments.List(Types.RingFingerprint), Types.ElementSelectionQuery, 'Return rings with the specified fingerprint(s). If no fingerprints are given, return all rings.'),
 
     queryInSelection: symbol(Arguments.Dictionary({

+ 1 - 0
src/mol-script/runtime/query/table.ts

@@ -229,6 +229,7 @@ const symbols = [
 
     D(MolScript.structureQuery.generator.all, function structureQuery_generator_all(ctx) { return Queries.generators.all(ctx) }),
     D(MolScript.structureQuery.generator.empty, function structureQuery_generator_empty(ctx) { return Queries.generators.none(ctx) }),
+    D(MolScript.structureQuery.generator.linkedAtomicPairs, function structureQuery_generator_linkedAtomicPairs(ctx, xs) { return Queries.generators.linkedAtomicPairs(xs && xs[0])(ctx) }),
 
     // ============= MODIFIERS ================
 

+ 1 - 0
src/mol-script/script/mol-script/symbols.ts

@@ -106,6 +106,7 @@ export const SymbolTable = [
             Alias(MolScript.structureQuery.generator.rings, 'sel.atom.rings'),
             Alias(MolScript.structureQuery.generator.empty, 'sel.atom.empty'),
             Alias(MolScript.structureQuery.generator.all, 'sel.atom.all'),
+            Alias(MolScript.structureQuery.generator.linkedAtomicPairs, 'sel.atom.linked-pairs'),
 
             Macro(MSymbol('sel.atom.atoms', Arguments.Dictionary({
                 0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to each atom.' })