diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index 13c6ed4422ff664de86e1eb801a6e224be21cb0e..1b6e2a7612d7486dd36fe09c92beff7828fa9d97 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -76,22 +76,15 @@ export function printBonds(structure: Structure) { if (!Unit.isAtomic(unit)) continue; const elements = unit.elements; - const { count, offset, neighbor } = unit.bonds; + const { a, b } = unit.bonds; const { model } = unit; - if (!count) continue; + if (!a.length) continue; - for (let j = 0; j < offset.length - 1; ++j) { - const start = offset[j]; - const end = offset[j + 1]; - - if (end <= start) continue; - - const aI = elements[j]; - for (let _bI = start; _bI < end; _bI++) { - const bI = elements[neighbor[_bI]]; - console.log(`${atomLabel(model, aI)} -- ${atomLabel(model, bI)}`); - } + 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])}`); } } } @@ -153,7 +146,7 @@ async function run(mmcif: mmCIF_Database) { printSequence(models[0]); printIHMModels(models[0]); printUnits(structure); - // printBonds(structure); + printBonds(structure); printSecStructure(models[0]); } diff --git a/src/mol-math/graph.ts b/src/mol-math/graph.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce0429662fe04ce07a6a24a56e8a25c188bb4cf1 --- /dev/null +++ b/src/mol-math/graph.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export * from './graph/int/graph' \ No newline at end of file diff --git a/src/mol-math/graph/_spec/int-graph.spec.ts b/src/mol-math/graph/_spec/int-graph.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3d75dddb0296d749b60261249cfd666ddb1d8a35 --- /dev/null +++ b/src/mol-math/graph/_spec/int-graph.spec.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { IntGraph } from '../int/graph'; + +describe('IntGraph', () => { + const vc = 3; + const xs = [0, 1, 2]; + const ys = [1, 2, 0]; + const _prop = [10, 11, 12]; + + const builder = new IntGraph.EdgeBuilder(vc, xs, ys); + const prop: number[] = new Array(builder.slotCount); + for (let i = 0; i < builder.edgeCount; i++) { + builder.addNextEdge(); + builder.assignProperty(prop, _prop[i]); + } + const graph = builder.createGraph({ prop }); + + it('triangle-edgeCount', () => expect(graph.edgeCount).toBe(3)); + it('triangle-vertexEdgeCounts', () => { + expect(graph.getVertexEdgeCount(0)).toBe(2); + expect(graph.getVertexEdgeCount(1)).toBe(2); + expect(graph.getVertexEdgeCount(2)).toBe(2); + }); + + it('triangle-propAndEdgeIndex', () => { + const prop = graph.prop; + expect(prop[graph.getEdgeIndex(0, 1)]).toBe(10); + expect(prop[graph.getEdgeIndex(1, 2)]).toBe(11); + expect(prop[graph.getEdgeIndex(2, 0)]).toBe(12); + }); +}); \ No newline at end of file diff --git a/src/mol-math/graph/int/graph.ts b/src/mol-math/graph/int/graph.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb50ee13f123ed9deec41270e3e81b5923878f56 --- /dev/null +++ b/src/mol-math/graph/int/graph.ts @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +/** + * Represent a graph using vertex adjacency list. + * + * Edges of the i-th vertex are stored in the arrays a and b + * for indices in the range [offset[i], offset[i+1]). + * + * Edge properties are indexed same as in the arrays a and b. + */ +type IntGraph<EdgeProperties extends object = { }> = { + readonly offset: ArrayLike<number>, + readonly a: ArrayLike<number>, + readonly b: ArrayLike<number>, + readonly vertexCount: number, + readonly edgeCount: number, + + /** + * Get the edge index between i-th and j-th vertex. + * -1 if the edge does not exist. + * + * Because the a and b arrays contains each edge twice, + * this always returns the smaller of the indices. + */ + getEdgeIndex(i: number, j: number): number, + getVertexEdgeCount(i: number): number +} & EdgeProperties + +namespace IntGraph { + class Impl implements IntGraph<any> { + readonly vertexCount: number; + + getEdgeIndex(i: number, j: number): number { + let a, b; + if (i < j) { a = i; b = j; } + else { a = j; b = i; } + for (let t = this.offset[a], _t = this.offset[a + 1]; t < _t; t++) { + if (this.b[t] === b) return t; + } + return -1; + } + + getVertexEdgeCount(i: number): number { + return this.offset[i + 1] - this.offset[i]; + } + + constructor(public offset: ArrayLike<number>, public a: ArrayLike<number>, public b: ArrayLike<number>, public edgeCount: number, props?: any) { + this.vertexCount = offset.length - 1; + if (props) { + for (const p of Object.keys(props)) { + (this as any)[p] = props[p]; + } + } + } + } + + export function create<EdgeProps extends object = { }>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntGraph<EdgeProps> { + return new Impl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>; + } + + export class EdgeBuilder { + private bucketFill: Int32Array; + private current = 0; + private curA: number = 0; + private curB: number = 0; + + offsets: Int32Array; + edgeCount: number; + /** the size of the A and B arrays */ + slotCount: number; + a: Int32Array; + b: Int32Array; + + createGraph<EdgeProps extends object = { }>(edgeProps?: EdgeProps) { + return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps); + } + + /** + * @example + * const property = new Int32Array(builder.slotCount); + * for (let i = 0; i < builder.edgeCount; i++) { + * builder.addNextEdge(); + * builder.assignProperty(property, srcProp[i]); + * } + * return builder.createGraph({ property }); + */ + addNextEdge() { + const a = this.xs[this.current], b = this.ys[this.current]; + + const oa = this.offsets[a] + this.bucketFill[a]; + const ob = this.offsets[b] + this.bucketFill[b]; + + this.a[oa] = a; + this.b[oa] = b; + this.bucketFill[a]++; + + this.a[ob] = b; + this.b[ob] = a; + this.bucketFill[b]++; + + this.current++; + this.curA = oa; + this.curB = ob; + } + + assignProperty<T>(prop: { [i: number]: T }, value: T) { + prop[this.curA] = value; + prop[this.curB] = value; + } + + constructor(public vertexCount: number, public xs: ArrayLike<number>, public ys: ArrayLike<number>) { + this.edgeCount = xs.length; + this.offsets = new Int32Array(this.vertexCount + 1); + this.bucketFill = new Int32Array(this.vertexCount); + + const bucketSizes = new Int32Array(this.vertexCount); + for (let i = 0, _i = this.xs.length; i < _i; i++) bucketSizes[this.xs[i]]++; + for (let i = 0, _i = this.ys.length; i < _i; i++) bucketSizes[this.ys[i]]++; + + let offset = 0; + for (let i = 0; i < this.vertexCount; i++) { + this.offsets[i] = offset; + offset += bucketSizes[i]; + } + this.offsets[this.vertexCount] = offset; + this.slotCount = offset; + this.a = new Int32Array(offset); + this.b = new Int32Array(offset); + } + } +} + +export { IntGraph } \ No newline at end of file diff --git a/src/mol-model/sequence/TODO b/src/mol-model/sequence/TODO deleted file mode 100644 index a14553cb9d4b45b94a29e6b28b9c0478205867b3..0000000000000000000000000000000000000000 --- a/src/mol-model/sequence/TODO +++ /dev/null @@ -1,2 +0,0 @@ -- Support for FASTA etc.. -- Mapping/properties for 'structure' \ No newline at end of file diff --git a/src/mol-math/graph/graph.ts b/src/mol-model/sequence/constants.ts similarity index 100% rename from src/mol-math/graph/graph.ts rename to src/mol-model/sequence/constants.ts diff --git a/src/mol-model/sequence/sequence.ts b/src/mol-model/sequence/sequence.ts new file mode 100644 index 0000000000000000000000000000000000000000..c13501983abdf90f57512f75e1d4e60447759fc8 --- /dev/null +++ b/src/mol-model/sequence/sequence.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// import { Column } from 'mol-data/db' +// TODO + +interface Sequence { + +} + +namespace Sequence { + export const enum Kind { + Protein = 'protein', + RNA = 'RNA', + DNA = 'DNA', + Generic = 'generic' + } + + export type ProteinAlphabet = 'X' + export type RnaAlphabet = 'X' + export type DnaAlphabet = 'C' + export type GenericAlphabet = 'X' + + export interface Base<K extends Kind, Alphabet extends string> { + readonly kind: K, + readonly sequence: ReadonlyArray<Alphabet> + } +} + +export { Sequence } 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 816d72cb7e1fcbda3ee5f94a9e587784522598fd..ba344073c94604ba612a5dfafe4bd100770e30bb 100644 --- a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts @@ -8,6 +8,7 @@ import { BondType, ElementSymbol } from '../../../model/types' import { IntraUnitBonds } from './intra-data' import { StructConn, ComponentBondInfo } from '../../../model/formats/mmcif/bonds' import Unit from '../../unit' +import { IntGraph } from 'mol-math/graph'; export interface BondComputationParameters { maxHbondLength: number, @@ -62,48 +63,17 @@ function isHydrogen(i: number) { return i === H_ID; } -function computePerAtomBonds(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number) { - const bucketSizes = new Int32Array(atomCount); - const bucketOffsets = new Int32Array(atomCount + 1) as any as number[]; - const bucketFill = new Int32Array(atomCount); - - for (const i of atomA) bucketSizes[i]++; - for (const i of atomB) bucketSizes[i]++; - - let offset = 0; - for (let i = 0; i < atomCount; i++) { - bucketOffsets[i] = offset; - offset += bucketSizes[i]; +function getGraph(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number): IntraUnitBonds { + const builder = new IntGraph.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]); } - bucketOffsets[atomCount] = offset; - - const neighbor = new Int32Array(offset) as any as number[]; - const flags = new Uint16Array(offset) as any as number[]; - const order = new Int8Array(offset) as any as number[]; - - for (let i = 0, _i = atomA.length; i < _i; i++) { - const a = atomA[i], b = atomB[i], f = _flags[i], o = _order[i]; - - const oa = bucketOffsets[a] + bucketFill[a]; - const ob = bucketOffsets[b] + bucketFill[b]; - neighbor[oa] = b; - flags[oa] = f; - order[oa] = o; - bucketFill[a]++; - - neighbor[ob] = a; - flags[ob] = f; - order[ob] = o; - bucketFill[b]++; - } - - return { - offsets: bucketOffsets, - neighbor, - flags, - order - }; + return builder.createGraph({ flags, order }); } function _computeBonds(unit: Unit.Atomic, params: BondComputationParameters): IntraUnitBonds { @@ -231,15 +201,7 @@ function _computeBonds(unit: Unit.Atomic, params: BondComputationParameters): In } } - const bonds = computePerAtomBonds(atomA, atomB, order, flags, atomCount); - - return { - offset: bonds.offsets, - neighbor: bonds.neighbor, - flags: bonds.flags, - order: bonds.order, - count: atomA.length - }; + return getGraph(atomA, atomB, order, flags, atomCount); } function computeIntraUnitBonds(unit: Unit.Atomic, params?: Partial<BondComputationParameters>) { diff --git a/src/mol-model/structure/structure/unit/bonds/intra-data.ts b/src/mol-model/structure/structure/unit/bonds/intra-data.ts index c897e6ca63ed8dff5071f1c52def66e41f3bf935..9914145379663e38ce3882ef4634bd57ae3da57e 100644 --- a/src/mol-model/structure/structure/unit/bonds/intra-data.ts +++ b/src/mol-model/structure/structure/unit/bonds/intra-data.ts @@ -6,24 +6,13 @@ */ import { BondType } from '../../../model/types' +import { IntGraph } from 'mol-math/graph'; -interface IntraUnitBonds { - /** - * Where bonds for atom A start and end. - * Start offset at idx, end at idx + 1 - */ - offset: ArrayLike<number>, - neighbor: ArrayLike<number>, - - order: ArrayLike<number>, - flags: ArrayLike<BondType.Flag>, - - count: number -} +type IntraUnitBonds = IntGraph<{ readonly order: ArrayLike<number>, readonly flags: ArrayLike<BondType.Flag> }> namespace IntraUnitBonds { export function createEmpty(): IntraUnitBonds { - return { offset: [], neighbor: [], order: [], flags: [], count: 0 } + return IntGraph.create([], [], [], 0, { flags: [], order: [] }); } export function isCovalent(flags: number) { return (flags & BondType.Flag.Covalent) !== 0;