From efa5ef1bd5993b6a4a628db1ef9e732b2d867932 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Wed, 9 May 2018 15:01:09 +0200 Subject: [PATCH] IntraUnitBonds and other stuff --- src/apps/structure-info/model.ts | 36 +- .../structure/_spec/atom-set.spec.ts | 202 -------- .../structure/model/formats/mmcif/bonds.ts | 10 +- src/mol-model/structure/query/generators.ts | 5 +- src/mol-model/structure/query/selection.ts | 38 +- src/mol-model/structure/query/utils.ts | 107 +++++ src/mol-model/structure/structure/element.ts | 4 +- .../structure/structure/element/group.ts | 77 --- .../structure/element/impl/properties.ts | 10 - .../structure/element/impl/set-builder.ts | 65 --- .../structure/structure/element/impl/set.ts | 452 ------------------ .../structure/structure/element/set.ts | 65 --- .../structure/structure/structure.ts | 17 +- src/mol-model/structure/structure/unit.ts | 56 ++- .../structure/structure/unit/bonds.ts | 8 + .../structure/structure/unit/bonds/compute.ts | 0 .../structure/structure/unit/bonds/data.ts | 0 .../bonds/intra-compute.ts} | 27 +- .../bonds/intra-data.ts} | 10 +- src/perf-tests/sets.ts | 62 +-- 20 files changed, 237 insertions(+), 1014 deletions(-) delete mode 100644 src/mol-model/structure/_spec/atom-set.spec.ts create mode 100644 src/mol-model/structure/query/utils.ts delete mode 100644 src/mol-model/structure/structure/element/group.ts delete mode 100644 src/mol-model/structure/structure/element/impl/properties.ts delete mode 100644 src/mol-model/structure/structure/element/impl/set-builder.ts delete mode 100644 src/mol-model/structure/structure/element/impl/set.ts delete mode 100644 src/mol-model/structure/structure/element/set.ts create mode 100644 src/mol-model/structure/structure/unit/bonds.ts delete mode 100644 src/mol-model/structure/structure/unit/bonds/compute.ts delete mode 100644 src/mol-model/structure/structure/unit/bonds/data.ts rename src/mol-model/structure/structure/{element/properties/bonds/group-compute.ts => unit/bonds/intra-compute.ts} (94%) rename src/mol-model/structure/structure/{element/properties/bonds/group-data.ts => unit/bonds/intra-data.ts} (88%) diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index 8fde77fce..1bc3b500b 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -41,29 +41,28 @@ export function atomLabel(model: Model, aI: number) { export function printBonds(structure: Structure) { - // TODO - // for (const unit of structure.units) { - // const unit = units[OrderedSet.getAt(unitIds, i)]; - // const group = ElementSet.groupFromUnitIndex(elements, OrderedSet.getAt(unitIds, i)); + for (const unit of structure.units) { + if (!Unit.isAtomic(unit)) continue; - // const { count, offset, neighbor } = Unit.getGroupBonds(unit, group); - // const { model } = unit; + const elements = unit.elements; + const { count, offset, neighbor } = unit.bonds; + const { model } = unit; - // if (!count) continue; + if (!count) continue; - // for (let j = 0; j < offset.length - 1; ++j) { - // const start = offset[j]; - // const end = offset[j + 1]; + for (let j = 0; j < offset.length - 1; ++j) { + const start = offset[j]; + const end = offset[j + 1]; - // if (end <= start) continue; + if (end <= start) continue; - // const aI = ElementGroup.getAt(group, j); - // for (let _bI = start; _bI < end; _bI++) { - // const bI = ElementGroup.getAt(group, neighbor[_bI]) - // console.log(`${atomLabel(model, aI)} -- ${atomLabel(model, bI)}`); - // } - // } - // } + const aI = elements[j]; + for (let _bI = start; _bI < end; _bI++) { + const bI = elements[neighbor[_bI]]; + console.log(`${atomLabel(model, aI)} -- ${atomLabel(model, bI)}`); + } + } + } } export function printSequence(model: Model) { @@ -123,6 +122,7 @@ async function run(mmcif: mmCIF_Database) { printSequence(models[0]); printIHMModels(models[0]); printUnits(structure); + printBonds(structure); } async function runDL(pdb: string) { diff --git a/src/mol-model/structure/_spec/atom-set.spec.ts b/src/mol-model/structure/_spec/atom-set.spec.ts deleted file mode 100644 index 60b208053..000000000 --- a/src/mol-model/structure/_spec/atom-set.spec.ts +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { OrderedSet } from 'mol-data/int' -import ElementSet from '../structure/element/set' -import Element from '../structure/element' -import ElementGroup from '../structure/element/group' - -describe('atom set', () => { - const p = (i: number, j: number) => Element.create(i, j); - - function setToPairs(set: ElementSet): ArrayLike<Element> { - const ret: Element[] = []; - const it = ElementSet.elements(set); - while (it.hasNext) { - ret[ret.length] = it.move(); - } - return ret; - } - - it('singleton pair', () => { - const set = ElementSet.ofAtoms([p(10, 11)], ElementSet.Empty); - expect(setToPairs(set)).toEqual([p(10, 11)]); - expect(ElementSet.elementHas(set, p(10, 11))).toBe(true); - expect(ElementSet.elementHas(set, p(11, 11))).toBe(false); - expect(ElementSet.elementAt(set, 0)).toBe(p(10, 11)); - expect(ElementSet.elementCount(set)).toBe(1); - }); - - it('singleton atom', () => { - const set = ElementSet.singleton(p(10, 11), ElementSet.Empty); - expect(setToPairs(set)).toEqual([p(10, 11)]); - expect(ElementSet.elementHas(set, p(10, 11))).toBe(true); - expect(ElementSet.elementHas(set, p(11, 11))).toBe(false); - expect(ElementSet.elementAt(set, 0)).toBe(p(10, 11)); - expect(ElementSet.elementCount(set)).toBe(1); - }); - - it('multi', () => { - const gen = ElementSet.Generator(); - gen.add(1, ElementGroup.createNew(OrderedSet.ofSortedArray([4, 6, 7]))); - gen.add(3, ElementGroup.createNew(OrderedSet.ofRange(0, 1))); - const set = gen.getSet(); - const ret = [p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]; - expect(ElementSet.elementCount(set)).toBe(ret.length); - expect(setToPairs(set)).toEqual([p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]); - expect(ElementSet.elementHas(set, p(10, 11))).toBe(false); - expect(ElementSet.elementHas(set, p(3, 0))).toBe(true); - expect(ElementSet.elementHas(set, p(1, 7))).toBe(true); - for (let i = 0; i < ElementSet.elementCount(set); i++) { - expect(Element.areEqual(ElementSet.elementAt(set, i), ret[i])).toBe(true); - } - }); - - it('template', () => { - const template = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty) - const gen = ElementSet.TemplateGenerator(template); - gen.add(0, OrderedSet.ofSortedArray([1, 2, 6])); - gen.add(1, OrderedSet.ofSingleton(3)); - const set = gen.getSet(); - - expect(ElementSet.groupFromUnitIndex(set, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); - expect(ElementSet.groupFromUnitIndex(set, 1)).toBe(ElementSet.groupFromUnitIndex(template, 1)); - expect(set).toBe(template); - }); - - it('template 1', () => { - const template = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty) - const gen = ElementSet.TemplateGenerator(template); - gen.add(0, OrderedSet.ofSortedArray([1, 2, 6])); - gen.add(1, OrderedSet.ofSingleton(4)); - const set = gen.getSet(); - - expect(ElementSet.groupFromUnitIndex(set, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); - expect(ElementSet.groupFromUnitIndex(set, 1) === ElementSet.groupFromUnitIndex(template, 1)).toBe(false); - expect(set === template).toBe(false); - }); - - it('template union', () => { - const template = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty) - - const p13 = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const p01 = ElementSet.ofAtoms([p(0, 1)], ElementSet.Empty); - const p02 = ElementSet.ofAtoms([p(0, 2)], ElementSet.Empty); - const p06 = ElementSet.ofAtoms([p(0, 6)], ElementSet.Empty); - - const u0 = ElementSet.union([p01, p02, p06], template); - const u1 = ElementSet.union([p01, p02, p06, p13], template); - expect(ElementSet.groupFromUnitIndex(u0, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); - expect(ElementSet.groupFromUnitIndex(u1, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); - expect(ElementSet.groupFromUnitIndex(u1, 1)).toBe(ElementSet.groupFromUnitIndex(template, 1)); - expect(u1).toBe(template); - }); - - it('element at / index of', () => { - const control: Element[] = []; - const gen = ElementSet.Generator(); - for (let i = 1; i < 10; i++) { - const set = []; - for (let j = 1; j < 7; j++) { - control[control.length] = p(i * i, j * j + 1); - set[set.length] = j * j + 1; - } - gen.add(i * i, ElementGroup.createNew(OrderedSet.ofSortedArray(set))); - } - const ms = gen.getSet(); - for (let i = 0; i < control.length; i++) { - expect(Element.areEqual(ElementSet.elementAt(ms, i), control[i])).toBe(true); - } - - for (let i = 0; i < control.length; i++) { - expect(ElementSet.elementIndexOf(ms, control[i])).toBe(i); - } - }); - - it('packed pairs', () => { - const set = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - expect(setToPairs(set)).toEqual([p(0, 1), p(0, 2), p(0, 6), p(1, 3)]); - }); - - it('equality', () => { - const a = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - const b = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - const c = ElementSet.ofAtoms([p(1, 3), p(0, 4), p(0, 6), p(0, 2)], ElementSet.Empty); - const d = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const e = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const f = ElementSet.ofAtoms([p(3, 3)], ElementSet.Empty); - - expect(ElementSet.areEqual(a, a)).toBe(true); - expect(ElementSet.areEqual(a, b)).toBe(true); - expect(ElementSet.areEqual(a, c)).toBe(false); - expect(ElementSet.areEqual(a, d)).toBe(false); - expect(ElementSet.areEqual(d, d)).toBe(true); - expect(ElementSet.areEqual(d, e)).toBe(true); - expect(ElementSet.areEqual(d, f)).toBe(false); - }); - - it('are intersecting', () => { - const a = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - const b = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - const c = ElementSet.ofAtoms([p(1, 3), p(0, 4), p(0, 6), p(0, 2)], ElementSet.Empty); - const d = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const e = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const f = ElementSet.ofAtoms([p(3, 3)], ElementSet.Empty); - const g = ElementSet.ofAtoms([p(10, 3), p(8, 1), p(7, 6), p(3, 2)], ElementSet.Empty); - - expect(ElementSet.areIntersecting(a, a)).toBe(true); - expect(ElementSet.areIntersecting(a, b)).toBe(true); - expect(ElementSet.areIntersecting(a, c)).toBe(true); - expect(ElementSet.areIntersecting(a, d)).toBe(true); - expect(ElementSet.areIntersecting(a, g)).toBe(false); - expect(ElementSet.areIntersecting(d, d)).toBe(true); - expect(ElementSet.areIntersecting(d, e)).toBe(true); - expect(ElementSet.areIntersecting(d, f)).toBe(false); - }); - - it('intersection', () => { - const a = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - const b = ElementSet.ofAtoms([p(10, 3), p(0, 1), p(0, 6), p(4, 2)], ElementSet.Empty); - const c = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const d = ElementSet.ofAtoms([p(2, 3)], ElementSet.Empty); - expect(ElementSet.intersect(a, a)).toBe(a); - expect(setToPairs(ElementSet.intersect(a, b))).toEqual([p(0, 1), p(0, 6)]); - expect(setToPairs(ElementSet.intersect(a, c))).toEqual([p(1, 3)]); - expect(setToPairs(ElementSet.intersect(c, d))).toEqual([]); - }); - - it('subtract', () => { - const a = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - const a1 = ElementSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], ElementSet.Empty); - const b = ElementSet.ofAtoms([p(10, 3), p(0, 1), p(0, 6), p(4, 2)], ElementSet.Empty); - const c = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const d = ElementSet.ofAtoms([p(2, 3)], ElementSet.Empty); - const e = ElementSet.ofAtoms([p(0, 2)], ElementSet.Empty); - expect(setToPairs(ElementSet.subtract(a, a))).toEqual([]); - expect(setToPairs(ElementSet.subtract(a, a1))).toEqual([]); - expect(setToPairs(ElementSet.subtract(a, b))).toEqual([p(0, 2), p(1, 3)]); - expect(setToPairs(ElementSet.subtract(c, d))).toEqual([p(1, 3)]); - expect(setToPairs(ElementSet.subtract(a, c))).toEqual([p(0, 1), p(0, 2), p(0, 6)]); - expect(setToPairs(ElementSet.subtract(c, a))).toEqual([]); - expect(setToPairs(ElementSet.subtract(d, a))).toEqual([p(2, 3)]); - expect(setToPairs(ElementSet.subtract(a, e))).toEqual([p(0, 1), p(0, 6), p(1, 3)]); - }); - - it('union', () => { - const a = ElementSet.ofAtoms([p(1, 3), p(0, 1)], ElementSet.Empty); - const a1 = ElementSet.ofAtoms([p(1, 3), p(0, 1)], ElementSet.Empty); - const b = ElementSet.ofAtoms([p(10, 3), p(0, 1)], ElementSet.Empty); - const c = ElementSet.ofAtoms([p(1, 3)], ElementSet.Empty); - const d = ElementSet.ofAtoms([p(2, 3)], ElementSet.Empty); - expect(ElementSet.union([a], ElementSet.Empty)).toBe(a); - expect(ElementSet.union([a, a], ElementSet.Empty)).toBe(a); - expect(setToPairs(ElementSet.union([a, a], ElementSet.Empty))).toEqual([p(0, 1), p(1, 3)]); - expect(setToPairs(ElementSet.union([a, a1], ElementSet.Empty))).toEqual([p(0, 1), p(1, 3)]); - expect(setToPairs(ElementSet.union([a, b], ElementSet.Empty))).toEqual([p(0, 1), p(1, 3), p(10, 3)]); - expect(setToPairs(ElementSet.union([c, d], ElementSet.Empty))).toEqual([p(1, 3), p(2, 3)]); - expect(setToPairs(ElementSet.union([a, b, c, d], ElementSet.Empty))).toEqual([p(0, 1), p(1, 3), p(2, 3), p(10, 3)]); - }); -}); \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif/bonds.ts b/src/mol-model/structure/model/formats/mmcif/bonds.ts index 139a7a811..7bd6ec6f9 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds.ts @@ -9,9 +9,9 @@ import Model from '../../model' import { BondType } from '../../types' import { findEntityIdByAsymId, findAtomIndexByLabelName } from './util' import { Column } from 'mol-data/db' -import { GroupBonds } from '../../../structure/element/properties/bonds/group-data'; +import { IntraUnitBonds } from '../../../structure/unit/bonds'; -export class StructConn implements GroupBonds.StructConn { +export class StructConn implements IntraUnitBonds.StructConn { private _residuePairIndex: Map<string, StructConn.Entry[]> | undefined = void 0; private _atomIndex: Map<number, StructConn.Entry[]> | undefined = void 0; @@ -71,7 +71,7 @@ export class StructConn implements GroupBonds.StructConn { } export namespace StructConn { - export interface Entry extends GroupBonds.StructConnEntry { + export interface Entry extends IntraUnitBonds.StructConnEntry { distance: number, order: number, flags: number, @@ -181,7 +181,7 @@ export namespace StructConn { } } -export class ComponentBondInfo implements GroupBonds.ComponentBondInfo { +export class ComponentBondInfo implements IntraUnitBonds.ComponentBondInfo { entries: Map<string, ComponentBondInfo.Entry> = new Map(); newEntry(id: string) { @@ -192,7 +192,7 @@ export class ComponentBondInfo implements GroupBonds.ComponentBondInfo { } export namespace ComponentBondInfo { - export class Entry implements GroupBonds.ComponentBondInfoEntry { + export class Entry implements IntraUnitBonds.ComponentBondInfoEntry { map: Map<string, Map<string, { order: number, flags: number }>> = new Map(); add(a: string, b: string, order: number, flags: number, swap = true) { diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts index c97533b54..ac9df8509 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -136,9 +136,10 @@ class LinearGroupingBuilder { private singletonSelection(): Selection { const builder = this.source.subsetBuilder(true); + const loc = Element.Location(); for (let i = 0, _i = this.builders.length; i < _i; i++) { - const e = this.builders[i].singleton(); - builder.addToUnit(Element.unit(e), Element.index(e)); + this.builders[i].setSingletonLocation(loc); + builder.addToUnit(loc.unit.id, loc.element); } return Selection.Singletons(this.source, builder.getStructure()); } diff --git a/src/mol-model/structure/query/selection.ts b/src/mol-model/structure/query/selection.ts index 02235ee8e..ebe352b68 100644 --- a/src/mol-model/structure/query/selection.ts +++ b/src/mol-model/structure/query/selection.ts @@ -6,7 +6,7 @@ import { HashSet } from 'mol-data/generic' import { Structure } from '../structure' -import { SortedArray } from 'mol-data/int'; +import { StructureUtils } from './utils'; // A selection is a pair of a Structure and a sequence of unique AtomSets type Selection = Selection.Singletons | Selection.Sequence @@ -31,7 +31,7 @@ namespace Selection { export function unionStructure(sel: Selection): Structure { if (isEmpty(sel)) return Structure.Empty; if (isSingleton(sel)) return sel.structure; - return union(sel.source, sel.structures); + return StructureUtils.union(sel.source, sel.structures); } export interface Builder { @@ -42,7 +42,7 @@ namespace Selection { function getSelection(source: Structure, structures: Structure[], allSingletons: boolean) { const len = structures.length; if (len === 0) return Empty(source); - if (allSingletons) return Singletons(source, union(source, structures)); + if (allSingletons) return Singletons(source, StructureUtils.union(source, structures)); return Sequence(source, structures); } @@ -83,38 +83,6 @@ namespace Selection { export function UniqueBuilder(structure: Structure): Builder { return new HashBuilderImpl(structure); } // TODO: spatial lookup - - function union(source: Structure, structures: Structure[]) { - if (structures.length === 0) return Structure.Empty; - if (structures.length === 1) return structures[0]; - - const unitMap = new Map<number, SortedArray>(); - const fullUnits = new Set<number>(); - - for (const { units } of structures) { - for (let i = 0, _i = units.length; i < _i; i++) { - const u = units[i]; - if (unitMap.has(u.id)) { - // check if there is anything more to union in this particual unit. - if (fullUnits.has(u.id)) continue; - const merged = SortedArray.union(unitMap.get(u.id)!, u.elements); - unitMap.set(u.id, merged); - if (merged.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id); - } else { - unitMap.set(u.id, u.elements); - if (u.elements.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id); - } - } - } - - const builder = source.subsetBuilder(true); - unitMap.forEach(buildUnion, builder); - return builder.getStructure(); - } - - function buildUnion(this: Structure.SubsetBuilder, elements: SortedArray, id: number) { - this.setUnit(id, elements); - } } export default Selection \ No newline at end of file diff --git a/src/mol-model/structure/query/utils.ts b/src/mol-model/structure/query/utils.ts new file mode 100644 index 000000000..2981878e9 --- /dev/null +++ b/src/mol-model/structure/query/utils.ts @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Structure, Unit } from '../structure' +import { SortedArray } from 'mol-data/int'; + +namespace StructureUtils { + export function union(source: Structure, structures: Structure[]) { + if (structures.length === 0) return Structure.Empty; + if (structures.length === 1) return structures[0]; + + const unitMap = new Map<number, SortedArray>(); + const fullUnits = new Set<number>(); + + for (const { units } of structures) { + for (let i = 0, _i = units.length; i < _i; i++) { + const u = units[i]; + if (unitMap.has(u.id)) { + // check if there is anything more to union in this particual unit. + if (fullUnits.has(u.id)) continue; + const merged = SortedArray.union(unitMap.get(u.id)!, u.elements); + unitMap.set(u.id, merged); + if (merged.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id); + } else { + unitMap.set(u.id, u.elements); + if (u.elements.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id); + } + } + } + + const builder = source.subsetBuilder(true); + unitMap.forEach(buildUnion, builder); + return builder.getStructure(); + } + + function buildUnion(this: Structure.SubsetBuilder, elements: SortedArray, id: number) { + this.setUnit(id, elements); + } + + export function areIntersecting(sA: Structure, sB: Structure): boolean { + if (sA === sB) return true; + + let a, b; + if (sA.units.length < sB.units.length) { a = sA; b = sB; } + else { a = sB; b = sA; } + + const aU = a.units, bU = b.unitMap; + + for (let i = 0, _i = aU.length; i < _i; i++) { + const u = aU[i]; + if (!bU.has(u.id)) continue; + const v = bU.get(u.id); + if (SortedArray.areIntersecting(u.elements, v.elements)) return true; + } + + return false; + } + + export function intersect(sA: Structure, sB: Structure): Structure { + if (sA === sB) return sA; + if (!areIntersecting(sA, sB)) return Structure.Empty; + + let a, b; + if (sA.units.length < sB.units.length) { a = sA; b = sB; } + else { a = sB; b = sA; } + + const aU = a.units, bU = b.unitMap; + const units: Unit[] = []; + + for (let i = 0, _i = aU.length; i < _i; i++) { + const u = aU[i]; + if (!bU.has(u.id)) continue; + const v = bU.get(u.id); + if (SortedArray.areIntersecting(u.elements, v.elements)) { + const int = SortedArray.intersect(u.elements, v.elements); + units[units.length] = u.getChild(int); + } + } + + return Structure.create(units); + } + + export function subtract(a: Structure, b: Structure): Structure { + if (a === b) return Structure.Empty; + if (!areIntersecting(a, b)) return a; + + const aU = a.units, bU = b.unitMap; + const units: Unit[] = []; + + for (let i = 0, _i = aU.length; i < _i; i++) { + const u = aU[i]; + if (!bU.has(u.id)) continue; + const v = bU.get(u.id); + const sub = SortedArray.intersect(u.elements, v.elements); + if (sub.length > 0) { + units[units.length] = u.getChild(sub); + } + } + + return Structure.create(units); + } +} + +export { StructureUtils } \ No newline at end of file diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts index f730ed988..009950daf 100644 --- a/src/mol-model/structure/structure/element.ts +++ b/src/mol-model/structure/structure/element.ts @@ -15,7 +15,7 @@ namespace Element { export const Zero: Element = Tuple.Zero; export const create: (unit: number, index: number) => Element = Tuple.create; export const is: (x: any) => x is Element = Tuple.is; - export const unit: (e: Element) => number = Tuple.fst; + export const unitId: (e: Element) => number = Tuple.fst; export const index: (e: Element) => number = Tuple.snd; export const areEqual: (e: Element, b: Element) => boolean = Tuple.areEqual; export const hashCode: (e: Element) => number = Tuple.hashCode; @@ -29,7 +29,7 @@ namespace Element { export interface Predicate extends Property<boolean> { } export function updateLocation(structure: Structure, l: Location, element: Element) { - l.unit = structure.units[unit(element)]; + l.unit = structure.units[unitId(element)]; l.element = index(element); return l; } diff --git a/src/mol-model/structure/structure/element/group.ts b/src/mol-model/structure/structure/element/group.ts deleted file mode 100644 index 04c4e12ac..000000000 --- a/src/mol-model/structure/structure/element/group.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { OrderedSet } from 'mol-data/int' -import { Lookup3D } from 'mol-math/geometry' -import Unit from '../unit' -import { GroupBonds } from './properties/bonds/group-data'; - -interface ElementGroup { - elements: OrderedSet, - // Unique identifier of the group, usable as partial key for various "caches". - key: number, - - __lookup3d__?: Lookup3D, - __bonds__?: GroupBonds -} - -namespace ElementGroup { - export const Empty = createNew(OrderedSet.Empty) - - export function singleton(idx: number) { - return createNew(OrderedSet.ofSingleton(idx)); - } - - export function createNew(elements: OrderedSet): ElementGroup { - return { key: nextKey(), elements }; - } - - export function create(unit: Unit, elements: OrderedSet): ElementGroup { - //if (OrderedSet.areEqual(elements, unit.fullGroup.elements)) return unit.fullGroup; - return createNew(elements); - } - - export function createChild(parent: ElementGroup, elements: OrderedSet): ElementGroup { - if (OrderedSet.areEqual(elements, parent.elements)) return parent; - return createNew(elements); - } - - export function size(group: ElementGroup) { return OrderedSet.size(group.elements); } - export function has(group: ElementGroup, element: number) { return OrderedSet.has(group.elements, element); } - export function getAt(group: ElementGroup, i: number) { return OrderedSet.getAt(group.elements, i); } - export function indexOf(group: ElementGroup, element: number) { return OrderedSet.indexOf(group.elements, element); } - export function hashCode(group: ElementGroup) { return OrderedSet.hashCode(group.elements); } - export function areEqual(a: ElementGroup, b: ElementGroup) { return OrderedSet.areEqual(a.elements, b.elements); } - - export function intersect(a: ElementGroup, b: ElementGroup) { - const set = OrderedSet.intersect(a.elements, b.elements); - if (set === a.elements) return a; - if (set === b.elements) return b; - return createNew(set); - } - - export function union(a: ElementGroup, b: ElementGroup) { - const set = OrderedSet.union(a.elements, b.elements); - if (set === a.elements) return a; - if (set === b.elements) return b; - return createNew(set); - } - - export function subtract(a: ElementGroup, b: ElementGroup) { - const set = OrderedSet.subtract(a.elements, b.elements); - if (set === a.elements) return a; - return createNew(set); - } - - let _id = 0; - function nextKey() { - const ret = _id; - _id = (_id + 1) % 0x3fffffff; - return ret; - } -} - -export default ElementGroup \ No newline at end of file diff --git a/src/mol-model/structure/structure/element/impl/properties.ts b/src/mol-model/structure/structure/element/impl/properties.ts deleted file mode 100644 index 03ae27d16..000000000 --- a/src/mol-model/structure/structure/element/impl/properties.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -// TODO: bounding sphere -// TODO: distance, areWithIn? -// TODO: check connected -// TODO: add "parent" property? how to avoid using too much memory? Transitive parents? Parent unlinking? \ No newline at end of file diff --git a/src/mol-model/structure/structure/element/impl/set-builder.ts b/src/mol-model/structure/structure/element/impl/set-builder.ts deleted file mode 100644 index 28956aa83..000000000 --- a/src/mol-model/structure/structure/element/impl/set-builder.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import ElementSet from '../set' -import Element from '../../element' -import { OrderedSet, IntMap } from 'mol-data/int' -import { sortArray } from 'mol-data/util/sort' - -export class Builder { - private keys: number[] = []; - private units = IntMap.Mutable<number[]>(); - private currentUnit: number[] = []; - - elementCount = 0; - - add(u: number, e: number) { - const unit = this.units.get(u); - if (!!unit) { unit[unit.length] = e; } - else { - this.units.set(u, [e]); - this.keys[this.keys.length] = u; - } - this.elementCount++; - } - - beginUnit() { this.currentUnit = this.currentUnit.length > 0 ? [] : this.currentUnit; } - addToUnit(a: number) { this.currentUnit[this.currentUnit.length] = a; this.elementCount++; } - commitUnit(u: number) { - if (this.currentUnit.length === 0) return; - this.keys[this.keys.length] = u; - this.units.set(u, this.currentUnit); - } - - getSet(): ElementSet { - const generator = ElementSet.TemplateGenerator(this.parent); - - for (let i = 0, _i = this.keys.length; i < _i; i++) { - const k = this.keys[i]; - const unit = this.units.get(k); - const l = unit.length; - if (!this.sorted && l > 1) sortArray(unit); - generator.add(k, OrderedSet.ofSortedArray(unit)); - } - - return generator.getSet(); - } - - singleton(): Element { - const u = this.keys[0]; - return Element.create(u, this.units.get(u)[0]); - } - - constructor(private parent: ElementSet, private sorted: boolean) { } -} - -export function LinearBuilder(parent: ElementSet) { - return new Builder(parent, true); -} - -export function UnsortedBuilder(parent: ElementSet) { - return new Builder(parent, false); -} \ No newline at end of file diff --git a/src/mol-model/structure/structure/element/impl/set.ts b/src/mol-model/structure/structure/element/impl/set.ts deleted file mode 100644 index 17ada0b57..000000000 --- a/src/mol-model/structure/structure/element/impl/set.ts +++ /dev/null @@ -1,452 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { SortedArray, Interval, Iterator, OrderedSet as OS, IntMap } from 'mol-data/int' -import { sortArray } from 'mol-data/util/sort' -import { hash1 } from 'mol-data/util/hash-functions' -import Element from '../../element' -import ElementGroup from '../group' -/** Long and painful implementation starts here */ - -export type ElementSetImpl = { - groups: IntMap<ElementGroup>, - offsets: Int32Array, - hashCode: number, - keys: SortedArray -} - -export const Empty: ElementSetImpl = { groups: IntMap.Empty, offsets: new Int32Array(1), hashCode: 0, keys: SortedArray.Empty }; - -export function ofElements(elements: ArrayLike<Element>, template: ElementSetImpl): ElementSetImpl { - return ofElementsImpl(elements, template); -} - -export function singleton(element: Element, template: ElementSetImpl) { - return singletonImpl(element, template); -} - -export function getKeys(set: ElementSetImpl): SortedArray { - return set.keys; -} - -export function keyCount(set: ElementSetImpl): number { - return set.keys.length; -} - -export function hasKey(set: ElementSetImpl, key: number): boolean { - return set.groups.has(key); -} - -export function getKey(set: ElementSetImpl, index: number): number { - return set.keys[index]; -} - -export function hasAtom(set: ElementSetImpl, t: Element): boolean { - const os = set.groups.get(Element.unit(t)); - return !!os && ElementGroup.has(os, Element.index(t)); -} - -export function getByKey(set: ElementSetImpl, key: number): ElementGroup { - return set.groups.get(key) || ElementGroup.Empty; -} - -export function getByIndex(set: ElementSetImpl, index: number): ElementGroup { - const key = set.keys[index]; - return set.groups.get(key) || ElementGroup.Empty; -} - -export function getAt(set: ElementSetImpl, i: number): Element { - const { offsets, keys } = set; - const o = getOffsetIndex(offsets, i); - if (o >= offsets.length - 1) return Element.Zero; - const k = keys[o]; - const e = ElementGroup.getAt(set.groups.get(k), i - offsets[o]); - return Element.create(k, e); -} - -export function indexOf(set: ElementSetImpl, t: Element) { - const { keys } = set; - const u = Element.unit(t); - const setIdx = SortedArray.indexOf(keys, u); - if (setIdx < 0) return -1; - const o = ElementGroup.indexOf(set.groups.get(u), Element.index(t)); - if (o < 0) return -1; - return set.offsets[setIdx] + o; -} - -/** Number elements in the "child" sets */ -export function size(set: ElementSetImpl) { - return set.offsets[set.offsets.length - 1]; -} - -export function hashCode(set: ElementSetImpl) { - if (set.hashCode !== -1) return set.hashCode; - return computeHash(set); -} - -export function areEqual(a: ElementSetImpl, b: ElementSetImpl) { - if (a === b) return true; - if (size(a) !== size(a)) return false; - - const keys = a.keys; - if (!SortedArray.areEqual(keys, b.keys)) return false; - const { groups: aG } = a; - const { groups: bG } = b; - for (let i = 0, _i = keys.length; i < _i; i++) { - const k = keys[i]; - if (!ElementGroup.areEqual(aG.get(k), bG.get(k))) return false; - } - return true; -} - -export function areIntersecting(a: ElementSetImpl, b: ElementSetImpl) { - if (a === b) return true; - const keysA = a.keys, keysB = b.keys; - if (!SortedArray.areIntersecting(a.keys, b.keys)) return false; - const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); - const start = Interval.start(r), end = Interval.end(r); - const { groups: aG } = a; - const { groups: bG } = b; - for (let i = start; i < end; i++) { - const k = keysA[i]; - const ak = aG.get(k), bk = bG.get(k); - if (!!ak && !!bk && OS.areIntersecting(ak.elements, bk.elements)) return true; - } - return false; -} - -export function intersect(a: ElementSetImpl, b: ElementSetImpl) { - if (a === b) return a; - - const keysA = a.keys, keysB = b.keys; - if (!SortedArray.areIntersecting(a.keys, b.keys)) return Empty; - const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); - const start = Interval.start(r), end = Interval.end(r); - - const { groups: aG } = a; - const { groups: bG } = b; - const generator = new ChildGenerator(a, b); - for (let i = start; i < end; i++) { - const k = keysA[i]; - const bk = bG.get(k); - if (!bk) continue; - const ak = aG.get(k); - generator.add(k, ElementGroup.intersect(aG.get(k), bk), ak, bk); - } - return generator.getSet(); -} - -export function subtract(a: ElementSetImpl, b: ElementSetImpl) { - if (a === b) return Empty; - - const keysA = a.keys, keysB = b.keys; - if (!SortedArray.areIntersecting(keysA, keysB)) return a; - const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); - const start = Interval.start(r), end = Interval.end(r); - - const generator = new ChildGenerator(a, b); - const { groups: aG } = a; - const { groups: bG } = b; - for (let i = 0; i < start; i++) { - const k = keysA[i]; - const ak = aG.get(k); - generator.addA(k, ak, ak); - } - for (let i = start; i < end; i++) { - const k = keysA[i]; - const ak = aG.get(k), bk = bG.get(k); - if (!!bk) { - const subtraction = ElementGroup.subtract(ak, bk); - generator.addA(k, subtraction, ak); - } else { - generator.addA(k, ak, ak); - } - } - for (let i = end, _i = keysA.length; i < _i; i++) { - const k = keysA[i]; - const ak = aG.get(k); - generator.addA(k, ak, ak); - } - return generator.getSet(); -} - -export function unionMany(sets: ArrayLike<ElementSetImpl>, template: ElementSetImpl) { - return findUnion(sets, template); -} - -class ElementsIterator implements Iterator<Element> { - private unit: number = 0; - private keyCount: number; - private setIndex = -1; - private currentIndex = 0; - private currentSize = 0; - private currentSet: OS = OS.Empty; - - hasNext: boolean = false; - - move() { - if (!this.hasNext) return Element.Zero; - const ret = Element.create(this.unit, OS.getAt(this.currentSet, this.currentIndex++)); - if (this.currentIndex >= this.currentSize) this.advance(); - return ret; - } - - private advance() { - if (++this.setIndex >= this.keyCount) { - this.hasNext = false; - return false; - } - this.unit = this.elements.keys[this.setIndex]; - this.currentSet = this.elements.groups.get(this.unit).elements; - this.currentIndex = 0; - this.currentSize = OS.size(this.currentSet); - return true; - } - - constructor(private elements: ElementSetImpl) { - this.keyCount = elements.keys.length; - this.hasNext = this.keyCount > 0; - this.advance(); - } -} - -export function values(set: ElementSetImpl): Iterator<Element> { - return new ElementsIterator(set); -} - -export class TemplateAtomSetGenerator { - private keys: number[] = []; - private groups = IntMap.Mutable<ElementGroup>(); - private templateGroups: IntMap<ElementGroup>; - private equalGroups = 0; - - add(unit: number, set: OS) { - if (OS.size(set) === 0) return; - this.keys[this.keys.length] = unit; - if (this.templateGroups.has(unit)) { - const t = this.templateGroups.get(unit); - if (OS.areEqual(t.elements, set)) { - this.groups.set(unit, t); - this.equalGroups++; - } else { - this.groups.set(unit, ElementGroup.createNew(set)); - } - } else { - this.groups.set(unit, ElementGroup.createNew(set)); - } - } - - getSet(): ElementSetImpl { - if (this.equalGroups === this.template.keys.length && this.equalGroups === this.keys.length) { - return this.template; - } - return create(this.keys, this.groups); - } - - constructor(private template: ElementSetImpl) { - this.templateGroups = template.groups; - } -} - -export function TemplateGenerator(template: ElementSetImpl) { - return new TemplateAtomSetGenerator(template); -} - -export class AtomSetGenerator { - private keys: number[] = []; - private groups = IntMap.Mutable<ElementGroup>(); - - add(unit: number, group: ElementGroup) { - if (ElementGroup.size(group) === 0) return; - this.keys[this.keys.length] = unit; - this.groups.set(unit, group); - } - - getSet(): ElementSetImpl { - return create(this.keys, this.groups); - } -} - -export function Generator() { - return new AtomSetGenerator(); -} - -/** When adding groups, compare them to existing ones. If they all match, return the whole original set. */ -class ChildGenerator { - private keys: number[] = []; - private groups = IntMap.Mutable<ElementGroup>(); - private aEqual = 0; - private bEqual = 0; - - add(unit: number, group: ElementGroup, a: ElementGroup, b: ElementGroup) { - if (ElementGroup.size(group) === 0) return; - if (a === group) this.aEqual++; - if (b === group) this.bEqual++; - this.keys[this.keys.length] = unit; - this.groups.set(unit, group); - } - - addA(unit: number, group: ElementGroup, a: ElementGroup) { - if (ElementGroup.size(group) === 0) return; - - if (a === group) this.aEqual++; - this.keys[this.keys.length] = unit; - this.groups.set(unit, group); - } - - constructor(private a: ElementSetImpl, private b: ElementSetImpl) { - } - - getSet(): ElementSetImpl { - if (this.aEqual === this.a.keys.length) return this.a; - if (this.bEqual === this.b.keys.length) return this.b; - return create(this.keys, this.groups); - } -} - -function create(keys: number[], groups: IntMap<ElementGroup>): ElementSetImpl { - sortArray(keys); - let runningSize = 0; - const offsets = new Int32Array(keys.length + 1); - for (let i = 0, _i = keys.length; i < _i; i++) { - runningSize += ElementGroup.size(groups.get(keys[i])); - offsets[i + 1] = runningSize; - } - return { keys: SortedArray.ofSortedArray(keys), groups: IntMap.asImmutable(groups), offsets, hashCode: -1 }; -} - -function getUniqueElements(xs: number[]): number[] { - let count = 1; - for (let i = 1, _i = xs.length; i < _i; i++) { - if (xs[i - 1] !== xs[i]) count++; - } - const ret = new (xs as any).constructor(count); - ret[0] = xs[0]; - let offset = 1; - for (let i = 1, _i = xs.length; i < _i; i++) { - if (xs[i - 1] !== xs[i]) ret[offset++] = xs[i]; - } - return ret; -} - -function normalizeArray(xs: number[]): number[] { - sortArray(xs); - for (let i = 1, _i = xs.length; i < _i; i++) { - if (xs[i - 1] === xs[i]) return getUniqueElements(xs); - } - return xs; -} - -function ofElementsImpl(xs: ArrayLike<Element>, template: ElementSetImpl) { - if (xs.length === 0) return Empty; - - const elements = IntMap.Mutable<number[]>(); - const keys: number[] = []; - for (let i = 0, _i = xs.length; i < _i; i++) { - const x = xs[i]; - const u = Element.unit(x), v = Element.index(x); - if (elements.has(u)) { - const set = elements.get(u); - set[set.length] = v; - } else { - keys[keys.length] = u; - elements.set(u, [v]); - } - } - - const generator = TemplateGenerator(template); - for (let i = 0, _i = keys.length; i < _i; i++) { - const k = keys[i]; - generator.add(k, OS.ofSortedArray(normalizeArray(elements.get(k)))); - } - - return generator.getSet(); -} - -function singletonImpl(element: Element, template: ElementSetImpl) { - const k = Element.unit(element), i = Element.index(element); - const { groups } = template; - const gs = IntMap.Mutable<ElementGroup>(); - if (groups.has(k)) { - const g = groups.get(k); - if (ElementGroup.size(g) === 1 && ElementGroup.getAt(g, 0) === i) { - gs.set(k, g); - return create([k], gs); - } - } - gs.set(k, ElementGroup.createNew(OS.ofSingleton(i))); - return create([k], gs); -} - -function getOffsetIndex(xs: ArrayLike<number>, value: number) { - let min = 0, max = xs.length - 1; - while (min < max) { - const mid = (min + max) >> 1; - const v = xs[mid]; - if (value < v) max = mid - 1; - else if (value > v) min = mid + 1; - else return mid; - } - if (min > max) { - return max; - } - return value < xs[min] ? min - 1 : min; -} - -function computeHash(set: ElementSetImpl) { - const { keys, groups } = set; - let hash = 23; - for (let i = 0, _i = keys.length; i < _i; i++) { - const k = keys[i]; - hash = (31 * hash + k) | 0; - hash = (31 * hash + ElementGroup.hashCode(groups.get(k))) | 0; - } - hash = (31 * hash + size(set)) | 0; - hash = hash1(hash); - set.hashCode = hash; - return hash; -} - -function findUnion(sets: ArrayLike<ElementSetImpl>, template: ElementSetImpl) { - if (!sets.length) return Empty; - if (sets.length === 1) return sets[0]; - if (sets.length === 2 && sets[0] === sets[1]) return sets[0]; - - const keys: number[] = []; - const groups = IntMap.Mutable<ElementGroup>(); - for (let i = 0, _i = sets.length; i < _i; i++) { - unionInto(keys, groups, sets[i]); - } - - return normalizeUnion(keys, groups, template); -} - -function normalizeUnion(keys: number[], groups: IntMap.Mutable<ElementGroup>, template: ElementSetImpl) { - let equalCount = 0; - let tg = template.groups, a: ElementGroup, t: ElementGroup; - for (let i = 0, _i = keys.length; i < _i; i++) { - const k = keys[i]; - if (tg.has(k) && ElementGroup.areEqual(a = groups.get(k), t = tg.get(k))) { - groups.set(k, t); - equalCount++; - } - } - return equalCount === template.keys.length && equalCount === keys.length ? template : create(keys, groups); -} - -function unionInto(keys: number[], groups: IntMap.Mutable<ElementGroup>, a: ElementSetImpl) { - const setKeys = a.keys; - const { groups: aG } = a; - for (let i = 0, _i = setKeys.length; i < _i; i++) { - const k = setKeys[i]; - if (groups.has(k)) { - groups.set(k, ElementGroup.union(aG.get(k), groups.get(k))) - } else { - keys[keys.length] = k; - groups.set(k, aG.get(k)); - } - } -} \ No newline at end of file diff --git a/src/mol-model/structure/structure/element/set.ts b/src/mol-model/structure/structure/element/set.ts deleted file mode 100644 index 0482f2d9e..000000000 --- a/src/mol-model/structure/structure/element/set.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { SortedArray, Iterator, OrderedSet } from 'mol-data/int' -import Element from '../element' -import ElementGroup from './group' -import * as Impl from './impl/set' -import * as Builders from './impl/set-builder' -import { StructureLookup3D } from '../util/lookup3d'; -import Structure from '../structure'; - -/** - * A map-like representation of grouped atom set - * - * Essentially corresponds to the type { [unitId: number]: ElementGroup }. - */ -namespace ElementSet { - export const Empty: ElementSet = Impl.Empty as any; - - export const ofAtoms: (elements: ArrayLike<Element>, template: ElementSet) => ElementSet = Impl.ofElements as any; - export const singleton: (element: Element, template: ElementSet) => ElementSet = Impl.singleton as any; - - export const unitIndices: (set: ElementSet) => SortedArray = Impl.getKeys as any; - export const unitHas: (set: ElementSet, index: number) => boolean = Impl.hasKey as any; - - export const groupCount: (set: ElementSet) => number = Impl.keyCount as any; - export const groupUnitIndex: (set: ElementSet, index: number) => number = Impl.getKey as any; - export const groupFromUnitIndex: (set: ElementSet, unitId: number) => ElementGroup = Impl.getByKey as any; - export const groupAt: (set: ElementSet, index: number) => ElementGroup = Impl.getByIndex as any; - - export const elementCount: (set: ElementSet) => number = Impl.size as any; - export const elementHas: (set: ElementSet, x: Element) => boolean = Impl.hasAtom as any; - export const elementIndexOf: (set: ElementSet, x: Element) => number = Impl.indexOf as any; - export const elementAt: (set: ElementSet, i: number) => Element = Impl.getAt as any; - export const elements: (set: ElementSet) => Iterator<Element> = Impl.values as any; - - export const hashCode: (set: ElementSet) => number = Impl.hashCode as any; - export const areEqual: (a: ElementSet, b: ElementSet) => boolean = Impl.areEqual as any; - export const areIntersecting: (a: ElementSet, b: ElementSet) => boolean = Impl.areIntersecting as any; - - export const union: (sets: ArrayLike<ElementSet>, template: ElementSet) => ElementSet = Impl.unionMany as any; - export const intersect: (a: ElementSet, b: ElementSet) => ElementSet = Impl.intersect as any; - export const subtract: (a: ElementSet, b: ElementSet) => ElementSet = Impl.subtract as any; - - export type Builder = Builders.Builder - export const LinearBuilder = Builders.LinearBuilder - export const UnsortedBuilder = Builders.UnsortedBuilder - - export interface Generator { add(unit: number, set: ElementGroup): void, getSet(): ElementSet } - export const Generator: () => Generator = Impl.Generator as any - - export interface TemplateGenerator { add(unit: number, set: OrderedSet): void, getSet(): ElementSet } - export const TemplateGenerator: (template: ElementSet) => TemplateGenerator = Impl.TemplateGenerator as any - - // TODO: distance, areWithIn? - // TODO: check connected - // TODO: add "parent" property? how to avoid using too much memory? Transitive parents? Parent unlinking? -} - -interface ElementSet { '@type': 'element-set' } - -export default ElementSet \ No newline at end of file diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index c3df47477..9a3cd5b2e 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -12,9 +12,8 @@ import { sortArray, sort, arraySwap, hash1 } from 'mol-data/util'; import Element from './element' import Unit from './unit' import { StructureLookup3D } from './util/lookup3d'; +import { computeStructureBoundary } from './util/boundary'; -// A structure is a pair of "units" and an element set. -// Each unit contains the data and transformation of its corresponding elements. class Structure { readonly unitMap: IntMap<Unit>; readonly units: ReadonlyArray<Unit>; @@ -48,6 +47,10 @@ class Structure { return new Structure.ElementLocationIterator(this); } + get boundary() { + return computeStructureBoundary(this); + } + constructor(units: ArrayLike<Unit>) { const map = IntMap.Mutable<Unit>(); let elementCount = 0; @@ -197,8 +200,10 @@ namespace Structure { return create(newUnits); } - singleton(): Element { - return Element.create(this.ids[0], this.unitMap.get(this.ids[0])[0]); + setSingletonLocation(location: Element.Location) { + const id = this.ids[0]; + location.unit = this.parent.unitMap.get(id); + location.element = this.unitMap.get(id)[0]; } get isEmpty() { @@ -257,11 +262,11 @@ namespace Structure { hasNext: boolean; move(): Element.Location { this.current.element = this.elements[this.idx]; - this.next(); + this.advance(); return this.current; } - private next() { + private advance() { if (this.idx < this.len - 1) { this.idx++; return; diff --git a/src/mol-model/structure/structure/unit.ts b/src/mol-model/structure/structure/unit.ts index 3783235b2..87bff5672 100644 --- a/src/mol-model/structure/structure/unit.ts +++ b/src/mol-model/structure/structure/unit.ts @@ -5,13 +5,12 @@ */ import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator' -import ElementGroup from './element/group' import { Model } from '../model' -import { GridLookup3D } from 'mol-math/geometry' -import { computeUnitBonds } from './element/properties/bonds/group-compute'; +import { GridLookup3D, Lookup3D } from 'mol-math/geometry' import CoarseGrained from '../model/properties/coarse-grained'; import { SortedArray } from 'mol-data/int'; import { idFactory } from 'mol-util/id-factory'; +import { IntraUnitBonds, computeIntraUnitBonds } from './unit/bonds' // A building block of a structure that corresponds to an atomic or a coarse grained representation // 'conveniently grouped together'. @@ -46,7 +45,9 @@ namespace Unit { readonly conformation: SymmetryOperator.ArrayMapping, getChild(elements: SortedArray): Unit, - applyOperator(id: number, operator: SymmetryOperator): Unit + applyOperator(id: number, operator: SymmetryOperator): Unit, + + readonly lookup3d: Lookup3D } const unitIdFactory = idFactory(); @@ -72,6 +73,7 @@ namespace Unit { readonly chainIndex: ArrayLike<number>; getChild(elements: SortedArray): Unit { + if (elements.length === this.elements.length) return this; return new Atomic(this.id, this.invariantId, this.model, elements, this.conformation); } @@ -80,6 +82,21 @@ namespace Unit { return new Atomic(id, this.invariantId, this.model, this.elements, SymmetryOperator.createMapping(op, this.model.atomSiteConformation)); } + private _lookup3d?: Lookup3D = void 0; + get lookup3d() { + if (this._lookup3d) return this._lookup3d; + const { x, y, z } = this.model.atomSiteConformation; + this._lookup3d = GridLookup3D({ x, y, z, indices: this.elements }); + return this._lookup3d; + } + + private _bonds?: IntraUnitBonds = void 0; + get bonds() { + if (this._bonds) return this._bonds; + this._bonds = computeIntraUnitBonds(this); + return this._bonds; + } + constructor(id: number, invariantId: number, model: Model, elements: SortedArray, conformation: SymmetryOperator.ArrayMapping) { this.id = id; this.invariantId = invariantId; @@ -108,6 +125,7 @@ namespace Unit { readonly sites: S; getChild(elements: SortedArray): Unit { + if (elements.length === this.elements.length) return this as any as Unit /** lets call this an ugly temporary hack */; return createCoarse(this.id, this.invariantId, this.model, this.kind, this.sites, elements, this.conformation); } @@ -116,6 +134,15 @@ namespace Unit { return createCoarse(id, this.invariantId, this.model, this.kind, this.sites, this.elements, SymmetryOperator.createMapping(op, this.sites)); } + private _lookup3d?: Lookup3D = void 0; + get lookup3d() { + if (this._lookup3d) return this._lookup3d; + const { x, y, z } = this.sites; + // TODO: support sphere radius + this._lookup3d = GridLookup3D({ x, y, z, indices: this.elements }); + return this._lookup3d; + } + constructor(id: number, invariantId: number, model: Model, kind: Kind, sites: S, elements: SortedArray, conformation: SymmetryOperator.ArrayMapping) { this.kind = kind; this.id = id; @@ -133,27 +160,6 @@ namespace Unit { export interface Spheres extends CoarseBase<CoarseGrained.Spheres> { kind: Kind.Spheres } export interface Gaussians extends CoarseBase<CoarseGrained.Gaussians> { kind: Kind.Gaussians } - - export function getLookup3d(unit: Unit, group: ElementGroup) { - if (group.__lookup3d__) return group.__lookup3d__; - if (Unit.isAtomic(unit)) { - const { x, y, z } = unit.model.atomSiteConformation; - group.__lookup3d__ = GridLookup3D({ x, y, z, indices: group.elements }); - return group.__lookup3d__; - } - - throw 'not implemented'; - } - - export function getGroupBonds(unit: Unit, group: ElementGroup) { - if (group.__bonds__) return group.__bonds__; - if (Unit.isAtomic(unit)) { - group.__bonds__ = computeUnitBonds(unit, group); - return group.__bonds__; - } - - throw 'not implemented'; - } } export default Unit; \ No newline at end of file diff --git a/src/mol-model/structure/structure/unit/bonds.ts b/src/mol-model/structure/structure/unit/bonds.ts new file mode 100644 index 000000000..6316c3eeb --- /dev/null +++ b/src/mol-model/structure/structure/unit/bonds.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export * from './bonds/intra-data' +export * from './bonds/intra-compute' \ No newline at end of file diff --git a/src/mol-model/structure/structure/unit/bonds/compute.ts b/src/mol-model/structure/structure/unit/bonds/compute.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/mol-model/structure/structure/unit/bonds/data.ts b/src/mol-model/structure/structure/unit/bonds/data.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/mol-model/structure/structure/element/properties/bonds/group-compute.ts b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts similarity index 94% rename from src/mol-model/structure/structure/element/properties/bonds/group-compute.ts rename to src/mol-model/structure/structure/unit/bonds/intra-compute.ts index a75540509..2d8f9af02 100644 --- a/src/mol-model/structure/structure/element/properties/bonds/group-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts @@ -4,11 +4,10 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { BondType, ElementSymbol } from '../../../../model/types' -import { GroupBonds } from './group-data' -import { StructConn, ComponentBondInfo } from '../../../../model/formats/mmcif/bonds' -import Unit from '../../../unit'; -import ElementGroup from '../../group'; +import { BondType, ElementSymbol } from '../../../model/types' +import { IntraUnitBonds } from './intra-data' +import { StructConn, ComponentBondInfo } from '../../../model/formats/mmcif/bonds' +import Unit from '../../unit' export interface BondComputationParameters { maxHbondLength: number, @@ -107,15 +106,15 @@ function computePerAtomBonds(atomA: number[], atomB: number[], _order: number[], }; } -function _computeBonds(unit: Unit.Atomic, atoms: ElementGroup, params: BondComputationParameters): GroupBonds { +function _computeBonds(unit: Unit.Atomic, params: BondComputationParameters): IntraUnitBonds { const MAX_RADIUS = 3; const { x, y, z } = unit.model.atomSiteConformation; - const atomCount = ElementGroup.size(atoms); - const { residueIndex } = unit; + const atomCount = unit.elements.length; + const { elements: atoms, residueIndex } = unit; const { type_symbol, label_atom_id, label_alt_id } = unit.model.hierarchy.atoms; const { label_comp_id } = unit.model.hierarchy.residues; - const query3d = Unit.getLookup3d(unit, atoms); + 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 @@ -129,7 +128,7 @@ function _computeBonds(unit: Unit.Atomic, atoms: ElementGroup, params: BondCompu let componentMap: Map<string, Map<string, { flags: number, order: number }>> | undefined = void 0; for (let _aI = 0; _aI < atomCount; _aI++) { - const aI = ElementGroup.getAt(atoms, _aI); + const aI = atoms[_aI]; const raI = residueIndex[aI]; if (!params.forceCompute && raI !== lastResidue) { @@ -155,7 +154,7 @@ function _computeBonds(unit: Unit.Atomic, atoms: ElementGroup, params: BondCompu for (let ni = 0; ni < count; ni++) { const _bI = indices[ni]; - const bI = ElementGroup.getAt(atoms, _bI); + const bI = atoms[_bI]; if (bI <= aI) continue; const altB = label_alt_id.value(bI); @@ -243,11 +242,11 @@ function _computeBonds(unit: Unit.Atomic, atoms: ElementGroup, params: BondCompu }; } -function computeUnitBonds(unit: Unit.Atomic, atoms: ElementGroup, params?: Partial<BondComputationParameters>) { - return _computeBonds(unit, atoms, { +function computeIntraUnitBonds(unit: Unit.Atomic, params?: Partial<BondComputationParameters>) { + return _computeBonds(unit, { maxHbondLength: (params && params.maxHbondLength) || 1.15, forceCompute: !!(params && params.forceCompute), }); } -export { computeUnitBonds } \ No newline at end of file +export { computeIntraUnitBonds } \ No newline at end of file diff --git a/src/mol-model/structure/structure/element/properties/bonds/group-data.ts b/src/mol-model/structure/structure/unit/bonds/intra-data.ts similarity index 88% rename from src/mol-model/structure/structure/element/properties/bonds/group-data.ts rename to src/mol-model/structure/structure/unit/bonds/intra-data.ts index 0406f05d3..c897e6ca6 100644 --- a/src/mol-model/structure/structure/element/properties/bonds/group-data.ts +++ b/src/mol-model/structure/structure/unit/bonds/intra-data.ts @@ -5,9 +5,9 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { BondType } from '../../../../model/types' +import { BondType } from '../../../model/types' -interface GroupBonds { +interface IntraUnitBonds { /** * Where bonds for atom A start and end. * Start offset at idx, end at idx + 1 @@ -21,8 +21,8 @@ interface GroupBonds { count: number } -namespace GroupBonds { - export function createEmpty(): GroupBonds { +namespace IntraUnitBonds { + export function createEmpty(): IntraUnitBonds { return { offset: [], neighbor: [], order: [], flags: [], count: 0 } } export function isCovalent(flags: number) { @@ -46,4 +46,4 @@ namespace GroupBonds { } } -export { GroupBonds } \ No newline at end of file +export { IntraUnitBonds } \ No newline at end of file diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts index fc5827592..bd38cf542 100644 --- a/src/perf-tests/sets.ts +++ b/src/perf-tests/sets.ts @@ -1,6 +1,6 @@ import * as B from 'benchmark' import { Tuple, Segmentation, OrderedSet as OrdSet } from 'mol-data/int' -import { ElementSet } from 'mol-model/structure' +//import { ElementSet } from 'mol-model/structure' // export namespace Iteration { // const U = 1000, V = 2500; @@ -198,39 +198,39 @@ export namespace Union { } } -export namespace Build { - function createSorted() { - const b = ElementSet.LinearBuilder(ElementSet.Empty); - for (let i = 0; i < 10; i++) { - for (let j = 0; j < 1000; j++) { - b.add(i, j); - } - } - return b.getSet(); - } +// export namespace Build { +// function createSorted() { +// const b = ElementSet.LinearBuilder(ElementSet.Empty); +// for (let i = 0; i < 10; i++) { +// for (let j = 0; j < 1000; j++) { +// b.add(i, j); +// } +// } +// return b.getSet(); +// } - function createByUnit() { - const b = ElementSet.LinearBuilder(ElementSet.Empty); - for (let i = 0; i < 10; i++) { - b.beginUnit(); - for (let j = 0; j < 1000; j++) { - b.addToUnit(j); - } - b.commitUnit(i); - } - return b.getSet(); - } +// function createByUnit() { +// const b = ElementSet.LinearBuilder(ElementSet.Empty); +// for (let i = 0; i < 10; i++) { +// b.beginUnit(); +// for (let j = 0; j < 1000; j++) { +// b.addToUnit(j); +// } +// b.commitUnit(i); +// } +// return b.getSet(); +// } - export function run() { - const suite = new B.Suite(); - suite - .add('create sorted', () => createSorted()) - .add('create by unit', () => createByUnit()) - .on('cycle', (e: any) => console.log(String(e.target))) - .run(); - } -} +// export function run() { +// const suite = new B.Suite(); +// suite +// .add('create sorted', () => createSorted()) +// .add('create by unit', () => createByUnit()) +// .on('cycle', (e: any) => console.log(String(e.target))) +// .run(); +// } +// } export namespace Tuples { function createData(n: number) { -- GitLab