Bladeren bron

Added InterUnitLinks and renamed "bonds" to "links"

David Sehnal 6 jaren geleden
bovenliggende
commit
565a5ed92e

+ 41 - 16
src/apps/structure-info/model.ts

@@ -72,20 +72,45 @@ export function printSecStructure(model: Model) {
     }
 }
 
-export function printBonds(structure: Structure) {
-    for (const unit of structure.units) {
-        if (!Unit.isAtomic(unit)) continue;
+export function printLinks(structure: Structure, showIntra: boolean, showInter: boolean) {
+    if (showIntra) {
+        console.log('\nIntra Unit Links\n=============');
+        for (const unit of structure.units) {
+            if (!Unit.isAtomic(unit)) continue;
+
+            const elements = unit.elements;
+            const { a, b } = unit.links;
+            const { model } = unit;
+
+            if (!a.length) continue;
+
+            for (let bI = 0, _bI = a.length; bI < _bI; bI++) {
+                const x = a[bI], y = b[bI];
+                if (x >= y) continue;
+                console.log(`${atomLabel(model, elements[x])} -- ${atomLabel(model, elements[y])}`);
+            }
+        }
+    }
 
-        const elements = unit.elements;
-        const { a, b } = unit.bonds;
-        const { model }  = unit;
+    if (showInter) {
+        console.log('\nInter Unit Links\n=============');
+        const links = structure.links;
+        for (const unit of structure.units) {
+            if (!Unit.isAtomic(unit)) continue;
+
+            for (const pairLinks of links.getLinkedUnits(unit)) {
+                if (!pairLinks.areUnitsOrdered) continue;
 
-        if (!a.length) continue;
+                const { unitA, unitB } = pairLinks;
 
-        for (let bI = 0, _bI = a.length; bI < _bI; bI++) {
-            const x = a[bI], y = b[bI];
-            if (x >= y) continue;
-            console.log(`${atomLabel(model, elements[x])} -- ${atomLabel(model, elements[y])}`);
+                console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.bondCount} bond(s)`);
+
+                for (const aI of pairLinks.linkedElementIndices) {
+                    for (const link of pairLinks.getBonds(aI)) {
+                        console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[link.indexB])}`);
+                    }
+                }
+            }
         }
     }
 }
@@ -160,7 +185,7 @@ async function run(mmcif: mmCIF_Database) {
     //printIHMModels(models[0]);
     printUnits(structure);
     printRings(structure);
-    //printBonds(structure);
+    printLinks(structure, false, true);
     //printSecStructure(models[0]);
 }
 
@@ -175,13 +200,13 @@ async function runFile(filename: string) {
 }
 
 const parser = new argparse.ArgumentParser({
-  addHelp: true,
-  description: 'Print info about a structure, mainly to test and showcase the mol-model module'
+    addHelp: true,
+    description: 'Print info about a structure, mainly to test and showcase the mol-model module'
 });
-parser.addArgument([ '--download', '-d' ], {
+parser.addArgument(['--download', '-d'], {
     help: 'Pdb entry id'
 });
-parser.addArgument([ '--file', '-f' ], {
+parser.addArgument(['--file', '-f'], {
     help: 'filename'
 });
 interface Args {

+ 10 - 10
src/mol-geo/representation/structure/bond.ts

@@ -8,7 +8,7 @@
 import { ValueCell } from 'mol-util/value-cell'
 
 import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
-import { Unit, Element, Bond } from 'mol-model/structure';
+import { Unit, Element, Link } from 'mol-model/structure';
 import { UnitsRepresentation, DefaultStructureProps } from './index';
 import { Task } from 'mol-task'
 import { createTransforms } from './utils';
@@ -29,7 +29,7 @@ function createBondMesh(unit: Unit, mesh?: Mesh) {
         if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
         const elements = unit.elements;
-        const bonds = unit.bonds
+        const bonds = unit.links
         const { edgeCount, a, b } = bonds
 
         if (!edgeCount) return Mesh.createEmpty(mesh)
@@ -96,7 +96,7 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
                 currentGroup = group
 
                 const unit = group.units[0]
-                const elementCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0
+                const elementCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
                 const instanceCount = group.units.length
 
                 mesh = await createBondMesh(unit).runAsChild(ctx, 'Computing bond mesh')
@@ -164,11 +164,11 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
             const { objectId, instanceId, elementId } = pickingId
             const unit = currentGroup.units[instanceId]
             if (cylinders.id === objectId && Unit.isAtomic(unit)) {
-                return Bond.Loci([{
+                return Link.Loci([{
                     aUnit: unit,
-                    aIndex: unit.bonds.a[elementId],
+                    aIndex: unit.links.a[elementId],
                     bUnit: unit,
-                    bIndex: unit.bonds.b[elementId]
+                    bIndex: unit.links.b[elementId]
                 }])
             }
             return null
@@ -179,7 +179,7 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
             const unit = group.units[0]
             if (!Unit.isAtomic(unit)) return
 
-            const elementCount = unit.bonds.edgeCount * 2
+            const elementCount = unit.links.edgeCount * 2
             const instanceCount = group.units.length
 
             let changed = false
@@ -187,11 +187,11 @@ export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps
             if (isEveryLoci(loci)) {
                 applyFlagAction(array, 0, elementCount * instanceCount, action)
                 changed = true
-            } else if (Bond.isLoci(loci)) {
-                for (const b of loci.bonds) {
+            } else if (Link.isLoci(loci)) {
+                for (const b of loci.links) {
                     const unitIdx = Unit.findUnitById(b.aUnit.id, group.units)
                     if (unitIdx !== -1) {
-                        const _idx = unit.bonds.getEdgeIndex(b.aIndex, b.bIndex)
+                        const _idx = unit.links.getEdgeIndex(b.aIndex, b.bIndex)
                         if (_idx !== -1) {
                             const idx = _idx
                             if (applyFlagAction(array, idx, idx + 1, action) && !changed) {

+ 1 - 1
src/mol-math/graph.ts

@@ -4,4 +4,4 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-export * from './graph/int-graph'
+export * from './graph/int-adjacency-graph'

+ 3 - 3
src/mol-math/graph/_spec/int-graph.spec.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { IntGraph } from '../int-graph';
+import { IntAdjacencyGraph } from '../int-adjacency-graph';
 
 describe('IntGraph', () => {
     const vc = 3;
@@ -12,7 +12,7 @@ describe('IntGraph', () => {
     const ys = [1, 2, 0];
     const _prop = [10, 11, 12];
 
-    const builder = new IntGraph.EdgeBuilder(vc, xs, ys);
+    const builder = new IntAdjacencyGraph.EdgeBuilder(vc, xs, ys);
     const prop: number[] = new Array(builder.slotCount);
     for (let i = 0; i < builder.edgeCount; i++) {
         builder.addNextEdge();
@@ -35,7 +35,7 @@ describe('IntGraph', () => {
     });
 
     it('induce', () => {
-        const induced = IntGraph.induceByVertices(graph, [1, 2]);
+        const induced = IntAdjacencyGraph.induceByVertices(graph, [1, 2]);
         expect(induced.vertexCount).toBe(2);
         expect(induced.edgeCount).toBe(1);
         expect(induced.edgeProps.prop[induced.getEdgeIndex(0, 1)]).toBe(11);

+ 10 - 10
src/mol-math/graph/int-graph.ts → src/mol-math/graph/int-adjacency-graph.ts

@@ -1,11 +1,11 @@
-import { arrayPickIndices } from 'mol-data/util';
-
 /**
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import { arrayPickIndices } from 'mol-data/util';
+
 /**
  * Represent a graph using vertex adjacency list.
  *
@@ -14,7 +14,7 @@ import { arrayPickIndices } from 'mol-data/util';
  *
  * Edge properties are indexed same as in the arrays a and b.
  */
-interface IntGraph<EdgeProps extends IntGraph.EdgePropsBase = {}> {
+interface IntAdjacencyGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase = {}> {
     readonly offset: ArrayLike<number>,
     readonly a: ArrayLike<number>,
     readonly b: ArrayLike<number>,
@@ -33,10 +33,10 @@ interface IntGraph<EdgeProps extends IntGraph.EdgePropsBase = {}> {
     getVertexEdgeCount(i: number): number
 }
 
-namespace IntGraph {
+namespace IntAdjacencyGraph {
     export type EdgePropsBase = { [name: string]: ArrayLike<any> }
 
-    class IntGraphImpl implements IntGraph<any> {
+    class IntGraphImpl implements IntAdjacencyGraph<any> {
         readonly vertexCount: number;
         readonly edgeProps: object;
 
@@ -60,8 +60,8 @@ namespace IntGraph {
         }
     }
 
-    export function create<EdgeProps extends IntGraph.EdgePropsBase = {}>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntGraph<EdgeProps> {
-        return new IntGraphImpl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>;
+    export function create<EdgeProps extends IntAdjacencyGraph.EdgePropsBase = {}>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntAdjacencyGraph<EdgeProps> {
+        return new IntGraphImpl(offset, a, b, edgeCount, edgeProps) as IntAdjacencyGraph<EdgeProps>;
     }
 
     export class EdgeBuilder {
@@ -77,7 +77,7 @@ namespace IntGraph {
         a: Int32Array;
         b: Int32Array;
 
-        createGraph<EdgeProps extends IntGraph.EdgePropsBase = {}>(edgeProps?: EdgeProps) {
+        createGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase = {}>(edgeProps?: EdgeProps) {
             return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps);
         }
 
@@ -135,7 +135,7 @@ namespace IntGraph {
         }
     }
 
-    export function induceByVertices<P extends IntGraph.EdgePropsBase>(graph: IntGraph<P>, vertexIndices: ArrayLike<number>): IntGraph<P> {
+    export function induceByVertices<P extends IntAdjacencyGraph.EdgePropsBase>(graph: IntAdjacencyGraph<P>, vertexIndices: ArrayLike<number>): IntAdjacencyGraph<P> {
         const { b, offset, vertexCount, edgeProps } = graph;
         const vertexMap = new Int32Array(vertexCount);
         for (let i = 0, _i = vertexIndices.length; i < _i; i++) vertexMap[vertexIndices[i]] = i + 1;
@@ -177,4 +177,4 @@ namespace IntGraph {
     }
 }
 
-export { IntGraph }
+export { IntAdjacencyGraph }

+ 2 - 2
src/mol-model/loci.ts

@@ -5,7 +5,7 @@
  */
 
 import { Element } from './structure'
-import { Bond } from './structure/structure/unit/bonds'
+import { Link } from './structure/structure/unit/links'
 
 /** A Loci that includes every loci */
 export const EveryLoci = { kind: 'every-loci' as 'every-loci' }
@@ -14,4 +14,4 @@ export function isEveryLoci(x: any): x is EveryLoci {
     return !!x && x.kind === 'every-loci';
 }
 
-export type Loci =  Element.Loci | Bond.Loci | EveryLoci
+export type Loci =  Element.Loci | Link.Loci | EveryLoci

+ 67 - 57
src/mol-model/structure/model/formats/mmcif/bonds.ts

@@ -6,72 +6,82 @@
  */
 
 import Model from '../../model'
-import { BondType } from '../../types'
+import { LinkType } from '../../types'
 import { findEntityIdByAsymId, findAtomIndexByLabelName } from './util'
 import { Column } from 'mol-data/db'
-import { IntraUnitBonds } from '../../../structure/unit/bonds';
 
-export class StructConn implements IntraUnitBonds.StructConn {
-    private _residuePairIndex: Map<string, StructConn.Entry[]> | undefined = void 0;
-    private _atomIndex: Map<number, StructConn.Entry[]> | undefined = void 0;
+export interface StructConn {
+    getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry>
+    getAtomEntries(atomIndex: number): ReadonlyArray<StructConn.Entry>
+}
+
+export interface ComponentBondInfo {
+    entries: Map<string, ComponentBondInfo.Entry>
+}
 
-    private static _resKey(rA: number, rB: number) {
+export namespace StructConn {
+    function _resKey(rA: number, rB: number) {
         if (rA < rB) return `${rA}-${rB}`;
         return `${rB}-${rA}`;
     }
-
-    private getResiduePairIndex() {
-        if (this._residuePairIndex) return this._residuePairIndex;
-        this._residuePairIndex = new Map();
-        for (const e of this.entries) {
-            const ps = e.partners;
-            const l = ps.length;
-            for (let i = 0; i < l - 1; i++) {
-                for (let j = i + i; j < l; j++) {
-                    const key = StructConn._resKey(ps[i].residueIndex, ps[j].residueIndex);
-                    if (this._residuePairIndex.has(key)) {
-                        this._residuePairIndex.get(key)!.push(e);
-                    } else {
-                        this._residuePairIndex.set(key, [e]);
+    const _emptyEntry: Entry[] = [];
+
+    class StructConnImpl implements StructConn {
+        private _residuePairIndex: Map<string, StructConn.Entry[]> | undefined = void 0;
+        private _atomIndex: Map<number, StructConn.Entry[]> | undefined = void 0;
+
+        private getResiduePairIndex() {
+            if (this._residuePairIndex) return this._residuePairIndex;
+            this._residuePairIndex = new Map();
+            for (const e of this.entries) {
+                const ps = e.partners;
+                const l = ps.length;
+                for (let i = 0; i < l - 1; i++) {
+                    for (let j = i + i; j < l; j++) {
+                        const key = _resKey(ps[i].residueIndex, ps[j].residueIndex);
+                        if (this._residuePairIndex.has(key)) {
+                            this._residuePairIndex.get(key)!.push(e);
+                        } else {
+                            this._residuePairIndex.set(key, [e]);
+                        }
                     }
                 }
             }
+            return this._residuePairIndex;
         }
-        return this._residuePairIndex;
-    }
 
-    private getAtomIndex() {
-        if (this._atomIndex) return this._atomIndex;
-        this._atomIndex = new Map();
-        for (const e of this.entries) {
-            for (const p of e.partners) {
-                const key = p.atomIndex;
-                if (this._atomIndex.has(key)) {
-                    this._atomIndex.get(key)!.push(e);
-                } else {
-                    this._atomIndex.set(key, [e]);
+        private getAtomIndex() {
+            if (this._atomIndex) return this._atomIndex;
+            this._atomIndex = new Map();
+            for (const e of this.entries) {
+                for (const p of e.partners) {
+                    const key = p.atomIndex;
+                    if (this._atomIndex.has(key)) {
+                        this._atomIndex.get(key)!.push(e);
+                    } else {
+                        this._atomIndex.set(key, [e]);
+                    }
                 }
             }
+            return this._atomIndex;
         }
-        return this._atomIndex;
-    }
 
-    private static _emptyEntry = [];
 
-    getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry> {
-        return this.getResiduePairIndex().get(StructConn._resKey(residueAIndex, residueBIndex)) || StructConn._emptyEntry;
-    }
+        getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry> {
+            return this.getResiduePairIndex().get(_resKey(residueAIndex, residueBIndex)) || _emptyEntry;
+        }
 
-    getAtomEntries(atomIndex: number): ReadonlyArray<StructConn.Entry> {
-        return this.getAtomIndex().get(atomIndex) || StructConn._emptyEntry;
-    }
+        getAtomEntries(atomIndex: number): ReadonlyArray<StructConn.Entry> {
+            return this.getAtomIndex().get(atomIndex) || _emptyEntry;
+        }
 
-    constructor(public entries: StructConn.Entry[]) {
+        constructor(public entries: StructConn.Entry[]) {
+        }
     }
-}
 
-export namespace StructConn {
-    export interface Entry extends IntraUnitBonds.StructConnEntry {
+
+
+    export interface Entry {
         distance: number,
         order: number,
         flags: number,
@@ -91,7 +101,7 @@ export namespace StructConn {
         | 'saltbr'
 
     export function create(model: Model): StructConn | undefined {
-        if (model.sourceData.kind !== 'mmCIF') return
+        if (model.sourceData.kind !== 'mmCIF') return;
         const { struct_conn } = model.sourceData.data;
         if (!struct_conn._rowCount) return void 0;
 
@@ -150,7 +160,7 @@ export namespace StructConn {
 
             const type = conn_type_id.value(i)! as StructConnType;
             const orderType = (pdbx_value_order.value(i) || '').toLowerCase();
-            let flags = BondType.Flag.None;
+            let flags = LinkType.Flag.None;
             let order = 1;
 
             switch (orderType) {
@@ -166,22 +176,22 @@ export namespace StructConn {
                 case 'covale_phosphate':
                 case 'covale_sugar':
                 case 'modres':
-                    flags = BondType.Flag.Covalent;
+                    flags = LinkType.Flag.Covalent;
                     break;
-                case 'disulf': flags = BondType.Flag.Covalent | BondType.Flag.Sulfide; break;
-                case 'hydrog': flags = BondType.Flag.Hydrogen; break;
-                case 'metalc': flags = BondType.Flag.MetallicCoordination; break;
-                case 'saltbr': flags = BondType.Flag.Ion; break;
+                case 'disulf': flags = LinkType.Flag.Covalent | LinkType.Flag.Sulfide; break;
+                case 'hydrog': flags = LinkType.Flag.Hydrogen; break;
+                case 'metalc': flags = LinkType.Flag.MetallicCoordination; break;
+                case 'saltbr': flags = LinkType.Flag.Ion; break;
             }
 
             entries.push({ flags, order, distance: pdbx_dist_value.value(i), partners });
         }
 
-        return new StructConn(entries);
+        return new StructConnImpl(entries);
     }
 }
 
-export class ComponentBondInfo implements IntraUnitBonds.ComponentBondInfo {
+export class ComponentBondInfo implements ComponentBondInfo {
     entries: Map<string, ComponentBondInfo.Entry> = new Map();
 
     newEntry(id: string) {
@@ -192,7 +202,7 @@ export class ComponentBondInfo implements IntraUnitBonds.ComponentBondInfo {
 }
 
 export namespace ComponentBondInfo {
-    export class Entry implements IntraUnitBonds.ComponentBondInfoEntry {
+    export class Entry implements Entry {
         map: Map<string, Map<string, { order: number, flags: number }>> = new Map();
 
         add(a: string, b: string, order: number, flags: number, swap = true) {
@@ -238,9 +248,9 @@ export namespace ComponentBondInfo {
                 entry = info.newEntry(id);
             }
 
-            let flags: number = BondType.Flag.Covalent;
+            let flags: number = LinkType.Flag.Covalent;
             let ord = 1;
-            if (aromatic) flags |= BondType.Flag.Aromatic;
+            if (aromatic) flags |= LinkType.Flag.Aromatic;
             switch (order.toLowerCase()) {
                 case 'doub':
                 case 'delo':

+ 7 - 3
src/mol-model/structure/model/types.ts

@@ -340,9 +340,9 @@ export const VdwRadii = {
 }
 export const DefaultVdwRadius = 2.0
 
-export interface BondType extends BitFlags<BondType.Flag> { }
-export namespace BondType {
-    export const is: (b: BondType, f: Flag) => boolean = BitFlags.has
+export interface LinkType extends BitFlags<LinkType.Flag> { }
+export namespace LinkType {
+    export const is: (b: LinkType, f: Flag) => boolean = BitFlags.has
     export const enum Flag {
         None                 = 0x0,
         Covalent             = 0x1,
@@ -354,4 +354,8 @@ export namespace BondType {
         Computed             = 0x40
         // currently at most 16 flags are supported!!
     }
+
+    export function isCovalent(flags: LinkType.Flag) {
+        return (flags & LinkType.Flag.Covalent) !== 0;
+    }
 }

+ 2 - 2
src/mol-model/structure/structure.ts

@@ -8,6 +8,6 @@ import Element from './structure/element'
 import Structure from './structure/structure'
 import Unit from './structure/unit'
 import StructureSymmetry from './structure/symmetry'
-import { Bond } from './structure/unit/bonds'
+import { Link } from './structure/unit/links'
 
-export { Element, Bond, Structure, Unit, StructureSymmetry }
+export { Element, Link, Structure, Unit, StructureSymmetry }

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

@@ -15,6 +15,7 @@ import { StructureLookup3D } from './util/lookup3d';
 import { CoarseElements } from '../model/properties/coarse';
 import { StructureSubsetBuilder } from './util/subset-builder';
 import { Queries } from '../query';
+import { InterUnitBonds, computeInterUnitBonds } from './unit/links';
 
 class Structure {
     readonly unitMap: IntMap<Unit>;
@@ -60,6 +61,13 @@ class Structure {
         return this._lookup3d;
     }
 
+    private _links?: InterUnitBonds = void 0;
+    get links() {
+        if (this._links) return this._links;
+        this._links = computeInterUnitBonds(this);
+        return this._links;
+    }
+
     constructor(units: ArrayLike<Unit>) {
         const map = IntMap.Mutable<Unit>();
         let elementCount = 0;

+ 8 - 8
src/mol-model/structure/structure/unit.ts

@@ -9,7 +9,7 @@ import { Model } from '../model'
 import { GridLookup3D, Lookup3D } from 'mol-math/geometry'
 import { SortedArray } from 'mol-data/int';
 import { idFactory } from 'mol-util/id-factory';
-import { IntraUnitBonds, computeIntraUnitBonds } from './unit/bonds'
+import { IntraUnitLinks, computeIntraUnitBonds } from './unit/links'
 import { CoarseElements, CoarseSphereConformation, CoarseGaussianConformation } from '../model/properties/coarse';
 import { ValueRef } from 'mol-util';
 import { UnitRings } from './unit/rings';
@@ -100,10 +100,10 @@ namespace Unit {
             return this.props.lookup3d.ref;
         }
 
-        get bonds() {
-            if (this.props.bonds.ref) return this.props.bonds.ref;
-            this.props.bonds.ref = computeIntraUnitBonds(this);
-            return this.props.bonds.ref;
+        get links() {
+            if (this.props.links.ref) return this.props.links.ref;
+            this.props.links.ref = computeIntraUnitBonds(this);
+            return this.props.links.ref;
         }
 
         get rings() {
@@ -127,12 +127,12 @@ namespace Unit {
 
     interface AtomicProperties {
         lookup3d: ValueRef<Lookup3D | undefined>,
-        bonds: ValueRef<IntraUnitBonds | undefined>,
+        links: ValueRef<IntraUnitLinks | undefined>,
         rings: ValueRef<UnitRings | undefined>
     }
 
-    function AtomicProperties() {
-        return { lookup3d: ValueRef.create(void 0), bonds: ValueRef.create(void 0), rings: ValueRef.create(void 0) };
+    function AtomicProperties(): AtomicProperties {
+        return { lookup3d: ValueRef.create(void 0), links: ValueRef.create(void 0), rings: ValueRef.create(void 0) };
     }
 
     class Coarse<K extends Kind.Gaussians | Kind.Spheres, C extends CoarseSphereConformation | CoarseGaussianConformation> implements Base {

+ 0 - 1
src/mol-model/structure/structure/unit/bonds/data.ts

@@ -1 +0,0 @@
-// TODO

+ 0 - 1
src/mol-model/structure/structure/unit/bonds/inter-compute.ts

@@ -1 +0,0 @@
-// TODO

+ 0 - 1
src/mol-model/structure/structure/unit/bonds/inter-data.ts

@@ -1 +0,0 @@
-// TODO

File diff suppressed because it is too large
+ 0 - 18
src/mol-model/structure/structure/unit/bonds/intra-compute.ts


+ 0 - 38
src/mol-model/structure/structure/unit/bonds/intra-data.ts

@@ -1,38 +0,0 @@
-/**
- * Copyright (c) 2017-2018 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 { BondType } from '../../../model/types'
-import { IntGraph } from 'mol-math/graph';
-
-type IntraUnitBonds = IntGraph<{ readonly order: ArrayLike<number>, readonly flags: ArrayLike<BondType.Flag> }>
-
-namespace IntraUnitBonds {
-    export function createEmpty(): IntraUnitBonds {
-        return IntGraph.create([], [], [], 0, { flags: [], order: [] });
-    }
-    export function isCovalent(flags: number) {
-        return (flags & BondType.Flag.Covalent) !== 0;
-    }
-    export interface StructConnEntry {
-        flags: BondType.Flag,
-        order: number,
-        distance: number,
-        partners: { residueIndex: number, atomIndex: number, symmetry: string }[]
-    }
-    export interface StructConn {
-        getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConnEntry>
-        getAtomEntries(atomIndex: number): ReadonlyArray<StructConnEntry>
-    }
-    export interface ComponentBondInfoEntry {
-        map: Map<string, Map<string, { order: number, flags: number }>>
-    }
-    export interface ComponentBondInfo {
-        entries: Map<string, ComponentBondInfoEntry>
-    }
-}
-
-export { IntraUnitBonds }

+ 10 - 9
src/mol-model/structure/structure/unit/bonds.ts → src/mol-model/structure/structure/unit/links.ts

@@ -6,10 +6,11 @@
 
 import { Unit } from '../../structure'
 
-export * from './bonds/intra-data'
-export * from './bonds/intra-compute'
+export * from './links/data'
+export * from './links/intra-compute'
+export * from './links/inter-compute'
 
-namespace Bond {
+namespace Link {
     export interface Location {
         readonly aUnit: Unit,
         /** Index into aUnit.elements */
@@ -20,17 +21,17 @@ namespace Bond {
     }
 
     export interface Loci {
-        readonly kind: 'bond-loci',
-        readonly bonds: ReadonlyArray<Location>
+        readonly kind: 'link-loci',
+        readonly links: ReadonlyArray<Location>
     }
 
-    export function Loci(bonds: ArrayLike<Location>): Loci {
-        return { kind: 'bond-loci', bonds: bonds as Loci['bonds'] };
+    export function Loci(links: ArrayLike<Location>): Loci {
+        return { kind: 'link-loci', links: links as Loci['links'] };
     }
 
     export function isLoci(x: any): x is Loci {
-        return !!x && x.kind === 'bond-loci';
+        return !!x && x.kind === 'link-loci';
     }
 }
 
-export { Bond }
+export { Link }

File diff suppressed because it is too large
+ 14 - 0
src/mol-model/structure/structure/unit/links/common.ts


+ 59 - 0
src/mol-model/structure/structure/unit/links/data.ts

@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2017-2018 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 { LinkType } from '../../../model/types'
+import { IntAdjacencyGraph } from 'mol-math/graph';
+import Unit from '../../unit';
+
+type IntraUnitLinks = IntAdjacencyGraph<{ readonly order: ArrayLike<number>, readonly flags: ArrayLike<LinkType.Flag> }>
+
+namespace IntraUnitLinks {
+    export const Empty: IntraUnitLinks = IntAdjacencyGraph.create([], [], [], 0, { flags: [], order: [] });
+}
+
+class InterUnitBonds {
+    getLinkedUnits(unit: Unit): ReadonlyArray<InterUnitBonds.UnitPairBonds> {
+        if (!this.map.has(unit.id)) return emptyArray;
+        return this.map.get(unit.id)!;
+    }
+
+    constructor(private map: Map<number, InterUnitBonds.UnitPairBonds[]>) {
+    }
+}
+
+namespace InterUnitBonds {
+    export class UnitPairBonds {
+        hasBonds(indexA: number) {
+            return this.linkMap.has(indexA);
+        }
+
+        getBonds(indexA: number): ReadonlyArray<InterUnitBonds.BondInfo> {
+            if (!this.linkMap.has(indexA)) return emptyArray;
+            return this.linkMap.get(indexA)!;
+        }
+
+        get areUnitsOrdered() {
+            return this.unitA.id < this.unitB.id;
+        }
+
+        constructor(public unitA: Unit.Atomic, public unitB: Unit.Atomic,
+            public bondCount: number, public linkedElementIndices: ReadonlyArray<number>,
+            private linkMap: Map<number, BondInfo[]>) {
+        }
+    }
+
+    export interface BondInfo {
+        /** indexInto */
+        readonly indexB: number,
+        readonly order: number,
+        readonly flag: LinkType.Flag
+    }
+}
+
+const emptyArray: any[] = [];
+
+export { IntraUnitLinks, InterUnitBonds }

+ 149 - 0
src/mol-model/structure/structure/unit/links/inter-compute.ts

@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2017 Mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { StructConn } from '../../../model/formats/mmcif/bonds';
+import { LinkType } from '../../../model/types';
+import Structure from '../../structure';
+import Unit from '../../unit';
+import { getElementIdx, getElementPairThreshold, getElementThreshold, isHydrogen, LinkComputationParameters, MetalsSet } from './common';
+import { InterUnitBonds } from './data';
+import { UniqueArray } from 'mol-data/generic';
+
+const MAX_RADIUS = 4;
+
+function addMapEntry<A, B>(map: Map<A, B[]>, a: A, b: B) {
+    if (map.has(a)) map.get(a)!.push(b);
+    else map.set(a, [b]);
+}
+
+interface PairState {
+    mapAB: Map<number, InterUnitBonds.BondInfo[]>,
+    mapBA: Map<number, InterUnitBonds.BondInfo[]>,
+    bondedA: UniqueArray<number, number>,
+    bondedB: UniqueArray<number, number>
+}
+
+function addLink(indexA: number, indexB: number, order: number, flag: LinkType.Flag, state: PairState) {
+    addMapEntry(state.mapAB, indexA, { indexB, order, flag });
+    addMapEntry(state.mapBA, indexB, { indexB: indexA, order, flag });
+    UniqueArray.add(state.bondedA, indexA, indexA);
+    UniqueArray.add(state.bondedB, indexB, indexB);
+}
+
+function findPairLinks(unitA: Unit.Atomic, unitB: Unit.Atomic, params: LinkComputationParameters, map: Map<number, InterUnitBonds.UnitPairBonds[]>) {
+    const state: PairState = { mapAB: new Map(), mapBA: new Map(), bondedA: UniqueArray.create(), bondedB: UniqueArray.create() };
+    let bondCount = 0;
+
+    const { elements: atomsA, conformation: { x, y, z } } = unitA;
+    const { elements: atomsB } = unitB;
+    const atomCount = unitA.elements.length;
+
+    const { type_symbol: type_symbolA, label_alt_id: label_alt_idA } = unitA.model.atomicHierarchy.atoms;
+    const { type_symbol: type_symbolB, label_alt_id: label_alt_idB } = unitB.model.atomicHierarchy.atoms;
+    const { lookup3d } = unitB;
+    const structConn = unitA.model === unitB.model && unitA.model.sourceData.kind === 'mmCIF' ? StructConn.create(unitA.model) : void 0;
+
+    for (let _aI = 0; _aI < atomCount; _aI++) {
+        const aI =  atomsA[_aI];
+
+        const aeI = getElementIdx(type_symbolA.value(aI));
+        const { indices, count, squaredDistances } = lookup3d.find(x(aI), y(aI), z(aI), MAX_RADIUS);
+        const isHa = isHydrogen(aeI);
+        const thresholdA = getElementThreshold(aeI);
+        const altA = label_alt_idA.value(aI);
+        const metalA = MetalsSet.has(aeI);
+        const structConnEntries = params.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI);
+
+        for (let ni = 0; ni < count; ni++) {
+            const _bI = indices[ni];
+            const bI = atomsB[_bI];
+
+            const altB = label_alt_idB.value(bI);
+            // TODO: check if they have the same model?
+            if (altA && altB && altA !== altB) continue;
+
+            const beI = getElementIdx(type_symbolB.value(bI)!);
+            const isMetal = metalA || MetalsSet.has(beI);
+
+            const isHb = isHydrogen(beI);
+            if (isHa && isHb) continue;
+
+            const dist = Math.sqrt(squaredDistances[ni]);
+            if (dist === 0) continue;
+
+            // handle "struct conn" bonds.
+            if (structConnEntries && structConnEntries.length) {
+                let added = false;
+                for (const se of structConnEntries) {
+                    for (const p of se.partners) {
+                        if (p.atomIndex === bI) {
+                            addLink(_aI, _bI, se.order, se.flags, state);
+                            bondCount++;
+                            added = true;
+                            break;
+                        }
+                    }
+                    if (added) break;
+                }
+                if (added) continue;
+            }
+
+            if (isHa || isHb) {
+                if (dist < params.maxHbondLength) {
+                    addLink(_aI, _bI, 1, LinkType.Flag.Covalent | LinkType.Flag.Computed, state); // TODO: check if correct
+                    bondCount++;
+                }
+                continue;
+            }
+
+            const thresholdAB = getElementPairThreshold(aeI, beI);
+            const pairingThreshold = thresholdAB > 0
+                ? thresholdAB
+                : beI < 0 ? thresholdA : Math.max(thresholdA, getElementThreshold(beI));
+
+            if (dist <= pairingThreshold) {
+                addLink(_aI, _bI, 1, (isMetal ? LinkType.Flag.MetallicCoordination : LinkType.Flag.Covalent) | LinkType.Flag.Computed, state);
+                bondCount++;
+            }
+        }
+    }
+
+    addMapEntry(map, unitA.id, new InterUnitBonds.UnitPairBonds(unitA, unitB, bondCount, state.bondedA.array, state.mapAB));
+    addMapEntry(map, unitB.id, new InterUnitBonds.UnitPairBonds(unitB, unitA, bondCount, state.bondedB.array, state.mapBA));
+
+    return bondCount;
+}
+
+function findLinks(structure: Structure, params: LinkComputationParameters) {
+    const map = new Map<number, InterUnitBonds.UnitPairBonds[]>();
+    if (!structure.units.some(u => Unit.isAtomic(u))) return new InterUnitBonds(map);
+
+    const lookup = structure.lookup3d;
+    for (const unit of structure.units) {
+        if (!Unit.isAtomic(unit)) continue;
+
+        const bs = unit.lookup3d.boundary.sphere;
+        const closeUnits = lookup.findUnitIndices(bs.center[0], bs.center[1], bs.center[2], bs.radius + MAX_RADIUS);
+        for (let i = 0; i < closeUnits.count; i++) {
+            const other = structure.units[closeUnits.indices[i]];
+            if (!Unit.isAtomic(other) || unit.id >= other.id) continue;
+
+            if (other.elements.length >= unit.elements.length) findPairLinks(unit, other, params, map);
+            else findPairLinks(other, unit, params, map);
+        }
+    }
+
+    return new InterUnitBonds(map);
+}
+
+function computeInterUnitBonds(structure: Structure, params?: Partial<LinkComputationParameters>): InterUnitBonds {
+    return findLinks(structure, {
+        maxHbondLength: (params && params.maxHbondLength) || 1.15,
+        forceCompute: !!(params && params.forceCompute),
+    });
+}
+
+export { computeInterUnitBonds };

+ 161 - 0
src/mol-model/structure/structure/unit/links/intra-compute.ts

@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2017 Mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { LinkType } from '../../../model/types'
+import { IntraUnitLinks } from './data'
+import { StructConn, ComponentBondInfo } from '../../../model/formats/mmcif/bonds'
+import Unit from '../../unit'
+import { IntAdjacencyGraph } from 'mol-math/graph';
+import { LinkComputationParameters, getElementIdx, MetalsSet, getElementThreshold, isHydrogen, getElementPairThreshold } from './common';
+
+function getGraph(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number): IntraUnitLinks {
+    const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB);
+    const flags = new Uint16Array(builder.slotCount);
+    const order = new Int8Array(builder.slotCount);
+    for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
+        builder.addNextEdge();
+        builder.assignProperty(flags, _flags[i]);
+        builder.assignProperty(order, _order[i]);
+    }
+
+    return builder.createGraph({ flags, order });
+}
+
+function _computeBonds(unit: Unit.Atomic, params: LinkComputationParameters): IntraUnitLinks {
+    const MAX_RADIUS = 4;
+
+    const { x, y, z } = unit.model.atomicConformation;
+    const atomCount = unit.elements.length;
+    const { elements: atoms, residueIndex } = unit;
+    const { type_symbol, label_atom_id, label_alt_id } = unit.model.atomicHierarchy.atoms;
+    const { label_comp_id } = unit.model.atomicHierarchy.residues;
+    const query3d = unit.lookup3d;
+
+    const structConn = unit.model.sourceData.kind === 'mmCIF' ? StructConn.create(unit.model) : void 0;
+    const component = unit.model.sourceData.kind === 'mmCIF' ? ComponentBondInfo.create(unit.model) : void 0;
+
+    const atomA: number[] = [];
+    const atomB: number[] = [];
+    const flags: number[] = [];
+    const order: number[] = [];
+
+    let lastResidue = -1;
+    let componentMap: Map<string, Map<string, { flags: number, order: number }>> | undefined = void 0;
+
+    for (let _aI = 0; _aI < atomCount; _aI++) {
+        const aI =  atoms[_aI];
+        const raI = residueIndex[aI];
+
+        if (!params.forceCompute && raI !== lastResidue) {
+            const resn = label_comp_id.value(raI)!;
+            if (!!component && component.entries.has(resn)) {
+                componentMap = component.entries.get(resn)!.map;
+            } else {
+                componentMap = void 0;
+            }
+        }
+        lastResidue = raI;
+
+        const componentPairs = componentMap ? componentMap.get(label_atom_id.value(aI)) : void 0;
+
+        const aeI = getElementIdx(type_symbol.value(aI)!);
+
+        const { indices, count, squaredDistances } = query3d.find(x[aI], y[aI], z[aI], MAX_RADIUS);
+        const isHa = isHydrogen(aeI);
+        const thresholdA = getElementThreshold(aeI);
+        const altA = label_alt_id.value(aI);
+        const metalA = MetalsSet.has(aeI);
+        const structConnEntries = params.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI);
+
+        for (let ni = 0; ni < count; ni++) {
+            const _bI = indices[ni];
+            const bI = atoms[_bI];
+            if (bI <= aI) continue;
+
+            const altB = label_alt_id.value(bI);
+            if (altA && altB && altA !== altB) continue;
+
+            const beI = getElementIdx(type_symbol.value(bI)!);
+            const isMetal = metalA || MetalsSet.has(beI);
+
+            const rbI = residueIndex[bI];
+            // handle "component dictionary" bonds.
+            if (raI === rbI && componentPairs) {
+                const e = componentPairs.get(label_atom_id.value(bI)!);
+                if (e) {
+                    atomA[atomA.length] = _aI;
+                    atomB[atomB.length] = _bI;
+                    order[order.length] = e.order;
+                    let flag = e.flags;
+                    if (isMetal) {
+                        if (flag | LinkType.Flag.Covalent) flag ^= LinkType.Flag.Covalent;
+                        flag |= LinkType.Flag.MetallicCoordination;
+                    }
+                    flags[flags.length] = flag;
+                }
+                continue;
+            }
+
+            const isHb = isHydrogen(beI);
+            if (isHa && isHb) continue;
+
+            const dist = Math.sqrt(squaredDistances[ni]);
+            if (dist === 0) continue;
+
+            // handle "struct conn" bonds.
+            if (structConnEntries && structConnEntries.length) {
+                let added = false;
+                for (const se of structConnEntries) {
+                    for (const p of se.partners) {
+                        if (p.atomIndex === bI) {
+                            atomA[atomA.length] = _aI;
+                            atomB[atomB.length] = _bI;
+                            flags[flags.length] = se.flags;
+                            order[order.length] = se.order;
+                            added = true;
+                            break;
+                        }
+                    }
+                    if (added) break;
+                }
+                if (added) continue;
+            }
+
+            if (isHa || isHb) {
+                if (dist < params.maxHbondLength) {
+                    atomA[atomA.length] = _aI;
+                    atomB[atomB.length] = _bI;
+                    order[order.length] = 1;
+                    flags[flags.length] = LinkType.Flag.Covalent | LinkType.Flag.Computed; // TODO: check if correct
+                }
+                continue;
+            }
+
+            const thresholdAB = getElementPairThreshold(aeI, beI);
+            const pairingThreshold = thresholdAB > 0
+                ? thresholdAB
+                : beI < 0 ? thresholdA : Math.max(thresholdA, getElementThreshold(beI));
+
+            if (dist <= pairingThreshold) {
+                atomA[atomA.length] = _aI;
+                atomB[atomB.length] = _bI;
+                order[order.length] = 1;
+                flags[flags.length] = (isMetal ? LinkType.Flag.MetallicCoordination : LinkType.Flag.Covalent) | LinkType.Flag.Computed;
+            }
+        }
+    }
+
+    return getGraph(atomA, atomB, order, flags, atomCount);
+}
+
+function computeIntraUnitBonds(unit: Unit.Atomic, params?: Partial<LinkComputationParameters>) {
+    return _computeBonds(unit, {
+        maxHbondLength: (params && params.maxHbondLength) || 1.15,
+        forceCompute: !!(params && params.forceCompute),
+    });
+}
+
+export { computeIntraUnitBonds }

+ 5 - 4
src/mol-model/structure/structure/unit/rings/compute.ts

@@ -5,8 +5,9 @@
  */
 
 import Unit from '../../unit';
-import { IntraUnitBonds } from '../bonds/intra-data';
+import { IntraUnitLinks } from '../links/data';
 import { Segmentation } from 'mol-data/int';
+import { LinkType } from '../../../model/types';
 
 export default function computeRings(unit: Unit.Atomic) {
     const size = largestResidue(unit);
@@ -40,7 +41,7 @@ interface State {
     currentColor: number,
 
     rings: number[][],
-    bonds: IntraUnitBonds,
+    bonds: IntraUnitLinks,
     unit: Unit.Atomic
 }
 
@@ -58,7 +59,7 @@ function State(unit: Unit.Atomic, capacity: number): State {
         currentColor: 0,
         rings: [],
         unit,
-        bonds: unit.bonds
+        bonds: unit.links
     };
 }
 
@@ -160,7 +161,7 @@ function findRings(state: State, from: number) {
 
         for (let i = start; i < end; i++) {
             const b = neighbor[i];
-            if (b < startVertex || b >= endVertex || !IntraUnitBonds.isCovalent(bondFlags[i])) continue;
+            if (b < startVertex || b >= endVertex || !LinkType.isCovalent(bondFlags[i])) continue;
 
             const other = b - startVertex;
 

+ 4 - 0
src/mol-model/structure/structure/util/lookup3d.ts

@@ -17,6 +17,10 @@ export class StructureLookup3D implements Lookup3D<Element> {
     private result = Result.create<Element>();
     private pivot = Vec3.zero();
 
+    findUnitIndices(x: number, y: number, z: number, radius: number): Result<number> {
+        return this.unitLookup.find(x, y, z, radius);
+    }
+
     find(x: number, y: number, z: number, radius: number): Result<Element> {
         Result.reset(this.result);
         const { units } = this.structure;

+ 3 - 3
src/mol-view/label.ts

@@ -6,7 +6,7 @@
  */
 
 import { Unit, Element, Queries } from 'mol-model/structure';
-import { Bond } from 'mol-model/structure/structure/unit/bonds';
+import { Link } from 'mol-model/structure/structure/unit/links';
 import { Loci } from 'mol-model/loci';
 
 const elementLocA = Element.Location()
@@ -23,8 +23,8 @@ export function labelFirst(loci: Loci) {
         if (e && e.indices[0] !== undefined) {
             return elementLabel(Element.Location(e.unit, e.indices[0]))
         }
-    } else if (Bond.isLoci(loci)) {
-        const bond = loci.bonds[0]
+    } else if (Link.isLoci(loci)) {
+        const bond = loci.links[0]
         if (bond) {
             setElementLocation(elementLocA, bond.aUnit, bond.aIndex)
             setElementLocation(elementLocB, bond.bUnit, bond.bIndex)

Some files were not shown because too many files changed in this diff