diff --git a/src/mol-model-formats/structure/mmcif/bonds/index-pair.ts b/src/mol-model-formats/structure/mmcif/bonds/index-pair.ts new file mode 100644 index 0000000000000000000000000000000000000000..484d119ad0d8949c23213440c786a399d5826d89 --- /dev/null +++ b/src/mol-model-formats/structure/mmcif/bonds/index-pair.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2019 Mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Model } from '../../../../mol-model/structure/model/model' +import { CustomPropertyDescriptor } from '../../../../mol-model/structure'; +import { IntAdjacencyGraph } from '../../../../mol-math/graph'; +import { Column } from '../../../../mol-data/db'; + +export type IndexPairBonds = IntAdjacencyGraph<number, { readonly order: ArrayLike<number> }> + +function getGraph(indexA: ArrayLike<number>, indexB: ArrayLike<number>, _order: ArrayLike<number>, count: number): IndexPairBonds { + const builder = new IntAdjacencyGraph.EdgeBuilder(count, indexA, indexB); + const order = new Int8Array(builder.slotCount); + for (let i = 0, _i = builder.edgeCount; i < _i; i++) { + builder.addNextEdge(); + builder.assignProperty(order, _order[i]); + } + + return builder.createGraph({ order }); +} + +export namespace IndexPairBonds { + export const Descriptor: CustomPropertyDescriptor = { + isStatic: true, + name: 'index_pair_bonds', + } + + export type Data = { + pairs: { + indexA: Column<number>, + indexB: Column<number> + order: Column<number> + }, + count: number + } + + export function attachFromData(model: Model, data: Data): boolean { + if (model.customProperties.has(Descriptor)) return true; + + model.customProperties.add(Descriptor); + model._staticPropertyData.__IndexPairBondsData__ = data; + return true; + } + + function getIndexPairBonds(model: Model) { + return model._staticPropertyData.__IndexPairBondsData__ as Data; + } + + export const PropName = '__IndexPairBonds__'; + export function get(model: Model): IndexPairBonds | undefined { + if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]; + if (!model.customProperties.has(Descriptor)) return void 0; + + const data = getIndexPairBonds(model); + if (!data) return void 0; + const { pairs, count } = data + + const indexA = pairs.indexA.toArray() + const indexB = pairs.indexB.toArray() + const order = pairs.order.toArray() + + const indexPairBonds = getGraph(indexA, indexB, order, count); + model._staticPropertyData[PropName] = indexPairBonds; + return indexPairBonds; + } +} \ No newline at end of file diff --git a/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts b/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts index b29c31dcee71bd7d5d0c0bc2d40896a0929fed7e..e32e4ba106236c757ece73241530e20d313d809b 100644 --- a/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts +++ b/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts @@ -118,7 +118,6 @@ export namespace StructConn { return this._atomIndex; } - getResidueEntries(residueAIndex: ResidueIndex, residueBIndex: ResidueIndex): ReadonlyArray<StructConn.Entry> { return this.getResiduePairIndex().get(_resKey(residueAIndex, residueBIndex)) || _emptyEntry; } diff --git a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts index 5fcf3ad9df65fa84eb4e37f9c9cb7230f0cd7bbb..8f456b97b7987746b660bb64c5b6b0c62919930e 100644 --- a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts @@ -17,6 +17,7 @@ import StructureElement from '../../element'; import { StructConn } from '../../../../../mol-model-formats/structure/mmcif/bonds'; import { ElementIndex } from '../../../model/indexing'; import { getInterBondOrderFromTable } from '../../../model/properties/atomic/bonds'; +import { IndexPairBonds } from '../../../../../mol-model-formats/structure/mmcif/bonds/index-pair'; const MAX_RADIUS = 4; @@ -68,6 +69,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput const { lookup3d } = unitB; const structConn = unitA.model === unitB.model && unitA.model.sourceData.kind === 'mmCIF' ? StructConn.get(unitA.model) : void 0; + const indexPairs = unitA.model === unitB.model ? IndexPairBonds.get(unitA.model) : void 0; // the lookup queries need to happen in the "unitB space". // that means imageA = inverseOperB(operA(aI)) @@ -84,6 +86,16 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput if (isNotIdentity) Vec3.transformMat4(imageA, imageA, imageTransform); if (Vec3.squaredDistance(imageA, bCenter) > testDistanceSq) continue; + if (!props.forceCompute && indexPairs) { + for (let i = indexPairs.offset[aI], il = indexPairs.offset[aI + 1]; i < il; ++i) { + const _bI = SortedArray.indexOf(unitA.elements, indexPairs.b[i]) as StructureElement.UnitIndex; + if (_bI < 0) continue; + addBond(_aI, _bI, indexPairs.edgeProps.order[i], BondType.Flag.Covalent, state); + bondCount++; + } + continue // assume `indexPairs` supplies all bonds + } + const structConnEntries = props.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI); if (structConnEntries && structConnEntries.length) { let added = false; diff --git a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts index 0746684ce5ce0707263a8f9bc4dc07f44813fb96..0a3c5f40cbe9d8124946791e9647ee988768ce44 100644 --- a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts @@ -14,6 +14,7 @@ import { SortedArray } from '../../../../../mol-data/int'; import { StructConn, ComponentBond } from '../../../../../mol-model-formats/structure/mmcif/bonds'; import { getIntraBondOrderFromTable } from '../../../model/properties/atomic/bonds'; import StructureElement from '../../element'; +import { IndexPairBonds } from '../../../../../mol-model-formats/structure/mmcif/bonds/index-pair'; function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], atomCount: number): IntraUnitBonds { const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB); @@ -40,6 +41,7 @@ function _computeBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUni const structConn = unit.model.sourceData.kind === 'mmCIF' ? StructConn.get(unit.model) : void 0; const component = unit.model.sourceData.kind === 'mmCIF' ? ComponentBond.get(unit.model) : void 0; + const indexPairs = IndexPairBonds.get(unit.model) const atomA: StructureElement.UnitIndex[] = []; const atomB: StructureElement.UnitIndex[] = []; @@ -51,6 +53,33 @@ function _computeBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUni for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) { const aI = atoms[_aI]; + + if (!props.forceCompute && indexPairs) { + for (let i = indexPairs.offset[aI], il = indexPairs.offset[aI + 1]; i < il; ++i) { + const _bI = SortedArray.indexOf(unit.elements, indexPairs.b[i]) as StructureElement.UnitIndex; + if (_bI < 0) continue; + atomA[atomA.length] = _aI; + atomB[atomB.length] = _bI; + order[order.length] = indexPairs.edgeProps.order[i]; + flags[flags.length] = BondType.Flag.Covalent; + } + continue // assume `indexPairs` supplies all bonds + } + + const structConnEntries = props.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI); + if (structConnEntries) { + for (const se of structConnEntries) { + for (const p of se.partners) { + const _bI = SortedArray.indexOf(unit.elements, p.atomIndex) as StructureElement.UnitIndex; + if (_bI < 0) continue; + atomA[atomA.length] = _aI; + atomB[atomB.length] = _bI; + flags[flags.length] = se.flags; + order[order.length] = se.order; + } + } + } + const raI = residueIndex[aI]; const compId = label_comp_id.value(raI); @@ -72,22 +101,6 @@ function _computeBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUni const thresholdA = getElementThreshold(aeI); const altA = label_alt_id.value(aI); const metalA = MetalsSet.has(aeI); - const structConnEntries = props.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI); - - if (structConnEntries) { - for (const se of structConnEntries) { - if (se.distance > MAX_RADIUS) continue; - - for (const p of se.partners) { - const _bI = SortedArray.indexOf(unit.elements, p.atomIndex) as StructureElement.UnitIndex; - if (_bI < 0) continue; - atomA[atomA.length] = _aI; - atomB[atomB.length] = _bI; - flags[flags.length] = se.flags; - order[order.length] = se.order; - } - } - } for (let ni = 0; ni < count; ni++) { const _bI = indices[ni];