Browse Source

generalized InterUnitBonds to InterUnitGraph

Alexander Rose 5 years ago
parent
commit
88d8998bd2

+ 4 - 4
src/apps/structure-info/model.ts

@@ -92,13 +92,13 @@ export function printLinks(structure: Structure, showIntra: boolean, showInter:
             if (!Unit.isAtomic(unit)) continue;
 
             for (const pairLinks of links.getLinkedUnits(unit)) {
-                if (!pairLinks.areUnitsOrdered || pairLinks.bondCount === 0) continue;
+                if (!pairLinks.areUnitsOrdered || pairLinks.edgeCount === 0) continue;
 
                 const { unitA, unitB } = pairLinks;
-                console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.bondCount} bond(s)`);
+                console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.edgeCount} bond(s)`);
 
-                for (const aI of pairLinks.linkedElementIndices) {
-                    for (const link of pairLinks.getBonds(aI)) {
+                for (const aI of pairLinks.connectedIndices) {
+                    for (const link of pairLinks.getEdges(aI)) {
                         console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[link.indexB])}`);
                     }
                 }

+ 128 - 0
src/mol-math/graph/inter-unit-graph.ts

@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2017-2019 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>
+ */
+
+export { InterUnitGraph }
+
+class InterUnitGraph<Unit extends InterUnitGraph.UnitBase, VertexIndex extends number, EdgeProps extends InterUnitGraph.EdgePropsBase = {}> {
+    /** Number of inter-unit edges */
+    readonly edgeCount: number
+    /** Array of inter-unit edges */
+    readonly edges: ReadonlyArray<InterUnitGraph.Edge<Unit, VertexIndex, EdgeProps>>
+    private readonly edgeKeyIndex: Map<string, number>
+    private readonly vertexKeyIndex: Map<string, number[]>
+
+    /** Get an array of unit-pair-edges that are linked to the given unit */
+    getLinkedUnits(unit: Unit): ReadonlyArray<InterUnitGraph.UnitPairEdges<Unit, VertexIndex, EdgeProps>> {
+        if (!this.map.has(unit.id)) return emptyArray;
+        return this.map.get(unit.id)!;
+    }
+
+    /** Index into this.edges */
+    getEdgeIndex(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit): number {
+        const edgeKey = InterUnitGraph.getEdgeKey<Unit, VertexIndex>(indexA, unitA, indexB, unitB)
+        const index = this.edgeKeyIndex.get(edgeKey)
+        return index !== undefined ? index : -1
+    }
+
+    /** Check if edge exists */
+    hasEdge(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit): boolean {
+        return this.getEdgeIndex(indexA, unitA, indexB, unitB) !== -1
+    }
+
+    /** Get inter-unit edge given a pair of indices and units */
+    getEdge(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit): InterUnitGraph.Edge<Unit, VertexIndex, EdgeProps> | undefined {
+        const index = this.getEdgeIndex(indexA, unitA, indexB, unitB)
+        return index !== -1 ? this.edges[index] : undefined
+    }
+
+    /** Indices into this.edges */
+    getEdgeIndices(index: VertexIndex, unit: Unit): ReadonlyArray<number> {
+        return this.vertexKeyIndex.get(InterUnitGraph.getVertexKey(index, unit)) || []
+    }
+
+    constructor(private map: Map<number, InterUnitGraph.UnitPairEdges<Unit, VertexIndex, EdgeProps>[]>) {
+        let count = 0
+        const edges: (InterUnitGraph.Edge<Unit, VertexIndex, EdgeProps>)[] = []
+        const edgeKeyIndex = new Map<string, number>()
+        const elementKeyIndex = new Map<string, number[]>()
+
+        this.map.forEach(pairEdgesArray => {
+            pairEdgesArray.forEach(pairEdges => {
+                count += pairEdges.edgeCount
+                pairEdges.connectedIndices.forEach(indexA => {
+                    pairEdges.getEdges(indexA).forEach(edgeInfo => {
+                        const { unitA, unitB } = pairEdges
+
+                        const edgeKey = InterUnitGraph.getEdgeKey<Unit, VertexIndex>(indexA, unitA, edgeInfo.indexB, unitB)
+                        edgeKeyIndex.set(edgeKey, edges.length)
+
+                        const elementKey = InterUnitGraph.getVertexKey(indexA, unitA)
+                        const e = elementKeyIndex.get(elementKey)
+                        if (e === undefined) elementKeyIndex.set(elementKey, [edges.length])
+                        else e.push(edges.length)
+
+                        edges.push({ ...edgeInfo, indexA, unitA, unitB })
+                    })
+                })
+            })
+        })
+
+        this.edgeCount = count
+        this.edges = edges
+        this.edgeKeyIndex = edgeKeyIndex
+        this.vertexKeyIndex = elementKeyIndex
+    }
+}
+
+namespace InterUnitGraph {
+    export class UnitPairEdges<Unit extends UnitBase, VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
+        hasEdges(indexA: VertexIndex) {
+            return this.edgeMap.has(indexA);
+        }
+
+        getEdges(indexA: VertexIndex): ReadonlyArray<EdgeInfo<VertexIndex, EdgeProps>> {
+            if (!this.edgeMap.has(indexA)) return emptyArray;
+            return this.edgeMap.get(indexA)!;
+        }
+
+        get areUnitsOrdered() {
+            return this.unitA.id < this.unitB.id;
+        }
+
+        constructor(public unitA: Unit, public unitB: Unit,
+            public edgeCount: number, public connectedIndices: ReadonlyArray<VertexIndex>,
+            private edgeMap: Map<number, EdgeInfo<VertexIndex, EdgeProps>[]>) {
+        }
+    }
+
+    export type UnitBase = { id: number }
+    export type EdgePropsBase = { [name: string]: any }
+
+    export interface EdgeInfo<VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
+        /** indexInto */
+        readonly indexB: VertexIndex,
+        readonly props: EdgeProps
+    }
+
+    export interface Edge<Unit extends UnitBase, VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
+        readonly unitA: Unit,
+        readonly unitB: Unit,
+        readonly indexA: VertexIndex,
+        readonly indexB: VertexIndex,
+        readonly props: EdgeProps
+    }
+
+    export function getEdgeKey<Unit extends UnitBase, VertexIndex extends number>(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit) {
+        return `${indexA}|${unitA.id}|${indexB}|${unitB.id}`
+    }
+
+    export function getVertexKey<Unit extends UnitBase, VertexIndex extends number>(index: VertexIndex, unit: Unit) {
+        return `${index}|${unit.id}`
+    }
+}
+
+const emptyArray: any[] = [];

+ 3 - 3
src/mol-model-props/computed/chemistry/util.ts

@@ -31,7 +31,7 @@ export function compId(unit: Unit.Atomic, index: StructureElement.UnitIndex) {
 //
 
 export function interBondCount(structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex): number {
-    return structure.interUnitBonds.getBondIndices(index, unit).length
+    return structure.interUnitBonds.getEdgeIndices(index, unit).length
 }
 
 export function intraBondCount(unit: Unit.Atomic, index: StructureElement.UnitIndex): number {
@@ -65,9 +65,9 @@ export function intraConnectedTo(unit: Unit.Atomic, indexA: StructureElement.Uni
 
 export function eachInterBondedAtom(structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex, cb: (unit: Unit.Atomic, index: StructureElement.UnitIndex) => void): void {
     // inter
-    const interIndices = structure.interUnitBonds.getBondIndices(index, unit)
+    const interIndices = structure.interUnitBonds.getEdgeIndices(index, unit)
     for (let i = 0, il = interIndices.length; i < il; ++i) {
-        const b = structure.interUnitBonds.bonds[i]
+        const b = structure.interUnitBonds.edges[i]
         cb(b.unitB, b.indexB)
     }
 }

+ 1 - 1
src/mol-model-props/computed/chemistry/valence-model.ts

@@ -68,7 +68,7 @@ export function explicitValence (structure: Structure, unit: Unit.Atomic, index:
     const { offset, edgeProps } = unit.links
     for (let i = offset[index], il = offset[index + 1]; i < il; ++i) v += edgeProps.order[i]
     // inter-unit bonds
-    structure.interUnitBonds.getBondIndices(index, unit).forEach(b => v += structure.interUnitBonds.bonds[b].order)
+    structure.interUnitBonds.getEdgeIndices(index, unit).forEach(b => v += structure.interUnitBonds.edges[b].props.order)
     return v
 }
 

+ 3 - 3
src/mol-model/structure/query/queries/filters.ts

@@ -277,7 +277,7 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
             for (let li = 0; li < luCount; li++) {
                 const lu = linkedUnits[li];
                 const bElements = lu.unitB.elements;
-                const bonds = lu.getBonds(inputIndex);
+                const bonds = lu.getEdges(inputIndex);
                 for (let bi = 0, _bi = bonds.length; bi < _bi; bi++) {
                     const bond = bonds[bi];
                     atomicLink.b.unit = lu.unitB;
@@ -290,8 +290,8 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
                     atomicLink.a.element = srcElements[i];
 
                     atomicLink.bIndex = bond.indexB;
-                    atomicLink.type = bond.flag;
-                    atomicLink.order = bond.order;
+                    atomicLink.type = bond.props.flag;
+                    atomicLink.order = bond.props.order;
                     if (atomicLink.test(queryCtx, true)) return true;
                 }
             }

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

@@ -319,15 +319,15 @@ export function linkedAtomicPairs(linkTest?: QueryPredicate): StructureQuery {
         }
 
         // Process inter unit links
-        for (const bond of interLinks.bonds) {
+        for (const bond of interLinks.edges) {
             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;
+            atomicLink.order = bond.props.order;
+            atomicLink.type = bond.props.flag;
 
             // No need to "swap test" because each bond direction will be visited eventually.
             if (atomicLink.test(ctx, false)) {

+ 4 - 4
src/mol-model/structure/query/queries/modifiers.ts

@@ -390,11 +390,11 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
             if (visitedSourceUnits.has(linkedUnit.unitB.id)) continue;
             const currentUnitB = structure.unitMap.get(linkedUnit.unitB.id);
 
-            for (const aI of linkedUnit.linkedElementIndices) {
+            for (const aI of linkedUnit.connectedIndices) {
                 // check if the element is in the expanded structure
                 if (!SortedArray.has(unit.elements, inputUnitA.elements[aI])) continue;
 
-                for (const bond of linkedUnit.getBonds(aI)) {
+                for (const bond of linkedUnit.getEdges(aI)) {
                     const bElement = linkedUnit.unitB.elements[bond.indexB];
 
                     // Check if the element is already present:
@@ -406,8 +406,8 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
                     atomicLink.b.unit = linkedUnit.unitB;
                     atomicLink.bIndex = bond.indexB;
                     atomicLink.b.element = bElement;
-                    atomicLink.type = bond.flag;
-                    atomicLink.order = bond.order;
+                    atomicLink.type = bond.props.flag;
+                    atomicLink.order = bond.props.order;
 
                     if (atomicLink.test(ctx, true)) {
                         builder.addToUnit(linkedUnit.unitB.id, bElement);

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

@@ -277,8 +277,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
         if (!Unit.isAtomic(unit)) continue
 
         structure.interUnitBonds.getLinkedUnits(unit).forEach(pairBonds => {
-            pairBonds.linkedElementIndices.forEach(indexA => {
-                pairBonds.getBonds(indexA).forEach(bondInfo => {
+            pairBonds.connectedIndices.forEach(indexA => {
+                pairBonds.getEdges(indexA).forEach(bondInfo => {
                     const { unitA, unitB } = pairBonds
                     const indexB = bondInfo.indexB
                     const ringElementIndexA = getRingElementIndex(unitA, indexA)

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

@@ -91,7 +91,7 @@ class Structure {
     /** Count of all bonds (intra- and inter-unit) in the structure */
     get bondCount() {
         if (!this._props.bondCount) {
-            this._props.bondCount = this.interUnitBonds.bondCount + Link.getIntraUnitBondCount(this)
+            this._props.bondCount = this.interUnitBonds.edgeCount + Link.getIntraUnitBondCount(this)
         }
         return this._props.bondCount;
     }

+ 6 - 6
src/mol-model/structure/structure/unit/links.ts

@@ -124,7 +124,7 @@ namespace Link {
             return LinkType.create(links.edgeProps.flags[idx]);
         } else {
             const bond = structure.interUnitBonds.getBondFromLocation(link);
-            if (bond) return LinkType.create(bond.flag);
+            if (bond) return LinkType.create(bond.props.flag);
             return LinkType.create(LinkType.Flag.None);
         }
     }
@@ -137,7 +137,7 @@ namespace Link {
             return links.edgeProps.order[idx];
         } else {
             const bond = structure.interUnitBonds.getBondFromLocation(link);
-            if (bond) return bond.order;
+            if (bond) return bond.props.order;
             return 0;
         }
     }
@@ -183,7 +183,7 @@ namespace Link {
             this.unit = unit
             this.index = index
 
-            this.interBondIndices = structure.interUnitBonds.getBondIndices(index, unit)
+            this.interBondIndices = structure.interUnitBonds.getEdgeIndices(index, unit)
             this.interBondCount = this.interBondIndices.length
             this.interBondIndex = 0
 
@@ -201,11 +201,11 @@ namespace Link {
                 this.current.order = this.unit.links.edgeProps.order[this.intraBondIndex]
                 this.intraBondIndex += 1
             } else if (this.interBondIndex < this.interBondCount) {
-                const b = this.structure.interUnitBonds.bonds[this.interBondIndex]
+                const b = this.structure.interUnitBonds.edges[this.interBondIndex]
                 this.current.otherUnit = b.unitA !== this.unit ? b.unitA : b.unitB
                 this.current.otherIndex = b.indexA !== this.index ? b.indexA : b.indexB
-                this.current.type = b.flag
-                this.current.order = b.order
+                this.current.type = b.props.flag
+                this.current.order = b.props.order
                 this.interBondIndex += 1
             } else {
                 this.hasNext = false

+ 9 - 113
src/mol-model/structure/structure/unit/links/data.ts

@@ -10,6 +10,7 @@ import { IntAdjacencyGraph } from '../../../../../mol-math/graph';
 import Unit from '../../unit';
 import StructureElement from '../../element';
 import { Link } from '../links';
+import { InterUnitGraph } from '../../../../../mol-math/graph/inter-unit-graph';
 
 type IntraUnitLinks = IntAdjacencyGraph<{ readonly order: ArrayLike<number>, readonly flags: ArrayLike<LinkType.Flag> }>
 
@@ -17,128 +18,23 @@ namespace IntraUnitLinks {
     export const Empty: IntraUnitLinks = IntAdjacencyGraph.create([], [], [], 0, { flags: [], order: [] });
 }
 
-class InterUnitBonds {
-    /** Number of inter-unit bonds */
-    readonly bondCount: number
-    /** Array of inter-unit bonds */
-    readonly bonds: ReadonlyArray<InterUnitBonds.Bond>
-    private readonly bondKeyIndex: Map<string, number>
-    private readonly elementKeyIndex: Map<string, number[]>
-
-    /** Get an array of unit-pair-bonds that are linked to the given unit */
-    getLinkedUnits(unit: Unit): ReadonlyArray<InterUnitBonds.UnitPairBonds> {
-        if (!this.map.has(unit.id)) return emptyArray;
-        return this.map.get(unit.id)!;
-    }
-
-    /** Index into this.bonds */
-    getBondIndex(indexA: StructureElement.UnitIndex, unitA: Unit, indexB: StructureElement.UnitIndex, unitB: Unit): number {
-        const bondKey = InterUnitBonds.getBondKey(indexA, unitA, indexB, unitB)
-        const index = this.bondKeyIndex.get(bondKey)
-        return index !== undefined ? index : -1
-    }
-
-    /** Check if bond exists */
-    hasBond(indexA: StructureElement.UnitIndex, unitA: Unit, indexB: StructureElement.UnitIndex, unitB: Unit): boolean {
-        return this.getBondIndex(indexA, unitA, indexB, unitB) !== -1
-    }
-
-    /** Get inter-unit bond given a pair of indices and units */
-    getBond(indexA: StructureElement.UnitIndex, unitA: Unit, indexB: StructureElement.UnitIndex, unitB: Unit): InterUnitBonds.Bond | undefined {
-        const index = this.getBondIndex(indexA, unitA, indexB, unitB)
-        return index !== -1 ? this.bonds[index] : undefined
-    }
+type InterUnitEdgeProps = { readonly order: number, readonly flag: LinkType.Flag }
 
+class InterUnitBonds extends InterUnitGraph<Unit.Atomic, StructureElement.UnitIndex, InterUnitEdgeProps> {
     /** Get inter-unit bond given a link-location */
     getBondFromLocation(l: Link.Location) {
-        return this.getBond(l.aIndex, l.aUnit, l.bIndex, l.bUnit);
-    }
-
-    /** Indices into this.bonds */
-    getBondIndices(index: StructureElement.UnitIndex, unit: Unit): ReadonlyArray<number> {
-        return this.elementKeyIndex.get(InterUnitBonds.getElementKey(index, unit)) || []
+        return Unit.isAtomic(l.aUnit) && Unit.isAtomic(l.bUnit) ? this.getEdge(l.aIndex, l.aUnit, l.bIndex, l.bUnit) : undefined;
     }
 
-    constructor(private map: Map<number, InterUnitBonds.UnitPairBonds[]>) {
-        let count = 0
-        const bonds: (InterUnitBonds.Bond)[] = []
-        const bondKeyIndex = new Map<string, number>()
-        const elementKeyIndex = new Map<string, number[]>()
-
-        this.map.forEach(pairBondsArray => {
-            pairBondsArray.forEach(pairBonds => {
-                count += pairBonds.bondCount
-                pairBonds.linkedElementIndices.forEach(indexA => {
-                    pairBonds.getBonds(indexA).forEach(bondInfo => {
-                        const { unitA, unitB } = pairBonds
-
-                        const bondKey = InterUnitBonds.getBondKey(indexA, unitA, bondInfo.indexB, unitB)
-                        bondKeyIndex.set(bondKey, bonds.length)
-
-                        const elementKey = InterUnitBonds.getElementKey(indexA, unitA)
-                        const e = elementKeyIndex.get(elementKey)
-                        if (e === undefined) elementKeyIndex.set(elementKey, [bonds.length])
-                        else e.push(bonds.length)
-
-                        bonds.push({ ...bondInfo, indexA, unitA, unitB })
-                    })
-                })
-            })
-        })
-
-        this.bondCount = count
-        this.bonds = bonds
-        this.bondKeyIndex = bondKeyIndex
-        this.elementKeyIndex = elementKeyIndex
+    /** Get inter-unit bond index given a link-location */
+    getBondIndexFromLocation(l: Link.Location) {
+        return Unit.isAtomic(l.aUnit) && Unit.isAtomic(l.bUnit) ? this.getEdgeIndex(l.aIndex, l.aUnit, l.bIndex, l.bUnit) : -1;
     }
 }
 
 namespace InterUnitBonds {
-    export class UnitPairBonds {
-        hasBonds(indexA: StructureElement.UnitIndex) {
-            return this.linkMap.has(indexA);
-        }
-
-        getBonds(indexA: StructureElement.UnitIndex): 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<StructureElement.UnitIndex>,
-            private linkMap: Map<number, BondInfo[]>) {
-        }
-    }
-
-    export interface BondInfo {
-        /** indexInto */
-        readonly indexB: StructureElement.UnitIndex,
-        readonly order: number,
-        readonly flag: LinkType.Flag
-    }
-
-    export interface Bond {
-        readonly unitA: Unit.Atomic,
-        readonly unitB: Unit.Atomic,
-        readonly indexA: StructureElement.UnitIndex,
-        readonly indexB: StructureElement.UnitIndex,
-        readonly order: number,
-        readonly flag: LinkType.Flag
-    }
-
-    export function getBondKey(indexA: StructureElement.UnitIndex, unitA: Unit, indexB: StructureElement.UnitIndex, unitB: Unit) {
-        return `${indexA}|${unitA.id}|${indexB}|${unitB.id}`
-    }
-
-    export function getElementKey(index: StructureElement.UnitIndex, unit: Unit) {
-        return `${index}|${unit.id}`
-    }
+    export class UnitPairBonds extends InterUnitGraph.UnitPairEdges<Unit.Atomic, StructureElement.UnitIndex, InterUnitEdgeProps> {}
+    export type BondInfo = InterUnitGraph.EdgeInfo<StructureElement.UnitIndex, InterUnitEdgeProps>
 }
 
-const emptyArray: any[] = [];
-
 export { IntraUnitLinks, InterUnitBonds }

+ 7 - 6
src/mol-model/structure/structure/unit/links/inter-compute.ts

@@ -32,9 +32,10 @@ interface PairState {
     bondedB: UniqueArray<StructureElement.UnitIndex, StructureElement.UnitIndex>
 }
 
-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 });
+function addLink(indexA: StructureElement.UnitIndex, indexB: StructureElement.UnitIndex, order: number, flag: LinkType.Flag, state: PairState) {
+    addMapEntry(state.mapAB, indexA, { indexB, props: { order, flag } });
+    addMapEntry(state.mapBA, indexB, { indexB: indexA, props: { order, flag } });
+
     UniqueArray.add(state.bondedA, indexA, indexA);
     UniqueArray.add(state.bondedB, indexB, indexB);
 }
@@ -77,7 +78,7 @@ function findPairLinks(unitA: Unit.Atomic, unitB: Unit.Atomic, props: LinkComput
     const { center: bCenter, radius: bRadius } = lookup3d.boundary.sphere;
     const testDistanceSq = (bRadius + MAX_RADIUS) * (bRadius + MAX_RADIUS);
 
-    for (let _aI = 0; _aI < atomCount; _aI++) {
+    for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
         const aI = atomsA[_aI];
         Vec3.set(imageA, xA[aI], yA[aI], zA[aI]);
         if (isNotIdentity) Vec3.transformMat4(imageA, imageA, imageTransform);
@@ -88,7 +89,7 @@ function findPairLinks(unitA: Unit.Atomic, unitB: Unit.Atomic, props: LinkComput
             let added = false;
             for (const se of structConnEntries) {
                 for (const p of se.partners) {
-                    const _bI = SortedArray.indexOf(unitB.elements, p.atomIndex);
+                    const _bI = SortedArray.indexOf(unitB.elements, p.atomIndex) as StructureElement.UnitIndex;
                     if (_bI < 0) continue;
                     // check if the bond is within MAX_RADIUS for this pair of units
                     if (getDistance(unitA, aI, unitB, p.atomIndex) > MAX_RADIUS) continue;
@@ -114,7 +115,7 @@ function findPairLinks(unitA: Unit.Atomic, unitB: Unit.Atomic, props: LinkComput
         const compIdA = label_comp_idA.value(residueIndexA[aI]);
 
         for (let ni = 0; ni < count; ni++) {
-            const _bI = indices[ni];
+            const _bI = indices[ni] as StructureElement.UnitIndex;
             const bI = atomsB[_bI];
 
             const altB = label_alt_idB.value(bI);

+ 17 - 15
src/mol-repr/structure/visual/inter-unit-link-cylinder.ts

@@ -35,15 +35,15 @@ const tmpLoc = StructureElement.Location.create()
 
 function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitLinkParams>, mesh?: Mesh) {
     const links = structure.interUnitBonds
-    const { bondCount, bonds } = links
+    const { edgeCount, edges } = links
     const { sizeFactor, sizeAspectRatio, ignoreHydrogens } = props
 
-    if (!bondCount) return Mesh.createEmpty(mesh)
+    if (!edgeCount) return Mesh.createEmpty(mesh)
 
     const builderProps = {
-        linkCount: bondCount,
+        linkCount: edgeCount,
         referencePosition: (edgeIndex: number) => {
-            const b = bonds[edgeIndex]
+            const b = edges[edgeIndex]
             let unitA: Unit, unitB: Unit
             let indexA: StructureElement.UnitIndex, indexB: StructureElement.UnitIndex
             if (b.unitA.id < b.unitB.id) {
@@ -58,15 +58,15 @@ function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structur
             return setRefPosition(tmpRef, structure, unitA, indexA) || setRefPosition(tmpRef, structure, unitB, indexB)
         },
         position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
-            const b = bonds[edgeIndex]
+            const b = edges[edgeIndex]
             const uA = b.unitA, uB = b.unitB
             uA.conformation.position(uA.elements[b.indexA], posA)
             uB.conformation.position(uB.elements[b.indexB], posB)
         },
-        order: (edgeIndex: number) => bonds[edgeIndex].order,
-        flags: (edgeIndex: number) => BitFlags.create(bonds[edgeIndex].flag),
+        order: (edgeIndex: number) => edges[edgeIndex].props.order,
+        flags: (edgeIndex: number) => BitFlags.create(edges[edgeIndex].props.flag),
         radius: (edgeIndex: number) => {
-            const b = bonds[edgeIndex]
+            const b = edges[edgeIndex]
             tmpLoc.unit = b.unitA
             tmpLoc.element = b.unitA.elements[b.indexA]
             const sizeA = theme.size.size(tmpLoc)
@@ -76,7 +76,7 @@ function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structur
             return Math.min(sizeA, sizeB) * sizeFactor * sizeAspectRatio
         },
         ignore: ignoreHydrogens ? (edgeIndex: number) => {
-            const b = bonds[edgeIndex]
+            const b = edges[edgeIndex]
             const uA = b.unitA, uB = b.unitB
             return isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB])
         } : () => false
@@ -118,7 +118,7 @@ export function InterUnitLinkVisual(materialId: number): ComplexVisual<InterUnit
 function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
     const { objectId, groupId } = pickingId
     if (id === objectId) {
-        const bond = structure.interUnitBonds.bonds[groupId]
+        const bond = structure.interUnitBonds.edges[groupId]
         return Link.Loci(structure, [
             Link.Location(
                 bond.unitA, bond.indexA as StructureElement.UnitIndex,
@@ -138,7 +138,7 @@ function eachLink(loci: Loci, structure: Structure, apply: (interval: Interval)
     if (Link.isLoci(loci)) {
         if (!Structure.areEquivalent(loci.structure, structure)) return false
         for (const b of loci.links) {
-            const idx = structure.interUnitBonds.getBondIndex(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
+            const idx = structure.interUnitBonds.getBondIndexFromLocation(b)
             if (idx !== -1) {
                 if (apply(Interval.ofSingleton(idx))) changed = true
             }
@@ -151,14 +151,16 @@ function eachLink(loci: Loci, structure: Structure, apply: (interval: Interval)
         for (const e of loci.elements) map.set(e.unit.id, e.indices)
 
         for (const e of loci.elements) {
-            structure.interUnitBonds.getLinkedUnits(e.unit).forEach(b => {
+            const { unit } = e
+            if (!Unit.isAtomic(unit)) continue
+            structure.interUnitBonds.getLinkedUnits(unit).forEach(b => {
                 const otherLociIndices = map.get(b.unitB.id)
                 if (otherLociIndices) {
                     OrderedSet.forEach(e.indices, v => {
-                        if (!b.linkedElementIndices.includes(v)) return
-                        b.getBonds(v).forEach(bi => {
+                        if (!b.connectedIndices.includes(v)) return
+                        b.getEdges(v).forEach(bi => {
                             if (OrderedSet.has(otherLociIndices, bi.indexB)) {
-                                const idx = structure.interUnitBonds.getBondIndex(v, e.unit, bi.indexB, b.unitB)
+                                const idx = structure.interUnitBonds.getEdgeIndex(v, unit, bi.indexB, b.unitB)
                                 if (apply(Interval.ofSingleton(idx))) changed = true
                             }
                         })

+ 2 - 2
src/mol-repr/structure/visual/util/link.ts

@@ -144,11 +144,11 @@ export namespace LinkIterator {
     }
 
     export function fromStructure(structure: Structure): LocationIterator {
-        const groupCount = structure.interUnitBonds.bondCount
+        const groupCount = structure.interUnitBonds.edgeCount
         const instanceCount = 1
         const location = Link.Location()
         const getLocation = (groupIndex: number) => {
-            const bond = structure.interUnitBonds.bonds[groupIndex]
+            const bond = structure.interUnitBonds.edges[groupIndex]
             location.aUnit = bond.unitA
             location.aIndex = bond.indexA as StructureElement.UnitIndex
             location.bUnit = bond.unitB