diff --git a/src/mol-math/graph/int-adjacency-graph.ts b/src/mol-math/graph/int-adjacency-graph.ts index fe26bfdfd852966881ddf063bcd6c275ce27e389..73ee9a0beafe0c853afc61cfaede7707af811ee4 100644 --- a/src/mol-math/graph/int-adjacency-graph.ts +++ b/src/mol-math/graph/int-adjacency-graph.ts @@ -6,7 +6,7 @@ */ import { arrayPickIndices, cantorPairing } from 'mol-data/util'; -import { LinkedIndex } from 'mol-data/int'; +import { LinkedIndex, SortedArray } from 'mol-data/int'; /** * Represent a graph using vertex adjacency list. @@ -281,22 +281,43 @@ export namespace IntAdjacencyGraph { return { componentCount: vCount, componentIndex }; } + + /** + * Check if any vertex in `verticesA` is connected to any vertex in `verticesB` + * via at most `maxDistance` edges. + * + * Returns true if A and B are intersecting. + */ + export function areVertexSetsConnected(graph: IntAdjacencyGraph, verticesA: SortedArray<number>, verticesB: SortedArray<number>, maxDistance: number): boolean { + // check if A and B are intersecting, this handles maxDepth = 0 + if (SortedArray.areIntersecting(verticesA, verticesB)) return true; + if (maxDistance < 1) return false; + + const visited = new Set<number>(); + for (let i = 0, il = verticesA.length; i < il; ++i) { + visited.add(verticesA[i]); + } + + return areVertexSetsConnectedImpl(graph, verticesA, verticesB, maxDistance, visited); + } } -/** - * Check if any vertex in `verticesA` is connected to any vertex in `verticesB` - * via `depth` hops or intermediate vertices - */ -export function areConnected(verticesA: ReadonlyArray<number>, verticesB: ReadonlyArray<number>, graph: IntAdjacencyGraph, depth: number): boolean { - const { b, offset } = graph - const linkedVectices: number[] = [] - for (let i = 0, il = verticesA.length; i < il; ++i) { - const vi = verticesA[i] - for (let j = offset[vi], jl = offset[vi + 1]; j < jl; ++j) { - const li = b[j] - if (verticesB.includes(li)) return true - if (!verticesA.includes(li)) linkedVectices.push(li) +function areVertexSetsConnectedImpl(graph: IntAdjacencyGraph, frontier: ArrayLike<number>, target: SortedArray<number>, distance: number, visited: Set<number>): boolean { + const { b: neighbor, offset } = graph; + const newFrontier: number[] = []; + + for (let i = 0, il = frontier.length; i < il; ++i) { + const src = frontier[i]; + + for (let j = offset[src], jl = offset[src + 1]; j < jl; ++j) { + const other = neighbor[j]; + if (visited.has(other)) continue; + if (SortedArray.has(target, other)) return true; + + visited.add(other); + newFrontier[newFrontier.length] = other; } } - return depth > 0 ? areConnected(linkedVectices, verticesB, graph, depth - 1) : false + + return distance > 1 ? areVertexSetsConnectedImpl(graph, newFrontier, target, distance - 1, visited) : false; } \ No newline at end of file diff --git a/src/mol-model/structure/structure/carbohydrates/compute.ts b/src/mol-model/structure/structure/carbohydrates/compute.ts index eb441c0d1cce720a116ddeee1b79d0ce797ce86e..62ee0357f2ce9109f1897692b71e81b9c7e42cec 100644 --- a/src/mol-model/structure/structure/carbohydrates/compute.ts +++ b/src/mol-model/structure/structure/carbohydrates/compute.ts @@ -6,7 +6,7 @@ import { Segmentation } from 'mol-data/int'; import { combinations } from 'mol-data/util/combination'; -import { areConnected } from 'mol-math/graph'; +import { IntAdjacencyGraph } from 'mol-math/graph'; import { Vec3 } from 'mol-math/linear-algebra'; import PrincipalAxes from 'mol-math/linear-algebra/matrix/principal-axes'; import { fillSerial } from 'mol-util/array'; @@ -54,7 +54,7 @@ function getSugarRingIndices(unit: Unit.Atomic) { } const C = ElementSymbol('C') -function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ReadonlyArray<StructureElement.UnitIndex>, center: Vec3) { +function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ArrayLike<StructureElement.UnitIndex>, center: Vec3) { let indexC1 = -1, indexC1X = -1, indexC = -1 const { elements } = unit const { position } = unit.conformation @@ -73,8 +73,8 @@ function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ReadonlyArray } const index = indexC1 !== -1 ? indexC1 : indexC1X !== -1 ? indexC1X - : indexC !== -1 ? indexC - : elements[indices[0]] + : indexC !== -1 ? indexC + : elements[indices[0]] Vec3.normalize(direction, Vec3.sub(direction, center, position(index, direction))) return direction } @@ -163,19 +163,19 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ringElements.push(elementIndex) elementsWithRingMap.set(elementKey(residueIndex, unit.id), elementIndex) elements.push({ - geometry: { center, normal, direction, }, + geometry: { center, normal, direction }, hasRing: true, unit, residueIndex, component: saccharideComp }) } // add carbohydrate links induced by intra-residue bonds - const ringCombinations = combinations(fillSerial(new Array(sugarRings.length)), 2) + const ringCombinations = combinations(fillSerial(new Array(sugarRings.length) as number[]), 2) for (let j = 0, jl = ringCombinations.length; j < jl; ++j) { const rc = ringCombinations[j]; const r0 = rings.all[sugarRings[rc[0]]], r1 = rings.all[sugarRings[rc[1]]]; - if (areConnected(r0, r1, unit.links, 2)) { - // fix both directions as it is unlcear where the C1 atom is + if (IntAdjacencyGraph.areVertexSetsConnected(unit.links, r0, r1, 2)) { + // fix both directions as it is unclear where the C1 atom is fixLinkDirection(ringElements[rc[0]], ringElements[rc[1]]) fixLinkDirection(ringElements[rc[1]], ringElements[rc[0]]) links.push({ diff --git a/src/mol-model/structure/structure/unit/rings.ts b/src/mol-model/structure/structure/unit/rings.ts index 0d5437773aa953568cd391a1279c3864be29b353..83640e22689d6f688704c2bb4866439ba6c1a04c 100644 --- a/src/mol-model/structure/structure/unit/rings.ts +++ b/src/mol-model/structure/structure/unit/rings.ts @@ -7,8 +7,9 @@ import { computeRings, getFingerprint, createIndex } from './rings/compute' import Unit from '../unit'; import StructureElement from '../element'; +import { SortedArray } from 'mol-data/int'; -type UnitRing = ReadonlyArray<StructureElement.UnitIndex> +type UnitRing = SortedArray<StructureElement.UnitIndex> interface UnitRings { /** Each ring is specified as an array of indices in Unit.elements. */ diff --git a/src/mol-model/structure/structure/unit/rings/compute.ts b/src/mol-model/structure/structure/unit/rings/compute.ts index a6d5da313c4492df5e1814f522717409834c554a..5485b49809e1c075944a70ecadd08c54ad85f57f 100644 --- a/src/mol-model/structure/structure/unit/rings/compute.ts +++ b/src/mol-model/structure/structure/unit/rings/compute.ts @@ -4,12 +4,13 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Segmentation } from 'mol-data/int'; +import { Segmentation, SortedArray } from 'mol-data/int'; import { IntAdjacencyGraph } from 'mol-math/graph'; import { LinkType } from '../../../model/types'; import { StructureElement } from '../../../structure'; import Unit from '../../unit'; import { IntraUnitLinks } from '../links/data'; +import { sortArray } from 'mol-data/util'; export function computeRings(unit: Unit.Atomic) { const size = largestResidue(unit); @@ -42,7 +43,7 @@ interface State { currentColor: number, - rings: StructureElement.UnitIndex[][], + rings: SortedArray<StructureElement.UnitIndex>[], bonds: IntraUnitLinks, unit: Unit.Atomic } @@ -146,7 +147,8 @@ function addRing(state: State, a: number, b: number) { for (let t = 0; t < leftOffset; t++) ring[ringOffset++] = state.startVertex + left[t]; for (let t = rightOffset - 1; t >= 0; t--) ring[ringOffset++] = state.startVertex + right[t]; - state.rings.push(ring as any as StructureElement.UnitIndex[]); + sortArray(ring); + state.rings.push(SortedArray.ofSortedArray(ring)); } function findRings(state: State, from: number) { @@ -247,7 +249,7 @@ function buildFinderprint(elements: string[], offset: number) { type RingIndex = import('../rings').UnitRings.Index type RingComponentIndex = import('../rings').UnitRings.ComponentIndex -export function createIndex(rings: StructureElement.UnitIndex[][]) { +export function createIndex(rings: SortedArray<StructureElement.UnitIndex>[]) { const elementRingIndices: Map<StructureElement.UnitIndex, RingIndex[]> = new Map(); // for each ring atom, assign all rings that it is present in