From 14c22147d52e400250f8716f849643b03f8f12ff Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Fri, 1 Nov 2019 14:38:32 +0100 Subject: [PATCH] mol-script: added linkedAtomicPairs generator + fixed assembly-symmetry-axes isApplicable for empty structures --- .../representations/assembly-symmetry-axes.ts | 2 +- src/mol-model/structure/query/context.ts | 14 ++++ .../structure/query/queries/generators.ts | 69 ++++++++++++++++++- .../language/symbol-table/structure-query.ts | 6 ++ src/mol-script/runtime/query/table.ts | 1 + src/mol-script/script/mol-script/symbols.ts | 1 + 6 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts b/src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts index 7c215a05e..1f6428c02 100644 --- a/src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts +++ b/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 } // diff --git a/src/mol-model/structure/query/context.ts b/src/mol-model/structure/query/context.ts index e1621e676..85603b000 100644 --- a/src/mol-model/structure/query/context.ts +++ b/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); } diff --git a/src/mol-model/structure/query/queries/generators.ts b/src/mol-model/structure/query/queries/generators.ts index cc2917b19..830fd516d 100644 --- a/src/mol-model/structure/query/queries/generators.ts +++ b/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(); + }; } \ No newline at end of file diff --git a/src/mol-script/language/symbol-table/structure-query.ts b/src/mol-script/language/symbol-table/structure-query.ts index 03e9080f4..234ef399c 100644 --- a/src/mol-script/language/symbol-table/structure-query.ts +++ b/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({ diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts index 77aba6db1..bbe3733cf 100644 --- a/src/mol-script/runtime/query/table.ts +++ b/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 ================ diff --git a/src/mol-script/script/mol-script/symbols.ts b/src/mol-script/script/mol-script/symbols.ts index 68a9e6685..66041b9d9 100644 --- a/src/mol-script/script/mol-script/symbols.ts +++ b/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.' }) -- GitLab